如何在每个对象类型级别创建接口?
How to create an interface on a per object type level?
在我的 rails (4.2.1) 应用程序中,我有一个类型(模型),其中包含 :name 为“string”、“integer”等的记录
我希望用户能够传入值并检查它是否是给定类型的有效对象。所以,伪代码是:
check_value(:integer, "1") #=> true
check_value(:integer, "foo") #=>false
我想随着时间的推移添加新类型,这些类型有自己的逻辑 check_value 该类型。
以下是我看过的几个备选方案:
1 将每种类型的一种方法直接添加到类型模型中 -
# 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 文件,我希望避免这种情况。
每个类型文件中每个对象方法 2 个:
# 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 的验证调用委托给要处理的单个类型。这可能吗?我该怎么做?
在 Ruby 中有多种方法可以做到这一点。这是一种非常基本的方法。让每个类型模块定义一个 check
方法,然后使用 Type 模块定义 "register" 本身,例如Type.register(:integer, IntegerType)
。然后 Type.check(type, value)
只需要检查注册的类型,如果匹配,则委托给它的 check
方法:
type.rb
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!
types/integer.rb
module Type
module Integer
def self.check(value)
!!Integer(value)
rescue ArgumentError
false
end
end
end
Type.register(:integer, Type::Integer)
types/string.rb
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
而不是将已注册模块保存在简单哈希中的解决方案)或者说,懒惰加载,但你明白了。 "core" 类型模块不必知道其他模块的名称(您可以根据需要定义 load_types!
,或者完全在其他地方定义)。唯一的要求是模块响应 "check_#{type}"
.
在我的 rails (4.2.1) 应用程序中,我有一个类型(模型),其中包含 :name 为“string”、“integer”等的记录
我希望用户能够传入值并检查它是否是给定类型的有效对象。所以,伪代码是:
check_value(:integer, "1") #=> true
check_value(:integer, "foo") #=>false
我想随着时间的推移添加新类型,这些类型有自己的逻辑 check_value 该类型。
以下是我看过的几个备选方案:
1 将每种类型的一种方法直接添加到类型模型中 -
# 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 文件,我希望避免这种情况。
每个类型文件中每个对象方法 2 个:
# 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 的验证调用委托给要处理的单个类型。这可能吗?我该怎么做?
在 Ruby 中有多种方法可以做到这一点。这是一种非常基本的方法。让每个类型模块定义一个 check
方法,然后使用 Type 模块定义 "register" 本身,例如Type.register(:integer, IntegerType)
。然后 Type.check(type, value)
只需要检查注册的类型,如果匹配,则委托给它的 check
方法:
type.rb
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!
types/integer.rb
module Type
module Integer
def self.check(value)
!!Integer(value)
rescue ArgumentError
false
end
end
end
Type.register(:integer, Type::Integer)
types/string.rb
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
而不是将已注册模块保存在简单哈希中的解决方案)或者说,懒惰加载,但你明白了。 "core" 类型模块不必知道其他模块的名称(您可以根据需要定义 load_types!
,或者完全在其他地方定义)。唯一的要求是模块响应 "check_#{type}"
.