I was implementing a form that includes a hard-coded dropdown for a collection and I was wondering what would be the best solution, I know both ways exposed below work, still I did as follows:
class Example
# Options for Example.
self.options
[ 'Yes', 'No', 'Not sure' ]
end
end
which is called by Example.options
, but I know it is possible to do as follows as well:
class Example
# Options for Example.
OPTIONS = [ 'Yes', 'No', 'Not sure' ]
end
that would be called with Example::OPTIONS
.
The question is, is any of these the good way or it just doesn't matter at all?
The latter is better. If it were a method, a new array and new strings will be created every time it is called, which is a waste of resource.
TL;DR: It depends. Are the values meant to be used outside the class? Could they ever become dynamic? Could they change for subclasses?
As @sawa wrote, the downside of the method (written this way) is that a new array and strings are created each time.
A better way to write it would be:
class Example
def self.options
@options ||= ['Yes', 'No', 'Not sure']
end
end
The array is stored in the instance variable @options
, to avoid creating a new array each time.
Written this way, the method is very similar to the constant.
One key difference is if Example
is subclassed, it will be more natural to refine the options
method than the constant OPTIONS
:
class Parent < Example
def self.options
@options ||= [*super, 'Extra']
end
end
To do something similar with constants is difficult. Imagine that your list of options is used in a class method, this would look like:
class Example
OPTIONS = ['Yes', 'No', 'Not sure']
def self.foo(arg)
puts "Available options:",
self::OPTIONS # The self:: is needed here
# ...
end
end
class Parent < Example
OPTIONS = [*superclass::OPTIONS, 'Extra']
end
The tricky thing about constants, is that self::OPTIONS
and OPTIONS
are not the always same, while self.options
and options
are the same. Constants are usually used without specifying the scope (eg OPTIONS
instead of self::OPTIONS
) and inheritance will simply not work in that case.
Note that the method gives you the opportunity to make the result dynamic (ie return different results depending on other circumstances) without changing the API.
Final note: I'd recommend calling freeze
on your array, to avoid anyone modifying it.
What I usually do is have a mix of above-mentioned techniques:
class Player
JURISDICTIONS = %i(de uk ru)
def self.jurisdictions
JURISDICTIONS
end
end
It has few advantages:
Player.jurisdictions
instead of Player::JURISDICTIONS
). IMHO, performance does not matter here.
Update : Constant can bee hidden using private_constant
method ( http://ruby-doc.org/core-2.3.0/Module.html#method-i-private_constant )
To further refine Artur's suggestion I would go with a class variable in order to hide visibility of the constant.
class Player
@@jurisdictions = %i(de uk ru)
def self.jurisdictions
@@jurisdictions
end
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.