Chronicling My Ruby on Rails Journey

March 7, 2007

ActionController and what the heck is attr_internal?

Filed under: ActionController — Bob Ngu @ 7:36 pm

My RoR efforts have culminated in JiggyMe, check it out!

I am a bit puzzled about something I encountered with Action Controller today. While in a view, I wanted to find out the action that invoked the controller/view, at first I used “params[:action]” and that worked. However, there is another way to find out the action via action_name attribute, however I couldn’t refer to it standalone like params. I have to qualify it as such “controller.action_name”. Like params, other variables like request, cookies, session, etc can also be accessed standalone.

So I dug into the ActionController::Base code and saw that params, request, cookies, session, etc are defined as such:

attr_internal :request
attr_internal :params
attr_internal :response
attr_internal :session

whereas action_name is defined using the standard read/writer accessors

attr_accessor :action_name

I have no idea what attr_internal is but it seems to allows these variables(?) to be accessed standalone, can anyone shed some light on this?

I also found this interesting line of code in ActionController::Base

# Determines whether the view has access to controller internals @request, @response, @session, and @template.
# By default, it does.
@@view_controller_internals = true
cattr_accessor :view_controller_internals

It seems to control the magic of variables defined using attr_internal making them available standalone. As an experiment, I set @@view_controller_internals = nil, restart webrick, and boom, it did something nasty as expected. So there is definitely some black magic going on with attr_internal.

As an additional note, it appears that a controller object is instantiated at the beginning of a request before it gets to the controller code. When I checked the controller object_id within the controller code using “self.object_id” and compared it to “controller.object_id” in the view, the values match exactly.

Advertisements

2 Comments »

  1. Hi, Bob,

    I am interested in Rails internals too (and in particular at how the Controller and View use metaprogramming to share information) and I spent some time studying the code in this area; although not a Rails expert, I can offer this explanation of what you saw:

    1) first a caveat: you mention that you were testing “in a View”, and then you looked at the Controller code. Controller and View are different classes, so don’t assume that we can just browse the controller code to explain what happens in the view (actually: as the Controller transmits some variables to the view, this will work some of the times; what will make things even more confusing!).

    2) Why writing ‘controller.action_name’ in the view worked?
    When the controller instantiates the view, it passes itself (’self’). The view stores this parameter as instance variable @controller (see method initialize); it also declares ‘attr_accessor :controller’, and this explains that you were able to write ‘controller.action_name’ (note: we are really calling method ‘action_name’ in the controller).
    However, ‘action_name’ by itself does not exist in the View, and that is the reason that it failed when you tried (actually… it exists as ‘@action_name’, but forget this: the standard method is to retrieve it via params).

    3) Let’s come to the heart of your question: what is attr_internal? it is not very different from attr_accessor, but with this small difference: when you write:

    attr_accessor :params # methods to read/write instance var @params
    attr_internal :params # methods to read/write instance var @_params

    Why did the Rails team go to the trouble of doing this? they want users to call a method (params in the example) and not access any more the instance variables [actually, for the moment they still make available @params & co, but flooding the logs with deprecation warnings]. This will make possible to change the implementations of params (and session, request, etc) without impact in existing software; of course, the variable @_params is still technically accessible to the user, but the ‘_’ should signals users that it is not to be used.
    Thus, the meaning of ‘internal’ means: use the method, not the variable!

    4) Why does setting @@view_controller_internals=nil causes Rails to malfunction? The reason (a bit silly) is the following:
    a) the controller transmits (just before invoking render) a snapshot of some of its variables to the view in a hash (look at controller method ‘add_instance_variables_to_assigns’), unless they are ‘protected’.
    b) now,if view_controller_internals is false, ‘protected’ includes almost all the variables of interest (including the ‘internal’ ones, but not only); so the View does not get any information!
    I don’t know the reason why this is present in the code (perhaps a feature being developed? if the controller wants to hide its variables, it surely needs another mechanism to transmit information to the view).

    5) Finally: you compared ‘self.object_id’ in the controller with ‘controller.object_id’ in the View and wondered if ‘a controller object is instantiated before it gets to the controller code’.
    No, things are simple here; from point 2) in my answers, you can see that the values were the same because… in fact they are the same objects! controller.object_id in the view is a call to method ‘object_id’ in the controller (the controller passed its identity to the view when instantiating it).

    There is a lot more to say on how information is manipulated between Controller and View, but I hope that this helped to shed some light on the points you raised,
    Raul

    Comment by Raul Parolari — July 5, 2007 @ 6:15 pm | Reply

  2. @Raul
    Thanks for the explanation. it’s still helpful even more than 1 year later!

    Comment by Linan Wang — September 14, 2008 @ 4:34 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: