I have method that takes as a parameter an array of integer numbers, and I would like to use the decorator pattern to validate first if each of the numbers in the array is in a specified range.
I've seen decorator in ruby that extend class or include modules, but this seems a little excesive to me, is there a way to just decorate a method without relaying on a class or module ?
EDIT I have several methods that takes an array as parameter, and need to validate the range of the items. Instead of inline coding the validation in each of this method, I want a decorator for all of this methods, but I was wondering if decorator classes/modules are the only ones that exist in Ruby ?
class MyClass
..some code here ...
def method(array)
..some code here...
end
end
Here's a simple example of how to wrap methods with a validator:
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
The Module#validate method takes a list of method names, and a block with the validation logic for those methods.
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)
As you can see, the validator rejected the argument list in the last two calls because they didn't pass the condition in the block.
This is the "magic" which makes it all happen, which isn't actually that magic. We generate a module which overrides the methods we want to validate with a version that raises an exception if the validation fails and then simply calls super. Then we prepend that module to the class/module that we are currently in, i.e. the one that the call to the validate method is in. And that's it, basically.
I chose to also be a good Ruby citizen and wrap the whole thing in a Refinement, so you need to say
using Validator
to actually activate it.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With