2016-06-01 60 views
2

我有一個方法,作爲參數的整數數組,我想使用裝飾模式首先驗證數組中的每個數字是否在指定的範圍內。裝飾在Ruby中的方法

我見過ruby中的裝飾器,它擴展了類或者包含模塊,但是這對我來說似乎有點過時,有沒有一種方法可以在不中繼類或模塊的情況下裝飾方法?

編輯 我有幾個方法需要一個數組作爲參數,並需要驗證項目的範圍。我沒有在每個方法中內聯編碼驗證,而是想要所有這些方法的裝飾器,但是我想知道裝飾器類/模塊是否僅存在於Ruby中?

class MyClass 
..some code here ... 
def method(array) 
    ..some code here... 
end 
end 
+1

您的問題不清楚。 「依靠班級或模塊」是什麼意思?你能編輯你的問題來包含示例代碼嗎? –

+0

我想知道你爲什麼想在這裏使用裝飾模式。爲什麼不只是內聯驗證參數?這樣做大概有三條簡單線條。 –

+0

真的不只是這種方法,我的軟件中還有其他方法可以使用這個裝飾器。 –

回答

4

下面是如何包裝方法與一個校驗器的簡單示例:

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

愛你的答案,非常清晰,整潔優雅 –

+0

謝謝。我實際上是直接在[so]的答案窗口中直接寫下來,然後複製並粘貼到Pry中,並且它在第一次嘗試時就起作用了。這通常是一個乾淨而簡單的設計的好兆頭:-D –

+0

Upvoted,這說我很高興*精緻度從未趕上,真的希望他們永遠不會,而不是我會在我的代碼中使用的東西。是的,我一直在猴子補丁,方面圖書館(如果有人現在還記得)alias_method_chain和所有的東西,但時間和實踐(大多數企業)證明它是baaaad設計,不要這樣做的孩子! – dolzenko

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的驗證鏈中。