我有一个方法,它将一个整数数组作为参数,我想使用装饰器模式首先验证数组中的每个数字是否在指定的范围内。
我在ruby中看过装饰器扩展类或包含模块,但这对我来说似乎有点过分,有没有办法在没有中继类或模块的情况下装饰方法?
修改 我有几个方法将数组作为参数,并需要验证项的范围。我不想在每个方法中对内部编码进行内联编码,而是想要所有这些方法的装饰器,但我想知道装饰器类/模块是否只是Ruby中存在的那些?
class MyClass
..some code here ...
def method(array)
..some code here...
end
end
答案 0 :(得分:5)
这是一个如何使用验证器包装方法的简单示例:
class Foo
def foo(a, b, c)
p a, b, c
end
def bar(a, b)
p a, b
end
validate(:foo, :bar) {|*args, &blk| args.reduce(:+) == 6 }
end
Module#validate
方法获取方法名称列表,以及具有这些方法验证逻辑的块。
foo = Foo.new
foo.foo(1, 2, 3)
# 1
# 2
# 3
foo.bar(2, 4)
# 2
# 4
foo.foo(2, 3, 4)
# [2, 3, 4] (Validator::ValidationError)
foo.bar(2, 3)
# [2, 3] (Validator::ValidationError)
如您所见,验证器在最后两次调用中拒绝了参数列表,因为它们没有在块中传递条件。
这是"魔法"这一切都发生了,实际上并不是那么神奇。我们生成一个module
,它覆盖了我们要验证的方法,如果验证失败,raise
是一个例外,那么只需调用super
。然后我们prepend
该模块到我们当前所在的类/模块,即调用validate
方法的模块。基本上就是这样。
我选择成为一名优秀的红宝石公民并将整个事情包装在精炼中,所以你需要说
using Validator
实际激活它。
module Validator
class ValidationError < ArgumentError; end
refine Module do
def validate(*methods, &validator)
prepend(Module.new do
methods.each do |method|
define_method(method) do |*args, &blk|
raise ValidationError, args.inspect unless yield *args
super(*args, &blk)
end
end
end)
end
end
end
答案 1 :(得分:3)
通常,当你谈论装饰时,你会在课程的上下文中使用它,而不是单一的方法。
以下是您可以装饰的课程示例:
class ValidatorSet
def initialize
@validators = [ ]
end
def <<(validator)
@validators << validator
end
def valid?(list)
@validators.all? do |v|
if v.respond_to?(:valid?)
v.valid?(list)
else
list.all?(&v)
end
end
end
end
class IsUnderTen
def valid?(list)
list.all? { |n| n < 10 }
end
end
validator = ValidatorSet.new
validator << IsUnderTen.new
validator << lambda { |n| n > 0 }
validator.valid?([ 1, 2, 3, 4, 5 ])
# => true
validator.valid?([ -1 ])
# => false
validator.valid?([ 9, 10, 11, 12 ])
# => false
你会在ActiveRecord验证链中看到这种东西。