0

Ruby Metaprogramming - define_method

Dear friends, in the article before I had an introduction to metaprogramming in Ruby and how to use the some function. To follow up the topic of metaprogramming in Ruby, this article I will mention define_methodand some of its applications.

Before the start, I have a small sample of the class:

class Dog
  def initialize name
   .@name = name
  end

  def is_dog?; true; end
end

Here you have a Dog class - also known as the dog. I have a dog, named it the "mic". To create the "mic", I will type:

a = Dog.new "mic"
a.is_dog?
=> true

As you can see, I have created a dog named dog1 mic, but I had no method name in the Dog class, I just know there's an instance variable called name is assigned to his dog. So how I can know your dog's name was created? The answer is because of the method instance_eval:

dog1.instance_eval{@name}
=> "mic"

So we were able to know the dog's name, but the statement seems a bit long and rather confusing. I'm going to declare a more concise method to obtain the name of the "mic" her:

dog1.instance_eval do
  def name
    .@name
  end
end

dog1.name
=> "mic"
dog1.define_singleton_method :name do
  .@name
end
dog1.name
=> "mic"
dog2.name
=> NoMethodError: undefined method `name' for...

And we did have a way for his dog name. But, if I may add another dog, I would not be able to use the method which his name was declared for the "mic" to use for the "join":

dog2 = Dog.new "join"
dog2.is_dog?
=> true
dog2.instance_eval{@name}
=> "join"
dog2.name
=> NoMethodError: undefined method `name' for...

So we can not use instance_evalto declare methods common to many objects of class Dog is. This method will be very useful if we need to declare private methods for a specific audience, but if you need to declare the method used for more specific audiences, perhaps we will need a tool like class_eval:

Dog.class_eval do
  def name
    .@name
  end
end

dog1.name
=> "mic"
dog2.name
=> "join"

Thus, thanks to the two methods instance_eval, and class_evalwe were able to declare method applies only to a single object and the method is applicable to a wide audience. But, there is another method that can be used as a substitute for his two aforementioned methods; that is define_method.

class Dog
  [...]
  def define_name
    define_method "name" do
      .@name
    end
  end

  define_method "kick" do
    "My " << .@name
  end
end

dog1.name
=> NoMethodError: undefined method `name` for ...

dog1.define_name
dog1.name
=> "join"
dog1.kick
=> "My mic"
dog2.kick
=> "My join"
Dog.send(:define_method, :name) do
  .@name
end

Other than instance_evaland class_evalis 2 public method, define_methodis private method. Therefore, we can only use it within the class to declare it, In the example above, you have to declare the methods kick in define_method, instead of using def kick; "My " << @name; endas normal. And I have also created a method define_name. Only dogs have been called define_namethe new method name. When only want to use a method for a certain number of objects, we declared that method through a different method. But why use define_methodthat is not a traditional function declaration?

The answer is in its versatility:

With define_method, we can declare the method with arbitrary names, depending on the context of use. This is what a traditional declared impossible. On the other hand, define_methodalso help us to "save" the line of code. Example:

 def a
   @a
 end

 def b
   @b
 end

 def c
   @c
 end
 ...

 %w(a b c ...).do |x|
   define_method x do
     instance_variable_get "@#{x}"
   end
 end

In short, define_methodas an improved eval, allowing us Automation program while ensuring the safety part of it.

In addition to the methods declared in define_methoda little longer than declared by defthe use of conventional and define_methodmay be confusing for our code, then everything else is almost fine. We can use define_methodin some cases to more stores some time writing code, the simplest is in the example above.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.