Ruby Meta programming technical details (ruby metaprogramming techniques) _lua

Source: Internet
Author: User
Tags aliases eval hash


I have recently considered a lot of metaprogramming (metaprogramming) issues and would like to see more examples and explanations of this technology. For better or worse, Metaprogramming has entered the Ruby community and is a standard way to accomplish various tasks and simplify code. Since I can't find this kind of resources, I'm going to start writing some general Ruby technology articles. These may be useful for programmers who have turned to Ruby from other languages or have not yet experienced the joys of Ruby metaprogramming.



1. Use a single case class using the Singleton-class



Many ways to manipulate a single object are based on manipulating its single instance class (Singleton Class), and this makes metaprogramming simpler. The classic way to get a single example class is to execute the following code:

code as follows:


Sclass = (class << self; self; end)

RCR231 suggests defining kernel#singleton_class methods like this: 

 code as follows:

Module Kernel
def Singleton_class
Class << self; Self End
End
End

I will use this method below.





2. Use class method of DSL to modify subclass Write DSL ' s using class-methods that rewrite subclasses



When you want to create a DSL to define class information, the most common question is how to represent information for use by other parts of the framework. To define a ActiveRecord model object as an example:


 code as follows:

Class Product < ActiveRecord::Base
Set_table_name ' Produce '
End





In this case, it is interesting to use the set_table_name. How does that work? Well, here's a little magic. This is an implementation method:


 code as follows:

Module ActiveRecord
Class Base
def self.set_table_name Name
Define_attr_method:table_name, Name
End
def self.define_attr_method (name, value)
Singleton_class.send:alias_method, "Original_#{name}", name
Singleton_class.class_eval do
Define_method (name) do
Value
End
End
End
End
End





What's interesting here is Define_attr_method. In this example we need to get a single instance class of the product class, but we don't want to modify the activerecord::base. By using a single example class we have achieved this goal. We have aliased the original method and then defined a new accessor (accessor) to return the value. If ActiveRecord needs table name, it can invoke the accessor directly. This dynamic creation method and accessor techniques are common in a single instance class, especially rails.



3. Dynamically create class and module create classes and modules dynamically



Ruby allows you to dynamically create and modify class and module. You can make any changes to the class or module that are not frozen. can be useful in certain situations. The struct class may be the best example:


 code as follows:

Personvo = struct.new (: Name,:p Hone,: email)
P1 = personvo.new (: Name => "Ola Bini")

This creates a new class, assigns it to Personvo, and then creates an instance of the class. It is also simple to create a new class from a draft and define a new method:
 code as follows:

c = class.new
C.class_eval do
Define_method:foo do
Puts "Hello World"
End
End
C.new.foo # => "Hello World"

In addition to struct, you can find examples of easily created classes in soap4r and camping. Camping is particularly interesting because it has a special way of creating these classes, which are inherited by your controller and view. Many of the interesting features of camping are implemented in this way:
 code as follows:

def R (*urls); Class.new (R) {meta_def (: URLs) {URLs}};
End


This makes it possible to create controller:
Class View < R '/view/(\d+) '
def get post_id
End
End


You can also create a module, and then include a module in the class.





4. Use the method_missing to do interesting things using method_missing to do interesting things



In addition to closures, method_missing may be the most powerful feature of Ruby and the easiest to abuse. With good method_missing, some code can become super simple, even can not be missing. A good example (camping) is an extended hash:


 code as follows:

