By using the Hook method, you can allow us to intervene in the lifecycle of a Ruby class or module, which can greatly improve the flexibility of programming.
The hook methods associated with the lifecycle are as follows:
Class is associated with a module
- 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 is included into #{othermod}"
end
module M2
def self.prepended (othermod)
puts "M2 is prepended to #{othermod}"
end
class C
Include M1
include M2
end
# output
M1 is included into C
M2 is prepended to C
module M
de F self.method_added (method)
puts the "New Method:m##{method}" End
def My_method;
New Method:m#my_method
In addition to some of the above listed methods, can also be overridden by a method of the parent class, some filtering operations, and then by calling the super method to complete the function of the original function, so as to achieve the effect of the hook method, the same, surround alias can also be used as a hook method of substitution.
Application examples
Task Description:
Write an action method similar to Attr_accessor's attr_checked class macro, which is used to test the value of the property, using the following method:
Class person
include Checkedattributes
attr_checked:age do |v|
V >= end
me = person.new
me.age = #ok
me.age = #抛出异常
Implementation plan:
Use the Eval method to write a kernel method named Add_checked_attribute that adds a simple checksum property to the specified class
Refactor the Add_checked_attribute method, remove the Eval method, and use other means to implement
Add code block checksum
Modify Add_checked_attribute to the required attr_checked and make it available to all classes
By introducing the module, the Attr_checked method is only added to the class that introduces the 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
Add_checked_attribute (String,: my_attr)
t = "Hello,kitty"
t.my_attr = 100
puts t.my_attr
t.my_attr = False
puts t.my_attr
This step uses the Eval method, opens the class with class and Def keywords, and defines the get and set methods of the specified property, where the set method simply evaluates whether the value is empty (nil or false) and throws an invalid attribute exception if it is.
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 end
This step replaces the Eval method, while also replacing the previous class and DEF keywords with the Class_eval and Define_method methods, and the settings and acquisition of instance variables are changed to Instance_variable_set and instance respectively. _variable_get method, the use of the first step without any difference, but some of the internal implementation of the difference.
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 does
instance_variable_get "@#{attribute}" end End end
add_ Checked_attribute (String,: my_attr) {|v| v >= 180}
t = "Hello,kitty"
t.my_attr = #Invaild attribute (runt Imeerror)
puts t.my_attr
t.my_attr =
puts t.my_attr #200
Nothing fancy, just adding to the code block validation, increases the flexibility of checksums, no longer limited to 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 does
instance_variable_get "@#{attribute}"
end End String.add_checked (: my_attr) {|v| v >= 180}
t = "Hello,kitty"
t.my_attr = #Invaild attribute (runtimeerr OR)
puts t.my_attr
t.my_attr =
puts t.my_attr #200
Here we put the name of the method in the top-level scope in class, and since all objects are instances of class, the instance method defined here can also be accessed by all other classes in Ruby, and in the class definition, self is the current class, So it eliminates the call class parameter and Class_eval method, and we change the name of the method to attr_checked.
Step 5
Module Checkedattributes
def self.included (base)
base.extend classmethods
end
module 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 do
instance_variable_get ' @#{attribute} ' end
end
class Person
include checkedattributes
attr_checked:age do |v|
V >= End
The last step is to extend the current class through the introduced module after the Checkedattributes module is introduced, so that the current class supports the introduced method invocation, that is, the get and set method groups here.
So we've got a class macro called attr_checked, like Attr_accessor, through which you can perform the validation you want on the property.