Example parsing for Hook methods in Ruby and ruby hook instance Parsing
By using the Hook method, we can intervene in the life cycle of Ruby classes or modules, greatly improving programming flexibility.
The following hook methods related to lifecycle are available:
Classes and modules
- Class # inherited
- Module # include
- Module # prepended
- Module # extend_object
- Module # method_added
- Module # method_removed
- Module # method_undefined
Single-piece class-related
- BasicObject # singleton_method_added
- BasicObject # singleton_method_removed
- BasicObject # singleton_method_undefined
Sample Code
Module M1 def self. included (othermod) puts "M1 was included into # {othermod}" endendmodule M2 def self. prepended (othermod) puts "M2 was prepended to # {othermod}" endendendclass C include M1 include M2end # output M1 was encoded into CM2 was prepended to Cmodule M def self. method_added (method) puts "New method: M ##{ method}" end def my_method; endend # output New method: M # my_method
In addition to the methods listed above, you can also rewrite a method of the parent class, perform some filtering operations, and then call the super method to complete the functions of the original function, in this way, the functions similar to the hook method are achieved. In the same way, the surround alias can also be used as an alternative to the hook method.
Use instances
Task Description:
Write a class macro similar to attr_accessor's attr_checked. This class macro is used to test the attribute value. The usage is as follows:
Class Person include CheckedAttributes attr_checked: age do | v> = 18 endendme = Person. newme. age = 39 # okme. age = 12 # throw an exception
Implementation plan:
Use the eval method to compile a kernel method named add_checked_attribute, and add simple verified attributes to the specified class.
Refactor the add_checked_attribute method, remove the eval method, and use other methods to implement it.
Add code block Verification
Modify add_checked_attribute to the required attr_checked and make it available to all classes.
By introducing modules, you can only add the attr_checked method to classes that introduce this function module.
Step 1
def add_checked_attribute(klass, attribute) eval " class #{klass} def #{attribute}=(value) raise 'Invalid attribute' unless value @#{attribute} = value end def #{attribute}() @#{attribute} end end "endadd_checked_attribute(String, :my_attr)t = "hello,kitty"t.my_attr = 100puts t.my_attrt.my_attr = falseputs t.my_attr
In this step, the eval method is used, the class is opened with the class and def keywords respectively, and the get and set methods of the specified attribute are defined, the set method can be used to determine whether the value is null (nil or false). If yes, an Invalid attribute exception is thrown.
Setp 2
def add_checked_attribute(klass, attribute) klass.class_eval do define_method "#{attribute}=" do |value| raise "Invaild attribute" unless value instance_variable_set("@#{attribute}", value) end define_method attribute do instance_variable_get "@#{attribute}" end endend
In this step, the eval method is replaced, and the previous class and def keywords are replaced by the class_eval and define_method methods. The instance_variable_set and instance_variable_get methods are used for setting and obtaining instance variables respectively, there is no difference between usage and the first step, but there are some internal implementation differences.
Step 3
def add_checked_attribute(klass, attribute, &validation) klass.class_eval do define_method "#{attribute}=" do |value| raise "Invaild attribute" unless validation.call(value) instance_variable_set("@#{attribute}", value) end define_method attribute do instance_variable_get "@#{attribute}" end endendadd_checked_attribute(String, :my_attr){|v| v >= 180 }t = "hello,kitty"t.my_attr = 100 #Invaild attribute (RuntimeError)puts t.my_attrt.my_attr = 200puts t.my_attr #200
There is nothing special about it. It only adds code block verification, which increases the flexibility of verification and is no longer limited to between nil and false.
Step 4
class Class def attr_checked(attribute, &validation) define_method "#{attribute}=" do |value| raise "Invaild attribute" unless validation.call(value) instance_variable_set("@#{attribute}", value) end define_method attribute do instance_variable_get "@#{attribute}" end endendString.add_checked(:my_attr){|v| v >= 180 }t = "hello,kitty"t.my_attr = 100 #Invaild attribute (RuntimeError)puts t.my_attrt.my_attr = 200puts t.my_attr #200
Here we put the Chinese method name of the top-level scope in the Class. Because all objects are Class instances, the instance method defined here can also be accessed by all other classes in Ruby, at the same time, in the class definition, self is the current class, so it eliminates the need to call the class parameter and the class_eval method, and we changed the method name to attr_checked.
Step 5
module CheckedAttributes def self.included(base) base.extend ClassMethods endendmodule ClassMethods def attr_checked(attribute, &validation) define_method "#{attribute}=" do |value| raise "Invaild attribute" unless validation.call(value) instance_variable_set("@#{attribute}", value) end define_method attribute do instance_variable_get "@#{attribute}" end endendclass Person include CheckedAttributes attr_checked :age do |v| v >= 18 endend
The last step is to use the Hook method to extend the current class through the introduced module after the CheckedAttributes module is introduced, so that the current class supports the introduced method call, that is, the get and set method groups here.
At this point, we have obtained a class macro named attr_checked, similar to attr_accessor, through which you can verify the attributes you want.