简体   繁体   中英

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_greeting, 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.

eg

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

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM