Hi Rubyists !
While writing a ‘getter’(it actually does more than just getting a
@var’s value) method, I was wondering what was the most ruby-way to
write it.
The ‘issue’ is
if the @var is not initialized in constructor, and you access it
directly like “@var”,
Ruby gives you a warning(only if you enable them of course) :
“warning: instance variable @value not initialized”
While “@var” returns nil as expected, it still show this warning.
Well, I know you would probably not care too much about warnings(do
you?), but how would you deal to make it disappear?
If you use attr_* methods, you’ll never see this warning, because it’s
“cheating” (For what I understood of the C code, it checks if
defined).
In fact, a ruby equivalent(for the user) of
attr_reader :var
is
def var
instance_variable_defined?(:@var) ? @var : nil
end
and not just “@var” (because of the warning).
I think you will agree with me, using #instance_variable_defined? is
not very nice.
Now I begin to describe the real code, because abstract examples would
be hard.
Here it is:
class Variable
attr_accessor :name, :proc
attr_writer :value
Create a new Variable, with optional name, value and proc
def initialize(*args, &proc)
args.each do |arg|
case arg
when Numeric then @value = arg
when String, Symbol then @name = arg.to_s
else raise ArgumentError, “…”
end
end
@proc = proc
end
def value
@value or @proc && @proc.call.value
end
def to_s
name || ‘unnamed_variable’
end
end
So the constructor have optional *args, and so we are not sure if a
String or Symbol will be given for the name, neither if the value will
be provided.
For #to_s, I used the accessor(getter) of @name (“name”), that avoid
me the warning(instead of @name || …)
But for value, it’s a bit complicated. Because we want to return or
the value if given, or the result of the proc.
I can’t use the accessor of value, because we are in a method with the
same name …
There are then many possibles:
- Initialize @value in #initialize : “@value = nil” or “@value =
unnamed_variable” (but it looks so old school) - Verify if @value is defined with #instance_variable_defined? :
instance_variable_defined?(:@value) ? @value : @proc &&
@proc.call.value (but it’s awful to read, btw #defined? still show the
warning) - We don’t care about warnings !
- bad names involve this conflict, we should rename @value (we can’t
change the name of the method), and then use his accessor. The main
problem is we’ll have then something like var.val = 2, which is less
easy to understand than var.value = 2. - Another idea ?
Excuse me if I’m writing very much for a very small problem, but I
thank you already if you take time to read and/or reply
Regards,
B.D.