arguments into instance methods in ruby
So, I'd like to be able to make a call
x = MyClass.new('good morning', 'good afternoon', 'good evening', 'good night',
['hello', 'goodbye'])
that would add methods to the class whose values are the values of the arguments. So now:
p x.methods #> [m_greeting, a_greetin开发者_如何学JAVAg, e_greeting, n_greeting,
r_greeting, ...]
And
p x.m_greeting #> "good morning"
p x.r_greeting #> ['hello', 'goodbye']
I realize that this is sort of what instance variables are to do (and that if I wanted them immutable I could make them frozen constants) but, for reasons beyond my control, I need to make methods instead.
Thanks!
BTW: I tried
def initialize(*args)
i = 0
%w[m_a, m_b, m_c].each do |a|
self.class.send(:define_method, a.to_s, Proc.new { args[i] })
i+=1
end
end
But that ended up giving every method the value of the last argument.
I guess this solves the problem:
def initialize(*args)
@args = args
%w[m_a m_b m_c].each_with_index do |a, i|
eval "def #{a}; @args[#{i}]; end"
end
end
You can do what you want, like so:
class Foo
def initialize(*args)
methods = %w[m_greeting a_greeting e_greeting n_greeting r_greeting]
raise ArgumentError unless args.size == methods.size
args.zip(methods).each do |arg, method|
self.class.instance_eval do
define_method method do
arg
end
end
end
end
end
foo = Foo.new(1, 2, 3, 4, 5)
p foo.m_greeting # => 1
p foo.a_greeting # => 2
p foo.e_greeting # => 3
p foo.n_greeting # => 4
p foo.r_greeting # => 5
But this may not be the droid you're looking for: More than a few positional arguments can make code difficult to read. You might consider using OpenStruct. You'll have to write almost no code, and the constructor calls will be easier to read:
require 'ostruct'
class Foo < OpenStruct
end
foo = Foo.new(:m_greeting=>1,
:a_greeting=>2,
:e_greeting=>3,
:n_greeting=>4,
:r_greeting=>5)
p foo.m_greeting # => 1
p foo.a_greeting # => 2
p foo.e_greeting # => 3
p foo.n_greeting # => 4
p foo.r_greeting # => 5
Don't sweat mutability. If you feel the need to write code to protect yourself from mistakes, consider writing unit tests instead. Then the code can be unfettered with sundry checks and protections.
Your last loop would send the last argument to redefine the method for each of your m_a, m_b, m_c
Try looping over the args and sending to the indexed method.
e.g.
def initialize(*args)
methods = %w[m_a m_b m_c]
args.each_with_index {|item,index|
self.class.send(:define_method, methods[index], lambda { item })
}
end
each_with_index
comes from the Enumerable module: http://ruby-doc.org/core/classes/Enumerable.html#M003137
精彩评论