2011-01-19 3 views
5

모듈을 포함하여 동적으로 생성 된 메서드를 재정의하려고합니다.왜이 모듈을 포함 시키면 동적으로 생성 된 메서드가 무시되지 않습니까?

아래 예에서 Ripple 연결은 rows= 메서드를 표에 추가합니다. 나는이 메소드를 호출하고 싶지만 나중에는 추가로 처리해야한다.

모듈의 row=이 기존 메서드를 사용하기 위해 super을 호출 할 수 있다고 생각하여이 메서드를 재정의하는 모듈을 만들었습니다.

class Table 

    # Ripple association - creates rows= method 
    many :rows, :class_name => Table::Row 

    # Hacky first attempt to use the dynamically-created 
    # method and also do additional stuff - I would actually 
    # move this code elsewhere if it worked 
    module RowNormalizer 
    def rows=(*args) 
     rows = super 
     rows.map!(&:normalize_prior_year) 
    end 
    end 
    include RowNormalizer 

end 

하지만, 내 내가 그 안에 예외가 발생하는 경우, 아무 변화가 없다는 사실에 의해 입증 rows=가 호출되지 않습니다 새.

모듈을 포함 시켰습니다.이 태그를 넣으면 예외가 발생하기 때문입니다.

 included do 
     raise 'I got included, woo!' 
     end 

또한 대신 rows=의 경우, 모듈은 그 메소드가 호출이다 somethingelse= 정의한다.

왜 동적으로 생성 된 모듈 방법이 내 모듈 방법으로 대체되지 않습니까?

답변

10

은의 실험을하자 : 왜 그

class A; def x; 'hi' end end 
module B; def x; super + ' john' end end 
A.class_eval { include B } 

A.new.x 
=> "hi" # oops 

입니까? 대답은 간단하다 : 조상 체인 A가 (당신이A 내부 B로 생각할 수 있습니다) 전에

A.ancestors 
=> [A, B, Object, Kernel, BasicObject] 

B입니다. 따라서 A.x은 항상 B.x보다 우선합니다.

그러나,이 해결할 수 있습니다

http://apidock.com/rails/Module/alias_method_chainalias_method_chain(target, feature)으로이 패턴을 구현 한 ActiveSupport (따라서 레일)로
class A 
    def x 
    'hi' 
    end 
end 

module B 
    # Define a method with a different name 
    def x_after 
    x_before + ' john' 
    end 

    # And set up aliases on the inclusion :) 
    # We can use `alias new_name old_name` 
    def self.included(klass) 
    klass.class_eval { 
     alias :x_before :x 
     alias :x :x_after 
    } 
    end 
end 

A.class_eval { include B } 

A.new.x #=> "hi john" 

: 루비 2 Module#prepend 함께 제공

module B 
    def self.included(base) 
    base.alias_method_chain :x, :feature 
    end 

    def x_with_feature 
    x_without_feature + " John" 
    end 
end 

업데이트 이 방법은 A의 메서드를 재정의하므로 대부분의 사용 사례에 대해 alias 해킹이 필요하지 않습니다.

+0

내가 상향 조정하려했으나, 그 다음에는 모든 사람들이 걸려 넘어졌습니다. :-) –

+0

고마워요! 나는 이것을 알아야했다 : 나는 여기에 상속 체인을 썼다 ... http://stackoverflow.com/questions/3492679/ruby-determining-method-origins :) –

2

왜 동적으로 생성 된 모듈 방식이 내 모듈 방식 대신 재정의되지 않습니까?

상속이 작동하지 않기 때문에. 클래스에 정의 된 메소드는 다른 클래스/모듈에서 상속 된 메소드를 대체합니다. 그것은 서브 클래스 대신 상속 체인의 슈퍼 클래스로 모듈을 삽입 제외

루비 2.0에서는, 단지 Module#include처럼 작동하는 Module#prepend있다.

+0

신난다, 나는 Module # prepend를 보게되어 정말로 기쁩니다. 루비 2에 대해 (최소한 임시로) 계획되었습니다! (http://redmine.ruby-lang.org/issues/1102 참조) –

0

extend 클래스 인스턴스를 사용하면됩니다.

관련 문제