在我的rails(4.2.1)app中,我有一个Type(模型),其中包含以下内容的记录:" string"," integer"等等。
我希望用户能够传入值并检查它是否是给定类型的有效对象。所以,伪代码是:
check_value(:integer, "1") #=> true
check_value(:integer, "foo") #=>false
我想随着时间的推移添加新类型,这些类型对于该类型有check_value的逻辑。
以下是我看过的一些替代方案:
# app/models/type.rb
# inside class Type...
def check_string_value(val)
true
end
def integer_value(val)
begin
Integer(val)
rescue
return false
end
return true
end
这可行,但每次添加新字段类型时都需要我修改type.rb文件,我想避免这种情况。
每个类型的文件中每个对象方法# lib/types/integer_type/integer_type.rb
int = Type.where(name: "integer").first
class << int
def check_value(val)
begin
Integer(val)
rescue
return false
end
return true
end
end
这个问题是我无法调用整数类型的特定实例来传递验证调用,因为我没有在我的调用代码中构造它。
所以,这些似乎都不理想 - 我想要一种技术,将来自type.rb的验证调用委托给要处理的单个类型。这可能吗?我怎么能这样做?
答案 0 :(得分:2)
在Ruby中有很多方法可以做到这一点。这是一种非常基本的方式。让每个类型模块定义check
方法然后&#34;注册&#34;本身与Type模块有关,例如Type.register(:integer, IntegerType)
。然后Type.check(type, value)
只需检查已注册的类型,如果匹配,则委托其check
方法:
module Type
@@checkers = {}
def self.check(type, value)
if @@checkers.key?(type)
@@checkers[type].check(value)
else
raise "No registered type checker for type `#{type}'"
end
end
def self.register(type, mod)
@@checkers[type] = mod
end
def self.registered_types
@@checkers.keys
end
def self.load_types!
Dir['./types/*.rb'].each do |file|
require file
end
end
end
Type.load_types!
module Type
module Integer
def self.check(value)
!!Integer(value)
rescue ArgumentError
false
end
end
end
Type.register(:integer, Type::Integer)
module Type
module String
def self.check(value)
true
end
end
end
Type.register(:string, Type::String)
p Type.registered_types # => [ :integer, :string ]
p Type.check(:integer, "1") # => true
p Type.check(:integer, "a") # => false
p Type.check(:string, "a") # => true
当然,使用元编程可以比这更好看(请参阅此答案的先前修订版,以获得使用Module#extend
而不是将注册模块保存在简单哈希中的解决方案),或者说,延迟加载,但是你明白了。 &#34;核心&#34;类型模块不必知道其他模块的名称(您可以根据需要定义load_types!
,或者完全在其他地方执行此操作)。唯一的要求是模块响应"check_#{type}"
。