假设您有来自ActiveSupport :: Concern文档的此代码,但您希望包含的Foo块具有不同的内容,具体取决于包含Foo的模块或类。
在我试图解决的具体问题中,我有一组地址验证,但地址字段将命名为home_zip_code或work_zip_code,我希望包含验证问题以了解zip_code的前缀字段。
require 'active_support/concern'
module Foo
extend ActiveSupport::Concern
included do
# have some_value be accessible
def self.method_injected_by_foo
...
end
end
end
module Bar
extend ActiveSupport::Concern
# set some_value that will used when Foo is included
include Foo
included do
self.method_injected_by_foo
end
end
class Host
include Bar # It works, now Bar takes care of its dependencies
end
我在这里讨论了这个问题:http://forum.railsonmaui.com/t/how-make-a-concern-parameterized/173
以下2个选项有效。我想知道哪个更好。
这是需要“参数化”的问题:
module Addressable
extend ActiveSupport::Concern
included do
zip_field = "#{address_prefix}_zip_code".to_sym
zip_code_regexp = /^\d{5}(?:[-\s]\d{4})?$/
validates zip_field, format: zip_code_regexp, allow_blank: true
end
在找到包含可寻址关注点之前,我找到了两种设置address_prefix的方法。
在包含关注点
之前,需要定义类方法cattr_accessor :address_prefix
self.address_prefix = "home"
include Addressable
或者像这样
def self.address_prefix
"home"
end
include Addressable
这里的技巧是覆盖self.append_features
并添加方法。
def self.append_features(base)
base.class_eval do
def self.address_prefix
"home"
end
end
super
end
或
def self.append_features(base)
base.cattr_accessor :address_prefix
base.address_prefix = "home"
super
end
cattr_accessor
方式还是定义类方法?self.append_features
正确的挂钩?class_eval
是否正确调用创建方法,而不是class exec
?或者,如果代码不需要访问实例变量,则无关紧要。 Module docs here。答案 0 :(得分:0)
cattr_accessor
或类方法是我以前使用的一种好方法。 cattr_accessor
定义了一个方法,所以也可以。差异涉及继承,我认为cattr_accessor
变量不会被继承。如果这可能是一个问题,可能要调查一下。
但实际上我现在会以完全不同的方式来处理。对我来说,最简单的方法是定义一个可以接受参数的类方法,例如acts_as_addressable(address_prefix
。您可以在模块Addressible
中定义此方法,您可以在其中进行以下操作:
extend Addressible
acts_as_addressbile('home')
acts_as_addressible('work')
您可以采用许多宝石采用的快捷方式,并实际上将您的模块强制插入ActiveRecord::Base
,从而避免您在每个课程中都需要使用extend
,但是我个人讨厌这种样式。 / p>
根据您所包含的块在做什么,也许validates_zip_code
是类方法的更好名称。对于样式,我建议您实际使用该字段的全名。稍微多点打字,更易读:
module ZipCodeValidatable
ZIP_CODE_REGEX = /^\d{5}(?:[-\s]\d{4})?$/
def validates_zip_code(zip_field)
validates zip_field, format: ZIP_CODE_REGEX, allow_blank: true
end
end
class MyRecord << ActiveRecord::Base
extend ZipCodeValidatable
validates_zip_code("work_zip_code")
validates_zip_code("home_zip_code")
end
如果您要验证的邮政编码是 all ,则可以使用custom validator。在我看来,您可以使用ActiveModel::EachValidator
的第二部分。我从没使用过这些,但我读过后看起来像这样:
class ZipCodeValidator < ActiveModel::EachValidator
ZIP_CODE_REGEX = /^\d{5}(?:[-\s]\d{4})?$/
def validate(record, attribute, value)
unless ZIP_CODE_REGEX =~ value
errors[attribute] << "Invalid zip code in field #{attribute}"
end
end
end