Class Hash
def method_missing (M,*a)
If m.to_s =~/=$/
self[$ '] = a[0]
Elsif a.empty?
SELF[M]
Else
Raise Nomethoderror, "#{m}"
End
End
End

You can use hash like this:
 code as follows:

x = {' abc ' => 123}
X.ABC # => 123
X.foo =: Baz
X # => {' abc ' => 123, ' foo ' =>: Baz}

As you can see, if someone calls a method that does not exist in a hash, the internal collection is searched. If the method name ends with =, it is assigned to the key with the same name.





Another good method_missing technique can be found in Markaby. The following referenced code can generate any XHTML tag that contains CSS class:


 code as follows:

Body do
H1.header ' Blog '
Div.content do
' Hellu '
End
End

Will generate:
 code as follows:

<body>

<div class= "Content" >
Hellu
</div>
</body>


Most of this functionality, especially the CSS class name, is set by method_missing the Self property and then back to self.





5. Method mode scheduling Dispatch on Method-patterns



This can be easily scalable for unpredictable methods. I recently created a small validation framework where the core validation classes find all of their own methods that start with Check_ and invoke them, which makes it easy to add new validations: Simply adding new methods to the class or instance.
Methods.grep/^check_/do |m|
Self.send m
End



It's very simple and incredibly powerful. You can take a look at Test::unit everywhere using this method.



6. Replacement method Replacing methods



Sometimes the implementation of a method is not what you want, or only half of it. The standard object-oriented approach is to inherit and overload, and then invoke the parent class method. It is only useful when you have an object-instantiated control, which is often not the case, and inheritance has no value. To get the same functionality, you can rename (alias) The old method, add a new method definition to invoke the old method, and ensure that the old method's conditions are preserved.


 code as follows:

Class String
Alias_method:original_reverse,: Reverse
DEF reverse
Puts "reversing, please wait ..." original_reverse
End
End

An extreme use is to temporarily modify a method and then restore it. For example:
 code as follows:

def Trace (*mths)
Add_tracing (*mths) # aliases methods named, adding tracing
Yield
Remove_tracing (*mths) # removes the tracing aliases
End

This example shows a typical way to write add_tracing and remove_tracing. It relies on the single example class of article 1th:
 code as follows:

Class Object
def add_tracing (*mths)
Mths.each do |m|
Singleton_class.send:alias_method, "Traced_#{m}", M
Singleton_class.send:d Efine_method, M do |*args|
$stderr. puts "before #{m} (#{args.inspect})"
ret = Self.send ("Traced_#{m}", *args)
$stderr. puts "after #{m}-#{ret.inspect}"
Ret
End
End
End
def remove_tracing (*mths)
Mths.each do |m|
Singleton_class.send:alias_method, M, "traced_#{m}"
End
End
End
"ABC". Add_tracing:reverse

If these methods are added to the module (a little different, see if you can write it out!) , you can also add and remove tracing on a class, not an instance.





7. Use the nil class to introduce the refactoring of empty objects using Nilclass to implement the introduce null object refactoring



In Fowler refactoring, the "introduce empty object" Refactoring is an object that has either a predefined value or a null one. Typical examples are as follows:


 code as follows:

Name = X.nil?? ' Default name ': X.name

Currently, java-based Refactoring recommends creating a subclass that is similar to null. For example, Nullperson inherits person, and the overloaded name method always returns "default name." But in Ruby we can open a class, and you can do this:
 code as follows:

def Nil.name; "Default name"; End
X # => Nil
name = x.name # => ' default name '





8. Learn different versions of Eval Learn the different versions of Eval



Ruby has several versions of the execution method (evaluation). It is important to understand their differences and use scenarios. There are a few eval, Instance_eval, Module_eval and Class_eval. First, Class_eval is the alias of Module_eval. Second, Eval is somewhat different from others. The most important thing is that eval can only execute a string, others can execute block. This means that eval is the last choice you have to do anything, it has its uses, but in most cases it should be done with Instance_eval and Module_eval.



Eval executes the string in the current environment unless the environment has provided a binding (binding). (see article 11th)



Instance_eval executes a string or block in the context of the recipient (reveiver), without specifying the self as the recipient.



Module_eval executes a string or block in the context of the calling module. This is a better fit to define a new method in a module or a single instance class. The main difference between Instance_eval and Module_eval is where the defined method will be placed. If you use String.instance_eval to define Foo method, you will get String.foo, if you use Module_eval, you will get String.new.foo.



Module_eval is almost always applicable; avoid eval as you do with plagues. It will be good for you to follow these simple rules.




9. Introspective Introspect on instance variables of instance variables



Rails uses a trick to make instance variables in controller also available in view, which is to introspect an instance variable of an object. This can seriously damage the encapsulation, but sometimes it's really handy. Can be easily achieved through instance_variables, Instance_variable_get, and Instance_variable_set. To copy all instance variables from one to another, you can do this:


 code as follows:

From.instance_variables.each do |v|
To.instance_variable_set V, from.instance_variable_get (v)
End





10. Create the proc from the block and expose the create procs from blocks and send them around



The practice of storing a proc instantiation in a variable and exposing it makes many APIs easy to use. This is a way for Markaby to manage CSS class definitions. It's easy to convert blocks into proc:
def create_proc (&p); P End
Create_proc do
Puts "Hello"
End # => #<proc ...>



It's also easy to call:
P.call (*args)



If you want to define a method with Proc, you should create it with a lambda, and you can use return and break:
P = lambda {puts "HoHo"; return 1}
Define_method (: A, &p)



If there is block, Method_missing will call block:
def method_missing (name, *args, &block)
Block.call (*args) if Block_given?
End
Thismethoddoesntexist ("abc", "CDE") do |*args|
P args
End # => ["abc", "CDE"]




11. Use binding (binding) to control the eval using binding to controls your evaluations



If you really need to use eval, you can control which variables are valid. This is when you use the kernel method binding to get the object you are binding on. For example:


 code as follows:

def Get_b; Binding End
Foo = 13
Eval ("puts Foo", Get_b) # => nameerror:undefined local variable or by ' foo ' for Main:object

Erb and rails Use this technique to set which instance variables are valid. For example:
 code as follows:

Class Holder
def Get_b; Binding End
End
H = holder.new
H.instance_variable_set "@foo", 25
Eval ("@foo", H.get_b)





Hopefully these techniques and techniques have clarified metaprogramming for you. I don't claim to be a Ruby or metaprogramming expert, that's just some of my thoughts on the subject.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.