2013-08-21 1 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을 호출하기 전에 정의 되어야만한다는 큰 결점을 가지고 있지만, 그것은 여러분의 요구에 맞을 수도 있습니다. 레일스 콜백에서 더 나은 솔루션을 얻으려면 영감을 얻거나 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) 
관련 문제