元编程:自定义初始化程序

时间:2014-11-19 23:12:07

标签: ruby class metaprogramming

我必须在类my_initialize中编写一个Class方法,以便它在这个庄园中工作,当在另一个类中使用时:

class Person my_initialize :name, :surname end

相当于:

class Person def initialize(name, surname) @name, @surname = name, surname end end

如果传递了错误数量的参数,它还必须引发ArgumentError。例如Person.new("Mickey")无效。我知道我的代码应该类似于:

class Class
  def my_initialize(*args)
    args.each do |arg|      
     self.class_eval("?????")          
    end
  end 
end

我刚开始阅读元编程,但找不到对我的问题有用的东西。任何想法如何完成这项任务?

2 个答案:

答案 0 :(得分:2)

class Class
  def my_initialize(*vars)
    define_method :initialize do |*args|
      if args.length != vars.length
        raise ArgumentError, 'wrong number of arguments'
      end
      vars.zip(args).each do |var, arg|
        instance_variable_set :"@#{var}", arg
      end
    end
  end
end

class C
  my_initialize :a, :b
end

Module#define_method方法接受方法名称和块,并定义该模块的方法。在这种情况下,模块为CObject#instance_variable_set方法接受实例变量名称和值并设置它。在这种情况下,Object的实例将是C的实例。

顺便说一句,最好避免使用传递一串代码进行评估的方法。我建议改为传递块。

答案 1 :(得分:1)

这是另一种不使用define_method的方式。

class Class
  def my_initialize(*vars)
    str = "def initialize(*args)
             raise ArgumentError if args.size != #{vars.size}
             #{vars}.zip(args).each do |var, arg|
               instance_variable_set(\"@\#{var}\", arg)
             end
           end"
    class_eval str
  end
end

class C
  my_initialize :a, :b
end

c = C.new("Betty", "Boop")
  #=> #<C:0x00000102805428 @a="Betty", @b="Boop">

C.private_instance_methods(false)
  #=> [:initialize]
c.instance_variables
  #=> [:@a, :@b]

C.new("Betty")
  #=> ArgumentError