1

I have to write a my_initialize method in class Class, so that it works in this manor, when used in another class:

class Person my_initialize :name, :surname end

is equivalent to :

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

It also has to raise an ArgumentError if a wrong number of arguments is passed. For example Person.new("Mickey") is invalid. I know that my code should look something like:

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

I just started to read metaprogramming, but can't find anything useful for my problem. Any ideas how to do this task?

1
  • Keep on reading until you discover define_method. :) Commented Nov 19, 2014 at 23:40

2 Answers 2

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

The Module#define_method method takes a method name and block and defines the method for that module. In this case, the module is C. The Object#instance_variable_set method takes an instance variable name and a value and sets it. The instance of Object in this case would be an instance of C.

By the way, it is best to avoid using methods where you pass a string of code in to be evaluated. I would recommend passing blocks instead.

Sign up to request clarification or add additional context in comments.

1 Comment

Good one, Adrian! I thought you might have to make initialize private, but no, Ruby takes care of that: C.private_instance_methods(false) => [:initialize].
1

Here's another way that does not use 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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.