2013-08-21 37 views
0

我試圖創建某種模塊或超類,它包裝子類的每個方法後的一個方法調用。 雖然有一些約束:我不希望在調用initialize()之後運行該方法,也不希望在調用其他幾個方法之後運行該方法。 另一個約束是,如果標誌@check_ec設置爲true,我只希望執行該方法。 我有超過60個方法的類,我硬編碼了遍佈整個地方的同一段代碼。 有沒有一種方法可以使我的類方法自動執行該方法的包裝?Ruby - 在類中的大多數方法後執行相同的代碼

這樣的想法是這樣的:

class Abstract 
    def initialize(check_ec) 
    @check_ec = check_ec 
    end 
    def after(result) # this is the method that I'd like to be added to most methods 
    puts "ERROR CODE: #{result[EC]}" 
    end 
    def methods(method) # below each method it would execute after 
    result = method() # execute the given method normally 
    after(result) if @check_ec and method != :initialize and method != :has_valid_params 
    end 
end 

class MyClass < Abstract 
    def initialize(name, some_stuff, check_error_code) 
    # do some stuff... 
    @name = name 
    super(check_error_code) 
    end 
    def my_method_a() # execute after() after this method 
    return {EC: 0} 
    end 
    def my_method_b() # execute after() after this method 
    return {EC: 7} 
    end 
    def has_valid_params() # don't execute after() on this method 
    return true 
    end 

end 

回答

1

這是十分容易使用method_missing,和組合物的,而不是繼承。你可以建立一個非常簡單的類轉發方法調用,然後執行一個after回調,除了具體的方法名稱:

class Abstract 
    def initialize(object) 
    @object = object 
    end 

    def method_missing(method, *arguments) 
    result = @object.send(method, *arguments) 

    after() unless method == "has_valid_params" 

    result 
    end 

    def after 
    # whatever 
    end 
end 

o = Abstract.new(MyClass.new) 
+0

哥倫布的蛋!簡單而美麗。 :) – jaeheung

+0

這太棒了!以優雅的方式解決我的問題! – Unglued

0

這個怎麼樣?它有一個主要缺點,那就是在調用check_error_code之前必須先定義你的方法,但它可能適合你的需要。您可以在Rails回調中尋找更好的解決方案的靈感,或者延遲每種方法的重新定義,直到使用method_added掛鉤添加該方法。

包括ErrorCodeChecker並在每個要檢查錯誤代碼的類中調用check_error_code(如代碼片段的最後一行所示)。

module ErrorCodeChecker 
    def self.included(base) 
    base.send(:extend, ClassMethods) 
    end 

    def after(result) # this is the method that I'd like to be added to most methods 
    puts "ERROR CODE: #{result[:ec]}" 
    end 

    module ClassMethods 
    def check_error_code(options = {}) 
     check_on = instance_methods(false) - Array(options[:except]) 
     check_on &= Array(options[:only]) if options[:only] 
     class_eval do 
     check_on.each do |method| 
      alias_method "#{ method }_without_ec", method 
      define_method(method) do |*args, &block| 
      send("#{ method }_without_ec", *args, &block).tap { |result| after(result) if @check_ec } 

      #if you want to actually return the return value of calling after: 
      #result = send("#{ method }_without_ec") 
      #@check_ec ? after(result) : result 
      end 
     end 
     end 
    end 
    end 
end 

class Abstract 
    include ErrorCodeChecker 

    def initialize(check_ec) 
    @check_ec = check_ec 
    end 
end 

class MyClass < Abstract 

    def initialize(name, some_stuff, check_error_code) 
    # do some stuff... 
    @name = name 
    super(check_error_code) 
    end 
    def my_method_a # execute after() after this method 
    {ec: 0} 
    end 
    def my_method_b # execute after() after this method 
    {ec: 7} 
    end 
    def has_valid_params # don't execute after() on this method 
    true 
    end 

    check_error_code except: :has_valid_params 
    #or whitelisting: 
    #check_error_code only: [:my_method_a, :my_method_b] 
    #or both: 
    #check_error_code only: :my_method_a, except: [:has_valid_params, dont_check_this_one] 
end 
0

使用單例類的解決方案。

class MyClass 
    def initialize(name, some_stuff) 
    # do some stuff... 
    @name = name 
    end 
    def my_method_a # execute after() after this method 
    return {EC: 0} 
    end 
    def my_method_b() # execute after() after this method 
    return {EC: 7} 
    end 
    def has_valid_params() # don't execute after() on this method 
    return true 
    end 
end 

module ErrorCodeChecker 
    def after(result) # this is the method that I'd like to be added to most methods 
    puts "ERROR CODE: #{result[:EC]}" 
    end 

    def addErrorCodeCheck(exclude = []) 
    methods = self.class.superclass.public_instance_methods(false) - exclude 
    class << self 
     self 
    end.class_exec { 
     methods.each {|method| 
     define_method(method) {|*p| 
      super(*p).tap {|res| after(res)} 
     } 
     } 
    } 
    end 
end 

class MyClassEC < MyClass 
    include ErrorCodeChecker 

    def initialize(name, some_stuff, check_error_code, exclude = []) 
    super name, some_stuff 
    addErrorCodeCheck(exclude) if check_error_code 
    end 
end 

「addErrorCodeCheck」開闢了單獨的類MyClassEC的實例,並重新定義不排除列表中的MyClass的實例方法。重新定義的方法隱藏原始方法,但在調用'after'之前通過'super'方法調用它們。

如果需要,您可以稍後重複應用'addErrorCodeCheck'。

執行例如:(在紅寶石1.9.3測試)

my = MyClassEC.new('test', 'abc', true, [:has_valid_params]) 

my.my_method_a # => ERROR CODE: 0 
my.my_method_b # => ERROR CODE: 7 
my.has_valid_params # => (nothing) 
相關問題