还清晰的记得这个问题抛出来的时候,我答非所问,失了方寸去解答时的样子。回来之后,花了一段时间才真正去解决这个问题。
实现代码
module Validate
VALIDATE_RULES = {
:presence => {
:condition => /^\S+$/,
:message => "%{name} cannot be blank"
},
:maxlength => {
:condition => -> (attr, val){ attr.to_s.length <= val },
:message => "%{name} max length is %{val}"
}
}
def self.included(base)
class << base; attr_accessor :defined_validates; end
base.defined_validates = {}
base.send(:include, InstanceMethods)
base.extend ClassMethods
end
# def self.extended(base)
# class << base; attr_accessor :defined_validates; end
# base.defined_validates = {}
# base.extend ClassMethods
# base.send(:include, InstanceMethods)
# end
module ClassMethods
def inherited(base)
base.defined_validates = defined_validates.deep_dup
super
end
def validates(name, options)
defined_validates[name.to_s] = options
end
end
module InstanceMethods
def valid?
@error_messages = {}
self.class.defined_validates.each do |name, conditon|
conditon.each do |rule, value|
next unless value
rule = VALIDATE_RULES[rule]
cond = rule[:condition]
attr_value = send(name)
valid = case cond
when Regexp
cond === attr_value
when Proc
cond.(attr_value, value)
end
@error_messages[name] = rule[:message] % {
name: name,
val: value
} unless valid
end
end
@error_messages.size == 0
end
def error_messages
@error_messages || {}
end
end
end
class User
include Validate
# extend Validate
attr_accessor :name
validates :name, :presence => true, :maxlength => 5
def self.create(options = {})
user = new.tap do |_|
options.each do |attr, value|
_.send("#{attr}=", value)
end
end
end
end
user = User.create(:name => nil)
user.valid?
user.error_messages
思路:
不继承于任何类,所以只能用 include 或 extend 的方法。这里写一个 Validate 的 module,这样任何类只要 include 或 extend 它就有了 validates 的功能了。
实现:
validates 是用来验证一个类的实例的,所以我们只要把 validates 的验证条件存在类的变量就可以了
def validates(name, options)
defined_validates[name.to_s] = options
end
这里有个类变量叫 defined_validates
通过钩子方法把我写好的方法变成了对应类的类方法和实例方法
def self.included(base)
class << base; attr_accessor :defined_validates; end
base.defined_validates = {}
base.send(:include, InstanceMethods)
base.extend ClassMethods
end
我们把我们定义的 InstanceMethods 中的所有方法 include 了,成了实例方法
定义的 ClassMethods 中的所有方法 extend 了,成了类方法
class << base; attr_accessor :defined_validates; end
base.defined_validates = {}
这个只要是定义类变量,就是@@defined_validates,并且初始化,后面在 User 中我们的 validates 其实就存在这个变量中的
我们在使用 valid 的时候,再进行判断就可以了,然后再把判断结果输出到实例变量@error_messages中
无二是个好公司,自己还有很长的路要走。