Forum: Ruby on Rails Controller Instance Variables

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-04-27 18:24
(Received via mailing list)
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
81b61875e41eaa58887543635d556fca?d=identicon&s=25 Frederick Cheung (Guest)
on 2009-04-27 19:27
(Received via mailing list)
On 27 Apr 2009, at 17:23, doug wrote:

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

Fred
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-04-27 20:25
Doug Jolley 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?
280b78a61a968391b7e07e912be102a8?d=identicon&s=25 Robert Walker (robert4723)
on 2009-04-27 20:55
7stud -- wrote:
> Doug Jolley 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-contr...

> 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.
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-04-27 21:53
(Received via mailing list)
> 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-contr......

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
5f94b9b346c2aa648a80bc259978e5bc?d=identicon&s=25 Colin Law (Guest)
on 2009-04-27 22:11
(Received via mailing list)
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 <ddjolley@gmail.com>
4c438a80fc30661ad619ea177cf9cbd0?d=identicon&s=25 Freddy Andersen (Guest)
on 2009-04-27 23:27
(Received via mailing list)
Is this something that you can do with a before_filter?
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-04-28 01:56
(Received via mailing list)
> 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
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-04-28 02:02
(Received via mailing list)
> 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
5f94b9b346c2aa648a80bc259978e5bc?d=identicon&s=25 Colin Law (Guest)
on 2009-04-28 10:07
(Received via mailing list)
In that case would an @variable in before_filter, rather than an
instance
variable do?
Colin

2009/4/28 djolley <ddjolley@gmail.com>
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-04-28 10:31
(Received via mailing list)
> 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
81b61875e41eaa58887543635d556fca?d=identicon&s=25 Frederick Cheung (Guest)
on 2009-04-28 10:34
(Received via mailing list)
On Apr 28, 9:30 am, djolley <ddjol...@gmail.com> 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
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-04-28 10:35
Freddy Andersen 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 Law 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.
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-04-28 10:40
7stud -- wrote:
> Doug Jolley 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 Walker 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-contr...

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
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-04-28 10:57
7stud -- wrote:
> Robert Walker 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-contr...
>
> 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
81b61875e41eaa58887543635d556fca?d=identicon&s=25 Frederick Cheung (Guest)
on 2009-04-28 11:09
(Received via mailing list)
On Apr 28, 9:35 am, 7stud -- <rails-mailing-l...@andreas-s.net> wrote:
> Freddy Andersen 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
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-04-28 12:24
Frederick Cheung wrote:
> On Apr 28, 9:35�am, 7stud -- <rails-mailing-l...@andreas-s.net> wrote:
>> Freddy Andersen 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.
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-04-28 12:38
Frederick Cheung 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?
81b61875e41eaa58887543635d556fca?d=identicon&s=25 Frederick Cheung (Guest)
on 2009-04-28 13:34
(Received via mailing list)
On Apr 28, 11:38 am, 7stud -- <rails-mailing-l...@andreas-s.net>
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
5f94b9b346c2aa648a80bc259978e5bc?d=identicon&s=25 Colin Law (Guest)
on 2009-04-28 15:09
(Received via mailing list)
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 Cheung <frederick.cheung@gmail.com>
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-04-28 17:59
(Received via mailing list)
So, I see that we have a working solution to this problem.  THAT'S
GREAT NEWS!  Thanks a batch for all of the contributions.

I would, however, like to see if the solution couldn't be advanced one
step further.  The methodology that we have working so far is to
define a method as a filter somewhere and then pass the name of that
method as a symbol to before_filter.  Obviously, this requires a
separate definition of the method.  My question is this: Can we
somehow do this whole thing in a single step by passing the
before_filter a block rather than a symbol?  before_filter is supposed
to accept a block.  So, conceptually, my understanding is that we
would need some code that looks something like this:

before_filter do |controller|
  some_code_here
end

Can anyone please tell me what code I would need to supply as a
replacement for some_code_here in order to get this thing to work
using a block.

Thanks.

          ... doug
81b61875e41eaa58887543635d556fca?d=identicon&s=25 Frederick Cheung (Guest)
on 2009-04-28 18:16
(Received via mailing list)
On Apr 28, 4:58 pm, djolley <ddjol...@gmail.com> wrote:
> using a block.
A key difference with using the block form is that (because blocks are
closures) self will be the class, not the controller instance (which
is why you get passed the controller as a parameter). For example you
can't do

before_filter do |controller|
  @val = true
end

because that will set the instance variable in the wrong object. you
could use controller.instance_variable_set but that's just disgusting.
so you're left with

before_filter do |controller|
  controller.some_method
end
...
def some_method

end

at which point you might as well do before_filter :some_method. Plus I
like giving things names when appropriate.


Fred
80159acdf89580a20e8e835772b5bf82?d=identicon&s=25 Doug Jolley (ddjolley)
on 2009-04-28 19:00
(Received via mailing list)
> because that will set the instance variable in the wrong object. you
> could use controller.instance_variable_set but that's just disgusting.

Well now, hold on there a minute, Fred. :)  I kind of like it.  In
fact, it's exactly what I asked for.  I don't find it all that
disgusting.  Anyway, I now know how to do it both ways and I can
decide which way I like the better.  Thanks to all for the input in
this topic.

          ... doug
C6c27ab13fc4e932f1b0944cb6dbb442?d=identicon&s=25 Morgan Christiansson (Guest)
on 2009-04-29 15:58
(Received via mailing list)
Hi djolley. You can also use class variables or global variables:

test.rb:

$b='global value'
class MyTest
  @@a='class value'
  def class_val
    @@a
  end
  def global_val
    $b
  end
end

test = MyTest.new
puts test.class_val
puts test.global_val


$ ruby test.rb
class value
global value

These variables will not be reset when a new instance is created.
Haven't tested @@class_val in a view but it should work.

Regards,
Morgan
280b78a61a968391b7e07e912be102a8?d=identicon&s=25 Robert Walker (robert4723)
on 2009-04-30 07:36
Morgan Christiansson wrote:
> Hi djolley. You can also use class variables or global variables:
>
> test.rb:
>
> $b='global value'
> class MyTest
>   @@a='class value'
>   def class_val
>     @@a
>   end
>   def global_val
>     $b
>   end
> end
>

BAD RUBY!! No global vars! Don't even go there. That's all I have to say
about that. :-)
280b78a61a968391b7e07e912be102a8?d=identicon&s=25 Robert Walker (robert4723)
on 2009-04-30 07:40
Doug Jolley wrote:
>> because that will set the instance variable in the wrong object. you
>> could use controller.instance_variable_set but that's just disgusting.
>
> Well now, hold on there a minute, Fred. :)  I kind of like it.  In
> fact, it's exactly what I asked for.  I don't find it all that
> disgusting.  Anyway, I now know how to do it both ways and I can
> decide which way I like the better.  Thanks to all for the input in
> this topic.
>
>           ... doug

If you want to be a good Rails citizen, I'd recommend following Fred's
advice. He have you a clean and conventional solution and I guarantee
you he knows what he's talking about. I'd have given you the same advice
if the original posting was stated slightly more clearly.
This topic is locked and can not be replied to.