Reputation: 26038
I have a module in Ruby containing one method and one variable:
module ApplicationHelper
@var1 = MyModel::CONST1.each_with_index.map { |item, i| [item, i] }
def method1 v1, v2, v3 = @var1[0]
#....
# access to @var1
end
end
The reason I want @var1
to be a variable is that if it was a method it would be evaluated each I access to it within method1
. It's not reasonable and thus, I want it to be evaluated only once.
However, when I call this method1
(it's a helper method) via ajax (ajax-based dialog which uses it), @var1 turns out to be nil. Likewise, during a first page loading it's filled out as I expect.
What do I do about it?
Upvotes: 0
Views: 104
Reputation: 6971
Your context is not the same (see):
module ApplicationHelper
p self # => ApplicationHelper
def method1
p self # => #<OtherClass:0x007fbe032cf6f0> an instance of an object
end
end
Ruby doesn't work like Java in the sense that you aren't able to set object instance variables in the context of a module (or class) as you did. In your code, @var1 = MyModel::CONST1.each_with_index.map { |item, i| [item, i] }
is defining an instance variable var1
on the actual module object ApplicationHelper
, not on any instance.
In Ruby, when you only want to define a variable once, we use the ||=
memoization trick:
module ApplicationHelper
def method1
@var1 ||= MyModel::CONST1.each_with_index.map { |item, i| [item, i] }
end
end
This is a trick that leverages the fact that the first time you reference a variable it will be nil
and nil
is considered fasely in Ruby. So the above line is the shortcut for writing:
@var1 || @var1 = MyModel::CONST1.each_with_index.map { |item, i| [item, i] }
NOTE: Be aware that if @var1
could be the values nil
or false
this will re-evaluate it each time. However, in this case. Since you are using Enumerable#map you will get []
, and not nil
, if there are no items. So the usage will be as expected in this situation.
This has an issue with the edge case the first time you call method1
because you are using it to set a default value. If we instead use message passing instead of direct variable access, we can achieve what you are looking for:
module ApplicationHelper
def method1(v1, v2, v3 = var1.first)
#....
# all further access to @var1 is done by passing the message var1
tmp = var1.select{ ... }
end
def var1
@var1 ||= MyModel::CONST1.each_with_index.map { |item, i| [item, i] }
end
end
Upvotes: 2