Application Controller???

9 views
Skip to first unread message

Will Merrell

unread,
Jan 16, 2011, 3:00:56 PM1/16/11
to Cream List
First the good news: the full_config seems to work properly now.

Now the next problem. The application controller does not seem to work
in a cream app. When I put a test function into the application
controller of my cream app, it is not recognized elsewhere in my app.

To test this, I made two apps, one is a regular rails app, and the other
is a rails app with full_config run first thing. In both I ran:
rails g scaffold item name:string data:text
ran the migration and added:
match '/' => 'items#index', :as => :root
into the routes.rb so that the apps would have something to do.

In both, I put the following code in place:

class ApplicationController < ActionController::Base
protect_from_forgery
def say_hello
return "Hello from ApplicationController"
end
end

and

class ItemsController < ApplicationController
# GET /items
# GET /items.xml
def index
@textstr = say_hello
@items = Item.all

respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @items }
end
end
...


And then display @textstr in the index view. That is both apps are the
same except for one has been creamified. The regular app works as
expected, but the cream app produces this output:

===================================
will@silver:~/rails/cream_app$ rails s
=> Booting WEBrick
=> Rails 3.0.3 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2011-01-16 10:01:46] INFO WEBrick 1.3.1
[2011-01-16 10:01:46] INFO ruby 1.9.2 (2010-12-25) [i686-linux]
[2011-01-16 10:01:46] INFO WEBrick::HTTPServer#start: pid=11110 port=3000


Started GET "/" for 127.0.0.1 at 2011-01-16 10:02:01 -0500
Processing by ItemsController#index as HTML
Completed in 118ms

NameError (undefined local variable or method `say_hello' for
#<ItemsController:0xb17f3f8>):
app/controllers/items_controller.rb:6:in `index'

Rendered
/home/will/.rvm/gems/ruby-1.9.2-p136/gems/actionpack-3.0.3/lib/action_dispatch/middleware/templates/rescues/_trace.erb
(1.7ms)
Rendered
/home/will/.rvm/gems/ruby-1.9.2-p136/gems/actionpack-3.0.3/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
(261.3ms)
Rendered
/home/will/.rvm/gems/ruby-1.9.2-p136/gems/actionpack-3.0.3/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
within rescues/layout (270.9ms)
==============================================

Now for the questions, is there anything in cream that would account for
this? Have you seen this behavior? Has anyone else seen this? And most
importantly, does anyone know what to do about it?

One reason that this matters, and the reason I discovered it, is that
your suggested way of handling "current_user" and creating a default
Guest user, depends on putting some code in the application controller.
This code was not working. This is what I am trying to fix.

--Will

Kristian Mandrup

unread,
Jan 16, 2011, 5:09:58 PM1/16/11
to rails...@googlegroups.com
Hi Will,

I understand what you are saying and I have also discovered that this part of the application is not as it should be, as noted in both the README and the wiki I think. I have tried to fix this in the
*user_types* branch. Please check branch that out!

https://github.com/kristianmandrup/cream/tree/user_types

In there is my latest version of #current_user

module Cream
module UserControl
def current_user
return session[:the_user] if session[:the_user]
if defined? Cream::UserTypes
# Try to get instance of any of the available roles as the current user
Cream::UserTypes.available.each do |user_type|
method = :"current_#{role}"
if respond_to?(method)
session[:the_user] ||= send(method)
return session[:the_user] if session[:the_user]
end
end
end
session[:the_guest] ||= Guest.create if defined?(Guest)
end

Here the user is stored in the session (if not there already) to avoid a new user being created on every request! But still this method seems overly complex due to even my own lack of understanding of how to use Devise properly in this respect.

I know extend the Rails controller the same way I do it for the other controller "add-ons" which seem to work ok

Rails3::Plugin::Extender.new do
# extend action_controller with methods from some modules

extend_rails :controller do
. ..
extend_from_module Cream::UserControl
end


This branch also introduces a --user-types option to the generator equation (fx full_config generator) to control which devise users (user types) are created. Each devise user has a current_xxxxx method (devise does this) to get the current user of that type. Currently I don't understand this part of Devise, maybe you can help me? Why/How would there be multiple different users in action for the same user session!?

Like I note in the latest README, I feel the project is getting a bit "out of hand" for even my own understanding. I would like to do "damage control" right now and repair whatever bugs/issues remaining and then work on a rewrite/refactoring of it all using my latest knowledge from reading Jose Valims new book on understanding and using the Rails 3 internals (reached Chapter 3 now).

I hope you can help me fix the last remaining issues :)
Thanks!

