Controller Instance Variables


#1

Values of instance variables set within an action of a controller are
available in the template associated with that action. I was thinking
(hoping?) that the values of instance variables set within a
controller but outside of any action would be universally available to
all the templates associated with that controller. Apparently that is
not the case. Two questions: (1) Why is that? (2) Is there some way
(other than using a global variable) that I can set a variable in a
controller and have its value available to all templates associated
with that controller?

Thanks for any input.

    ... doug

#2

On 27 Apr 2009, at 17:23, doug wrote:

it might clear things up if you pastied what you’ve been trying.

Fred


#3

Doug J. wrote:

Values of instance variables set within an action of a controller are
available in the template associated with that action. I was thinking
(hoping?) that the values of instance variables set within a
controller but outside of any action would be universally available to
all the templates associated with that controller. Apparently that is
not the case. Two questions: (1) Why is that?

Perhaps a more basic question is: why is the template able to access the
private variables in the controller at all? In other words, if you
define a ruby class like this:

class A
def initialize
@var = 10
end
end

then all the other methods in class A can access @var, but an object of
class B cannot access @var. I think a template is over in class B
somewhere.

(2) Is there some way
(other than using a global variable) that I can set a variable in a
controller and have its value available to all templates associated
with that controller?

Sessions?


#4

Rails does some magic to make that happen. Here is a blog post that
explains the basics of how this happens:

http://www.neeraj.name/blog/articles/719-how-controller-and-view-shar

Thanks. The blog post explains the magic that Rails uses to make the
instance variables contained in an action visible in the view
associated with that action. I just need to figure out how I can set
a value in a controller and make that value available to all of the
views in that controller.

Thanks again.

      ... doug

#5

I am having difficulty imagining why you would want an instance variable
of
a controller that is not associated with an action, what else do
controllers
do? Are you sure it is not an instance variable of a model that you
should
be using?
Colin

2009/4/27 djolley removed_email_address@domain.invalid


#6

7stud – wrote:

Doug J. wrote:
Perhaps a more basic question is: why is the template able to access the
private variables in the controller at all? In other words, if you
define a ruby class like this:

Rails does some magic to make that happen. Here is a blog post that
explains the basics of how this happens:

http://www.neeraj.name/blog/articles/719-how-controller-and-view-share-instance-variables

then all the other methods in class A can access @var, but an object of
class B cannot access @var. I think a template is over in class B
somewhere.

Yes, you are correct about this in the general case, but as you can see
above Rails performs some additional work to simplify template
development by coping ivars from the controller into the views.

(2) Is there some way
(other than using a global variable) that I can set a variable in a
controller and have its value available to all templates associated
with that controller?

Sessions?

I would think avoiding global variable would be a very smart thing to
do. Session variables might be what you want. It really all depends on
what you’re trying to do. Generally speaking if you find yourself
fighting against Rails you’ve probably got a design problem. Of course,
there’s exceptions to every rule.


#7

I am having difficulty imagining why you would want an instance variable of
a controller that is not associated with an action, what else do controllers
do?

The thing is that this particular instance variable is associated with
ALL of the actions of the controller. I want to be DRY and only
specify the value of the instance variable once and have that value
available in all of the views associated with each of the actions of
that controller.

Are you sure it is not an instance variable of a model that you should
be using?

I don’t think so. Models extend from ActiveRecord::Base. Therefore
in my nieve view of the Rails world, models are only used with
databases. I don’t have a database involved. I just want to pass a
common value from the controller to the views and I don’t want to have
to redundantly specify that value in each of the actions.

Thanks for the input.

    ... doug

#8

Is this something that you can do with a before_filter?

I think you may be onto something. Conceptually, it sounds like your
suggestion should work. I’m just not sure that I know how to go about
implementing it. I’ll play around with the concept and post if I am
successful. In the meantime, if anyone has any specific input on
precisely how to do this, I’d sure love to hear it.

Thanks for the input.

     ... doug

#9

Is this something that you can do with a before_filter?


#10

In that case would an @variable in before_filter, rather than an
instance
variable do?
Colin

2009/4/28 djolley removed_email_address@domain.invalid


#11

On Apr 28, 9:30 am, djolley removed_email_address@domain.invalid wrote:

