开发者

arguments into instance methods in ruby

开发者 https://www.devze.com 2022-12-29 09:37 出处:网络
So, I\'d like to be able to make a call x = MyClass.new(\'good morning\', \'good afternoon\', \'good evening\', \'good night\',

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

0

精彩评论

暂无评论...
验证码 换一张
取 消