需要进行定义和执行的类
#示例代码
class Person
include CheckedAttributes
attr_checked :age do |v|
v >= 18
end
end
me = Person.new
me.age = 39 #success
me.age = 12 #error
step1:使用eval方法编写一个名为add checked attribute的内核属性,用来为类添加一个最为简单的见过校验的属性
def add_checked_attribute(klass, attribute)
eval "
class #{klass}
def #{attribute}=(value)
raise(NameError, 'value is wrong') unless value
@#{attribute} = value
end
def #{attribute}
@#{attribute}
end
end
"
end
add_checked_attribute(String, :age)
obj = String.new
obj.age = 10
p obj.age #10
obj.age = nil
p obj.age #value is wrong
#注意点
1.add_checked_attribute中的参数不能为常量,就是不能 def add_checked_attribute(Klass, attribute)
2.add_checked_attribute中的第一个个参数必须为系统中已经定义过的常量,比如String这个核心类,如果使用Person则为错
3.add_checked_attribute中的第二个参数为方法名,必须使用":"的形式,如果使用age则会报查找不到这个变量的错误
step2:使用instance_eval替代eval
def add_checked_attribute(klass, attribute)
klass.class_eval{
define_method("#{attribute}=") do |value|
raise(NameError, 'value is wrong') unless value
instance_variable_set("@#{attribute}", value)
end
define_method("#{attribute}") do
instance_variable_get("@#{attribute}")
end
}
end
add_checked_attribute(String, :age)
obj = String.new
obj.age = 10
p obj.age #10
obj.age = nil
p obj.age #value is wrong
#注意点
1.instance_variable_set和instance_variable_get中的参数有两种形式,分别是
instance_variable_set(:@name, "jayzen")
instance_variable_set("@name", "jayzen")
step3:使用块检验属性,step1-2中的代码中只能满足false和nil的情况,step扩展到可以通过块来进行校验
def add_checked_attribute(klass, attribute, &validation)
klass.class_eval{
define_method("#{attribute}=") do |value|
raise(NameError, 'value is wrong') unless validation.call(value)
instance_variable_set("@#{attribute}", value)
end
define_method("#{attribute}") do
instance_variable_get("@#{attribute}")
end
}
end
add_checked_attribute(String, :age, &proc{|value| value >= 18})
obj = String.new
obj.age = 20
p obj.age #20
obj.age = 10
p obj.age #value is wrong
step4:使用类宏的形式来定义方法
class Class
def attr_checked(attribute, &validation)
define_method("#{attribute}=") do |value|
raise(NameError, 'value is wrong') unless validation.call(value)
instance_variable_set("@#{attribute}", value)
end
define_method("#{attribute}") do
instance_variable_get("@#{attribute}")
end
end
end
class Person
attr_checked :age do |v|
v >= 18
end
end
obj = Person.new
obj.age = 20
p obj.age #20
obj.age = 10
p obj.age #value is wrong
step5:使用hook方法来实现最开始的功能
module CheckedAttributes
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def attr_checked(attribute, &validation)
define_method("#{attribute}=") do |value|
raise(NameError, 'value is wrong') unless validation.call(value)
instance_variable_set("@#{attribute}", value)
end
define_method("#{attribute}") do
instance_variable_get("@#{attribute}")
end
end
end
end
class Person
include CheckedAttributes
attr_checked :age do |v|
v >= 18
end
end
obj = Person.new
obj.age = 20
p obj.age
obj.age = 10
p obj.age