In that case would an @variable in before_filter, rather than an instance
variable do?

I’m sorry. I’m not following. I thought an @variable was an instance
variable.

it is.

As I see it, in order to implement your suggestion, I need to do
something like:

before_filter do |controller|
The_meat_goes_here
end

you don’t want to use that form of before filter (you’d be setting
instance variables on the wrong object). you want something like

before_filter :setup_stuff

protected
def setup_stuff

end

As for what goes inside setup_stuff, that’s up to you. If what you
want to do is set instance variables you can do it in there.

Fred


#12

In that case would an @variable in before_filter, rather than an instance
variable do?

I’m sorry. I’m not following. I thought an @variable was an instance
variable.

As I see it, in order to implement your suggestion, I need to do
something like:

before_filter do |controller|
The_meat_goes_here
end

I don’t know what to put where I have, “The_meat_goes_here”.

   ... doug

#13

Freddy A. wrote:

Is this something that you can do with a before_filter?

I looked up filters, and the description I read says a filter applies to
only one action. That would mean the op would have to write the same
before filter for every action. Or is there a way to refer to the same
filter?

Colin L. wrote:

I am having difficulty imagining why you would want an
instance variable of a controller that is not associated with
an action…

That does not summarize what the op wants to do–quite the opposite.
The op wants one instance variable to be associated with every action.
For instance, in a ruby class you can do this:

class A
def initialize(val)
@var1 = val
end


end

Then @var1 will be accessible inside any method in class A. Also, if
you write:

class A
def meth(val2)
@var2 = val2
end

and then you call meth(), then @var2 will be created and it too will be
accessible inside any method in class A. If rails worked the same way
as ruby, then any instance variable defined in one action would be
accessible inside any another action–which would also mean that all the
views associated with all the actions could also access the instance
variable.

Instance variables are really just global variables within a scope: the
class scope. Like global variables, instance variables can be accessed
anywhere inside their scope. Rails seems to be saying, “No global
variables”. But in this case, that rule is causing the op to “get wet”
by having to write something like:

@var1 = 10

in every action. If the op wants to change that constant at some later
date, then it means changing it in every action (which really isn’t that
bad–search and replace in one file), but that isn’t DRY.

Are you sure it is not an instance variable of a model that you should
be using?

I don’t think so. Models extend from ActiveRecord::Base. Therefore
in my nieve view of the Rails world, models are only used with
databases.

Not true, not true. I’m reading AWDWR(3rd), which is very highly
regarded in the rails world, and in their first non trivial example they
create a model that is not hooked up to a database. They just navigate
to the models directory, open up a blank file, and define a class which
does not inherit from anything. Voila, a new model.

In my naive view, a model is any class that has methods that do
something. The way I look at it is, the controller is very
lazy–instead of calculating or doing stuff itself, it would rather call
methods in other classes so that they have to do the work. A model that
is hooked up to a database provides easy database access for the
controller. But other models have methods that do other things, and
when the controller needs to get those other things done, it calls the
methods in those models.

they I don’t have a database involved. I just want to pass a
common value from the controller to the views and I don’t want to have
to redundantly specify that value in each of the actions.

Creating a model that is not hooked up to a database sounds like
something that might work. It could be as simple as:

class Constants
attr_reader :val

def initialize
@const = 10
end
end

Then in an action, you could write:

c = Constants.new
val = c.const

Of course, then you have to write that in every action–but if you ever
want to change the value of the constant, you only have to change it in
one place: in the Constants class.


#14

7stud – wrote:

Robert W. wrote:

Rails does some magic to make that happen. Here is a blog post
that explains the basics of how this happens:

http://www.neeraj.name/blog/articles/719-how-controller-and-view-share-instance-variables

In the linked article, there is an extra step: a’s var names and values
are stored in a hash. That is easy to incorporate:

a_names_vals = {} #<—added ****

names.each do |name|
print "#{name} = "
name_symbol = name.to_s
val = a.instance_variable_get(name_symbol)
print val
puts

a_names_vals[name_symbol] = val #<—added ***
end
puts

p a_names_vals #<—added ***

–output:–
{"@var1"=>10, “@var2”=>20, “@greeting”=>“hello”}

class B
end

b = B.new

#>Then this simplifies down to:

