我如何在 Ruby 中实现我自己的 Rails 风格的 validates() 方法?
How would I implement my own Rails-style validates() method in Ruby?
我正在尝试了解一些 Ruby 元编程概念。
我想我理解 classes、对象和元classes。不幸的是,我非常不清楚包含的模块在它们的实例/'class'变量方面到底发生了什么.
这是一个人为的问题,其解决方案将回答我的问题:
假设我正在编写自己的蹩脚 Rails "validates" 方法,但我希望它来自混合模块,而不是基准 class:
module MyMixin
# Somehow validates_wordiness_of() is defined/injected here.
def valid?
# Run through all of the fields enumerated in a class that uses
# "validate_wordiness_of" and make sure they .match(/\A\w+\z/)
end
end
class MyClass
include MyMixin
# Now I can call this method in my class definition and it will
# validate the word-ness of my string fields.
validate_wordiness_of :string_field1, :string_field2, :string_field3
# Insert rest of class here...
end
# This should work.
MyMixin.new.valid?
好的,那么您将如何存储来自 validate_wordiness_of 调用(在 MyClass 中)的字段列表,以便它可以在 valid 中使用?方法(来自 MyMixin)?
或者我是不是完全错了?任何信息将不胜感激!
所以这里有两种替代方法:
有"direct"访问
module MyMixin
def self.included(base)
base.extend(ClassMethods)
end
def wordy?(value)
value.length > 2
end
module ClassMethods
def validates_wordiness_of(*attrs)
define_method(:valid?) do
attrs.all? do |attr|
wordy?(send(attr))
end
end
end
end
end
class MyClass
include MyMixin
validates_wordiness_of :foo, :bar
def foo
"a"
end
def bar
"asrtioenarst"
end
end
puts MyClass.new.valid?
此方法的缺点 是多次连续调用 validates_wordiness_of
会相互覆盖。
所以你不能这样做:
validates_wordiness_of :foo
validates_wordiness_of :bar
在 class
中保存经过验证的属性名称
您也可以这样做:
require 'set'
module MyMixin
def self.included(base)
base.extend(ClassMethods)
end
module Validation
def valid?
self.class.wordy_attributes.all? do |attr|
wordy?(self.send(attr))
end
end
def wordy?(value)
value.length > 2
end
end
module ClassMethods
def wordy_attributes
@wordy_attributes ||= Set.new
end
def validates_wordiness_of(*attrs)
include(Validation) unless validation_included?
wordy_attributes.merge(attrs)
end
def validation_included?
ancestors.include?(Validation)
end
end
end
class MyClass
include MyMixin
validates_wordiness_of :foo, :bar
def foo
"aastrarst"
end
def bar
"asrtioenarst"
end
end
MyClass.new.valid?
# => true
我选择让 valid?
方法在您实际添加验证之前不可用。这可能是不明智的。如果没有验证,您可能只是 return true。
如果您引入其他类型的验证,此解决方案将很快变得笨拙。在那种情况下,我将开始在验证器对象中包装验证。
我正在尝试了解一些 Ruby 元编程概念。
我想我理解 classes、对象和元classes。不幸的是,我非常不清楚包含的模块在它们的实例/'class'变量方面到底发生了什么.
这是一个人为的问题,其解决方案将回答我的问题:
假设我正在编写自己的蹩脚 Rails "validates" 方法,但我希望它来自混合模块,而不是基准 class:
module MyMixin
# Somehow validates_wordiness_of() is defined/injected here.
def valid?
# Run through all of the fields enumerated in a class that uses
# "validate_wordiness_of" and make sure they .match(/\A\w+\z/)
end
end
class MyClass
include MyMixin
# Now I can call this method in my class definition and it will
# validate the word-ness of my string fields.
validate_wordiness_of :string_field1, :string_field2, :string_field3
# Insert rest of class here...
end
# This should work.
MyMixin.new.valid?
好的,那么您将如何存储来自 validate_wordiness_of 调用(在 MyClass 中)的字段列表,以便它可以在 valid 中使用?方法(来自 MyMixin)?
或者我是不是完全错了?任何信息将不胜感激!
所以这里有两种替代方法:
有"direct"访问
module MyMixin
def self.included(base)
base.extend(ClassMethods)
end
def wordy?(value)
value.length > 2
end
module ClassMethods
def validates_wordiness_of(*attrs)
define_method(:valid?) do
attrs.all? do |attr|
wordy?(send(attr))
end
end
end
end
end
class MyClass
include MyMixin
validates_wordiness_of :foo, :bar
def foo
"a"
end
def bar
"asrtioenarst"
end
end
puts MyClass.new.valid?
此方法的缺点 是多次连续调用 validates_wordiness_of
会相互覆盖。
所以你不能这样做:
validates_wordiness_of :foo
validates_wordiness_of :bar
在 class
中保存经过验证的属性名称您也可以这样做:
require 'set'
module MyMixin
def self.included(base)
base.extend(ClassMethods)
end
module Validation
def valid?
self.class.wordy_attributes.all? do |attr|
wordy?(self.send(attr))
end
end
def wordy?(value)
value.length > 2
end
end
module ClassMethods
def wordy_attributes
@wordy_attributes ||= Set.new
end
def validates_wordiness_of(*attrs)
include(Validation) unless validation_included?
wordy_attributes.merge(attrs)
end
def validation_included?
ancestors.include?(Validation)
end
end
end
class MyClass
include MyMixin
validates_wordiness_of :foo, :bar
def foo
"aastrarst"
end
def bar
"asrtioenarst"
end
end
MyClass.new.valid?
# => true
我选择让 valid?
方法在您实际添加验证之前不可用。这可能是不明智的。如果没有验证,您可能只是 return true。
如果您引入其他类型的验证,此解决方案将很快变得笨拙。在那种情况下,我将开始在验证器对象中包装验证。