Kristian

Will Merrell

unread,
Jan 16, 2011, 8:10:36 PM1/16/11
to rails...@googlegroups.com
Kristian Mandrup wrote:
> Hi Will,
>
> I understand what you are saying and I have also discovered that this part of the application is not as it should be, as noted in both the README and the wiki I think. I have tried to fix this in the
> *user_types* branch. Please check branch that out!
>

Regardless of how we set up roles, I think it is just wrong that the
local app's application_controller does not work. This must be fixed.
Functions in the local app's application_controller must work as expected.

> Here the user is stored in the session (if not there already) to avoid a new user being created on every request! But still this method seems overly complex due to even my own lack of understanding of how to use Devise properly in this respect.
>

Well, this does not surprise me, and I kind of suspected it. When I
first started using Devise I was confused by all of this also. Here's
what I figured out.

The roles that Devise uses are different from what I expected.

Basically, there are two strategies for roles, one is just a property of
a user, the other uses different kinds of users. Devise uses the second
method so that when a user logs in the "user" belongs to a super class
of User such as Admin.

I decided that I did not like that method of roles, and settled on a
single class of User, with roles being a property of that class. That
way I just test if the current user has a property and then alter my
code based on the presence of that property.

These two strategies are very distinct, and must be thought of as
separate. If you try to mush them together, I think you will become
hopelessly confused. They are just not the same thing.

The multi class approach that Devise uses is good for cases where there
are a small number of roles where the roles imply a large amount of
distinct code, such as when there are normal users and admin users and
the admin section is distinct and involves a lot of code.

When the code is mostly shared, and/or the number of roles is larger, I
think the single class with a roles property is easier to manage.

In my projects I tend towards the second, single class/ many roles
style. One of my projects supports an organization that puts on courses,
so I have an editor that can manage the sites content, a scheduler that
manages what courses we have on offer, a registrar who can mange the
people that have signed up, an admin who can mange other users, a
shopkeeper who can manage our store front, and maybe a few others yet to
come. Any given user may have none, one or many of these roles.

I would like to see cream clearly separate these two styles and allow
for both types of role approach.

--Will

Will Merrell

unread,
Jan 16, 2011, 8:37:32 PM1/16/11
to rails...@googlegroups.com
Kristian Mandrup wrote:
> This branch also introduces a --user-types option to the generator equation (fx full_config generator) to control which devise users (user types) are created. Each devise user has a current_xxxxx method (devise does this) to get the current user of that type. Currently I don't understand this part of Devise, maybe you can help me? Why/How would there be multiple different users in action for the same user session!?
>

A couple more things:

First, you should not store a user object on the session. The session
has limited storage capacity in some situations, and I understand it is
not recommended to store a lot of stuff on the session. I think it is
better to store the id of the user on the session, and instantiate it
for each page.

Second, I agree that Devise's method of multiple users of different
classes is weired and doesn't make sense. I think there should always be
one user, and only one user.

-- Will

Kristian Mandrup

unread,
Jan 17, 2011, 4:06:16 AM1/17/11
to rails...@googlegroups.com
Dear Will,

Thanks for your very thorough walkthrough on your thought about Users and Roles. I must say, that originally I had the same inclination, of just one generic User class with one or multiple roles.
But then in the app I am currently building we have multiple distinct user types with different data for each and different requirements in general. Then it makes sense to have them inherit and extend a very basic User class (email, password etc.). It also allows them to override and specify their own devise strategies (fx requires confirmation or not). Fx an Admin user would always be generated "from within", so it makes no sense to require email confirmation.

