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