Ruby with Meta programing
Bài đăng này đã không được cập nhật trong 7 năm
What is metaprogramming?
One of the most impressive of Ruby is metaprgramming. One dynamic language, Ruby let you feel free for defining the methods and even the class during runtime. With ruby meta programming, you can finish something minutes while other language can take several hours to do it. By careful planning and skillful organization of your command line using the techniques mentioned below, your code DRY, lightweight, intuitive and more scalable.
We write a program, and this program will generate, control other programs or as one piece of work at the time of compilation. Therefore, metaprogramming help our source becomes shorter, minimizing duplicate issues, such as the methods have similar functionality in source code (especially in the test), easy to change and edit, help system becomes compact and smoother.
Metaprogramming is the act of writing code that operates the natural lines of this code in the code rather than on data. Including checking and modifying the structure of the program activities in the language.
Add method on the content of one object
In Ruby, everything is an object. Basic class (base class) in Ruby called Object (or BasicObject in Ruby 1.9), all other classes inherit attributes from it. Every object in Ruby has its own methods and instance variables (@variable_name) can be added, edited or removed during runtime. Example :
my_object = Object.new
def my_object.set_my_variable=(var)
@my_instance_variable = var
end
def my_object.get_my_variable
@my_instance_variable
end
my_object.set_my_variable = "Hello"
my_object.get_my_variable # => Hello
In the above example, we create a new instance of the class 1 Object and definitions 2 in this instance method to record and read data (settings and getting). 2 This method is defined for object my_object and is only available with this object. This can be demonstrated in the following example:
my_object = Object.new
my_other_object = Object.new
def my_object.set_my_variable=(var)
@my_instance_variable = var
end
def my_object.get_my_variable
@my_instance_variable
end
my_object.set_my_variable = "Hello"
my_object.get_my_variable # => Hello
my_other_object.get_my_variable = "Hello" # => NoMethodError
When calling get_my_variable()the object my_other_object the result returned is NoMethodError means my_other_object unrecognized method get_my_variable () .
Class Methods
The general syntax for writing class method in Ruby:
class MyClass
def self.capitalize_name
name.upcase
end
end
print MyClass.capitalize_name # => MYCLASS
In the example above, the method capitalize_name()is only available with object MyClass , it's just a simple way to guide us how to write one class method also in this example above, we have others different ways to define one method:
class MyClass
def MyClass.capitalize_name
name.upcase
end
end
class MyClass
...
end
def MyClass.capitalize_name
name.upcase
end
MyClass = Class.new
def MyClass.capitalize_name
name.upcase
end
Do you recognize the similarities between 3 simple ways than in first example? through the example above, I think that writing one class method in Ruby is like creating one instance of the class and add methods to that instance.
Brief Explanation Ruby Object Model
Before going deeper to learn about metaprogramming, it is important to understand the basics of Ruby's Object Model and how Ruby deals with how the method call. When you call one method on an object, first the interpreter will search in the instance of the object method to see if it can find a method is called or not. If you can find a method, it will execute method is called, and if not found it will send the request to the object class that chain. If you still can not find the method need it will continue to search in the superclass of that class then the superclass of Object father .... up to class by itself. Khoogn stop there, if you can not find anywhere method of the class of object that inherits it will pass back to the object and call one another method called method_missing () and returns an error NoMethodError .
By defining method_missing () within one class, we can change the default behavior of this method by one of the other effects are useful. In the following example method_missing method () is passed to the second argument: the name of the missing method (same as 1 character) and its array of arguments:
class MyGhostClass
def method_missing(name, *args)
puts "#{name} was called with arguments: #{args.join(',')}"
end
end
m = MyGhostClass.new
m.awesome_method("one", "two") # => awesome_method was called with arguments: one,two
m.another_method("three", "four") # => another_method was called with arguments: three,four
In the example above there is no method awesome_method () or another_method () in our class to call them, instead of seeing the results returned "NoMethodError" as usual, we will see the name of the method and argument them that we have defined in the method method_missing () above.
We can extend this idea by adding conditions in this method, for example, only when one can not find a certain number of methods, the results are returned in method_missing method () returns the rest " NoMethodError "as default. Like in the following example:
class MyGhostClass
def method_missing(name, *args)
if name.to_s =~ /awesome/
puts "#{name} was called with arguments: #{args.join(',')}"
else
super
end
end
end
m = MyGhostClass.new
m.awesome_method("one", "two") # => awesome_method was called with arguments: one,two
m.another_method("three", "four") # => NoMethodError
Ghost Methods
Strictly speaking, "MyGhostClass # awesome_method" is not really the first method. If we create an instance of MyGhostClass and its scanning method to find the method that contains the "awesome", then we will not find any results.
@m = MyGhostClass.new
@m.methods.grep(/awesome/) # => nil
Rather than call it a method we call it the magic method. It comes pros and cons. Its advantage is the ability to meet the coding method when you have no way to identify the method is called. The downside is that changing the default behavior of Ruby like this can cause unexpected errors if you're not careful with your method name. This method can be used drug in the car example above management:
class CarModel
def method_missing(name, *args)
name = name.to_s
super unless name =~ /(_info|_price)=?$/
if name =~ (/=$/)
instance_variable_set("@#{name.chop}", args.first)
else
instance_variable_get("@#{name}")
end
end
end
All rights reserved