a_names_vals.each do |var_name, var_val|
b.instance_variable_set(var_name, var_val)
B.class_eval(“attr_reader :#{var_name[1…-1]}”)
end

puts b.var1, b.var2, b.greeting

–output:–
10
20
hello

Darn, I forgot to incorporate some changes I made before posting that
second part (namely astring.to_s doesn’t do anything):

class A
def initialize
@var1 = 10
@var2 = 20
end

def meth
@greeting = “hello”
end
end

a = A.new
a.meth

a_names_vals = {}
names = a.instance_variables

names.each do |name|
val = a.instance_variable_get(name)
a_names_vals[name] = val
end

p a_names_vals

–output:–
{"@var1"=>10, “@var2”=>20, “@greeting”=>“hello”}

class B
end
b = B.new

a_names_vals.each do |var_name, var_val|
b.instance_variable_set(var_name, var_val)
B.class_eval(“attr_reader :#{var_name[1…-1]}”)
end

puts b.var1, b.var2, b.greeting

–output:–
10
20
hello


#15

On Apr 28, 9:35 am, 7stud – removed_email_address@domain.invalid wrote:

Freddy A. wrote:

Is this something that you can do with a before_filter?

I looked up filters, and the description I read says a filter applies to
only one action. That would mean the op would have to write the same
before filter for every action. Or is there a way to refer to the same
filter?

That’s not true. By default a filter applies to all actions. You can
change that with the :only and :except options.

accessible inside any method in class A. If rails worked the same way
as ruby, then any instance variable defined in one action would be
accessible inside any another action–which would also mean that all the
views associated with all the actions could also access the instance
variable.

rails is ruby. different web requests are handled by new instances of
controllers though, so if you set a controller instance variable it
won’t be there on the next request.

At a basic level, before_filter :meth is pretty much the same as just
calling meth at the top of every action, without the repetitiveness.

Of course, then you have to write that in every action–but if you ever
want to change the value of the constant, you only have to change it in
one place: in the Constants class.

You could also create an actual constant, eg

class Person
MINIMUM_AGE = 18
end

and then use Person::MINIMUM_AGE

Fred


#16

7stud – wrote:

Doug J. wrote:

Values of instance variables set within an action of a controller are
available in the template associated with that action. I was thinking
(hoping?) that the values of instance variables set within a
controller but outside of any action would be universally available to
all the templates associated with that controller. Apparently that is
not the case. Two questions: (1) Why is that?

Perhaps a more basic question is: why is the template able to access the
private variables in the controller at all?

Well, I fooled around and answered my own question. This is a toy
example that I came up with (the output, variable names, and the
comments make it self explanatory):

class A
def initialize
@var1 = 10
@var2 = 20
end

def meth
@greeting = “hello”
end
end

a = A.new
p A.new.instance_variables

–output:–
["@var1", “@var2”]

a.meth #>creates another instance variable(see definition of meth)
names = a.instance_variables
p names

–output:–
["@var1", “@greeting”, “@var2”]

names.each do |name|
print "#{name} = "
val = a.instance_variable_get(name)
print val
puts
end
puts

–output:–
@var1 = 10
@greeting = hello
@var2 = 20

class B
end

b = B.new
#>Now attempt to insert the instance variables from
obj a into object b:

names.each do |var_name|
#>names contains a’s instance var names from above

var_val = a.instance_variable_get(var_name)
b.instance_variable_set(var_name, var_val)
#>Now if you inspect b:
#> p b
#>the output will be:
#> #<B:0x2420c @var1=10>
#>If you look closely, you can see @var1 in there
#>along with its value. However, @var1 ends up being a
#>private variable and it is inaccessible:
#> puts b.var1
#>results in an error message. So…

B.class_eval(“attr_reader :#{var_name[1…-1]}”)
#>I want to insert the statement:
#> attr_reader :var1
#>into class B. The variable var_name is assigned a
#>string like “@var1”, so var_name[1…-1] is equal to
#>“var1”. Remember arrays and strings are indexed
#>starting at 0, also an index of -1 is the last character
#>in the string, so some_string[1…-1] returns the substring
#>starting at position 1 and ending at the last character–
#>in other words some_string[1…-1] is a copy of some_string
#>with the first character chopped off. So if var_name is
#>equal to “@var1” the string:
#> “attr_reader :#{var_name[1…-1]}”
#>is converted to:
#> “attr_reader :var1”
#>which is the string I am after. After that string is
#>eval’ed in class B, I should have access to the
#>private variable @var1.
end

puts b.var1, b.var2, b.greeting
#>Will it work?

–output:–
10
20
hello

Great success!

Robert W. wrote:

Rails does some magic to make that happen. Here is a blog post
that explains the basics of how this happens:

http://www.neeraj.name/blog/articles/719-how-controller-and-view-share-instance-variables

In the linked article, there is an extra step: a’s var names and values
are stored in a hash. That is easy to incorporate:

a_names_vals = {} #<—added ****

names.each do |name|
print "#{name} = "
name_symbol = name.to_s
val = a.instance_variable_get(name_symbol)
print val
puts

a_names_vals[name_symbol] = val #<—added ***
end
puts

p a_names_vals #<—added ***

–output:–
{"@var1"=>10, “@var2”=>20, “@greeting”=>“hello”}

class B
end

b = B.new

#>Then this simplifies down to:

a_names_vals.each do |var_name, var_val|
b.instance_variable_set(var_name, var_val)
B.class_eval(“attr_reader :#{var_name[1…-1]}”)
end

puts b.var1, b.var2, b.greeting

–output:–
10
20
hello


#17

Frederick C. wrote:

On Apr 28, 9:35�am, 7stud – removed_email_address@domain.invalid wrote:

Freddy A. wrote:

Is this something that you can do with a before_filter?

I looked up filters, and the description I read says a filter applies to
only one action. �That would mean the op would have to write the same
before filter for every action. �Or is there a way to refer to the same
filter?

That’s not true. By default a filter applies to all actions. You can
change that with the :only and :except options.

Ah, yes. I should have read more carefully

accessible inside any method in class A. �If rails worked the same way
as ruby, then any instance variable defined in one action would be
accessible inside any another action–which would also mean that all the
views associated with all the actions could also access the instance
variable.

rails is ruby. different web requests are handled by new instances of
controllers though, so if you set a controller instance variable it
won’t be there on the next request.

Ok, that makes sense.

At a basic level, before_filter :meth is pretty much the same as just
calling meth at the top of every action, without the repetitiveness.

Yes. I tested out a before filter, and it seems like a simple way to
provide a controller wide “global variable” that the views can access.
In my case, I used the before filter to set the value of an instance
variable for the new and edit CRUD actions, and then both views used the
instance variable as the default value for a text field. Simple
solution. So what’s all the fuss about??

Of course, then you have to write that in every action–but if you ever
want to change the value of the constant, you only have to change it in
one place: in the Constants class.

You could also create an actual constant, eg

class Person
MINIMUM_AGE = 18
end

and then use Person::MINIMUM_AGE

Yes, I know.

Thanks.


#18

Frederick C. wrote:

If rails worked the same way
as ruby, then any instance variable defined in one action would be
accessible inside any another action–which would also mean that all the
views associated with all the actions could also access the instance
variable.

rails is ruby. different web requests are handled by new instances of
controllers though, so if you set a controller instance variable it
won’t be there on the next request.

I had been thinking along those lines, and I meant to test the following
but I got sidetracked:

class ProductsController < ApplicationController

GET /links

GET /links.xml

def initialize
@val = 10
super
end

and that seems to work as well. All the views for the actions in the
ProductsController gain access to the @val instance variable. Is
overriding the super class’s initialize method bad form in rails?


#19

So to sum it up is is as simple as this:

class MyController < ApplicationController

before_filter :setup_stuff

protected
def setup_stuff
@var1 = …
@var2 = …
end

end

then @var1 and @var2 will be available in views for all actions of
MyController

Colin

2009/4/28 Frederick C. removed_email_address@domain.invalid


#20

On Apr 28, 11:38 am, 7stud – removed_email_address@domain.invalid
wrote:

ProductsController gain access to the @val instance variable. Is
overriding the super class’s initialize method bad form in rails?

you could do that. i would usually prefer before_filters (at least in
part because you only have one initialize method so you’d have to
chuck everything in there, whereas you can have separate,
appropriately named before filters that setup related collections of
variables.

Fred