I agree, that you should only store an Id in the session. Good point! Also, I think the session should be set on #sign_in instead of my current approach where I try to find it by "trial and error", a very crude approach.

Something like this perhaps?

def sign_in
...
session[:user_id] = resource.id
end

def sign_out
...
session[:user_id] = nil
@current_user = nil
end

def current_user
@guest ||= User.create_guest guest_options if !session[:user_id]
@current_user ||= User.find session[:user_id]
end

def guest_options
session[:guest_options] ||= {}
end

As I see it, the Guest user should never be saved to the datastore (naturally) but in other respects function pretty much like any other user. Even some data on it should be settable for the guest session, such as selecting language or country etc.

def select_language lang
current_user.language = lang
guest_options[:lang] = lang
end

User#create_guest options = {}
language = guest_options[:lang] || 'en' (or application.default_language or I18n locale or ... ?)
Guest.new :language => language
end

The amazing is, that this approach of splitting up into roles and user types, in principal allows different user types to have different role strategies with respect to what roles they allow and even if they employ single or multiple roles.
I describe this in my latest README. Not sure how useful this is in practice, except for kind of fine-grained roles and subroles. So the Admin user tpe is a kind of super role, within which there can be different specific types of admins (fx also using permission) to administer different areas of the model. Wow! Crazy stuff... ?

I sure hope we can move on from here. I will try to implement something like the above approach in my own app.

Cheers!

Kris

Kristian Mandrup

unread,
Jan 18, 2011, 6:12:38 AM1/18/11
to rails...@googlegroups.com
Hi Will,

The latest gem release 0.8.9.2 should fix the last remaining problems. Let me know what you think ;)

Cheers!

Kristian

Will Merrell

unread,
Jan 18, 2011, 7:46:04 AM1/18/11
to rails...@googlegroups.com
Kristian Mandrup wrote:
> Hi Will,
>
> The latest gem release 0.8.9.2 should fix the last remaining problems. Let me know what you think ;)
>
> Cheers!
>
> Kristian

Good, I'll check it out when I have a chance. Weekdays are busy for me.

Does this fix the application_controller issue?

Is the a write up of how you handle the guest user?

--Will


Kristian Mandrup

unread,
Jan 18, 2011, 8:04:17 AM1/18/11
to rails...@googlegroups.com
Yeah, this should fix the Application Controller problem

I basically do this!

if defined? Cream::UserControl
ApplicationController.class_eval "include Cream::UserControl"
end

You can customize Cream::UserControl or you can override the functionality by rolling your own into ApplicationController or make your own module and include it in similar fashion in an initializer I guess.

Check lib/cream/controller/user_control

I will see if I can do a writeup of the current Guest user strategy ASAP

Kris

Will Merrell

unread,
Jan 18, 2011, 8:38:12 AM1/18/11
to rails...@googlegroups.com
Kristian Mandrup wrote:
> Hi Will,
>
> The latest gem release 0.8.9.2 should fix the last remaining problems. Let me know what you think ;)
>
> Cheers!
>
> Kristian

I got a chance to try it and I see that the application_controller issue
seems to be fixed.

I got a chance to try a simple little fix for guest as follows:


def current_user
current_user = super || Guest.new
end


With this I can try to start getting a handle on how you do roles.

Later,
-- Will

Will Merrell

unread,
Jan 18, 2011, 9:03:01 AM1/18/11
to rails...@googlegroups.com
Kristian Mandrup wrote:
> Hi Will,
>
> The latest gem release 0.8.9.2 should fix the last remaining problems. Let me know what you think ;)
>
> Cheers!
>
> Kristian

Now that I have the basics working and can test, I find that any attempt
to access or manipulate roles fails. The exact failure depends on the
strategy I use. I think I need for you to explain how this is supposed
to work.

-- Will

Reply all
Reply to author
Forward
0 new messages