2012-09-25 7 views
9

Mixin으로 사용할 모듈에서 인스턴스 변수를 초기화하는 방법이 있습니까? -이 약간 자극적입니다 내가 각 @member이 제대로 각 기능에서 초기화 된 경우 또 다시 확인해야Mixins에서 인스턴스 변수 초기화

module Example 

    def on(...) 
    @handlers ||= {} 
    # do something with @handlers 
    end 

    def all(...) 
    @all_handlers ||= [] 
    # do something with @all_handlers 
    end 

    def unhandled(...) 
    @unhandled ||= [] 
    # do something with unhandled 
    end 

    def do_something(..) 
    @handlers  ||= {} 
    @unhandled ||= [] 
    @all_handlers ||= [] 

    # potentially do something with any of the 3 above 
    end 

end 

주의 사항 : 예를 들어, 나는 다음 있습니다. 나는 많이 쓰고 싶다 :

module Example 

    def initialize 
    @handlers  = {} 
    @unhandled = [] 
    @all_handlers = [] 
    end 

    # or 
    @handlers = {} 
    @unhandled = [] 
    # ... 
end 

그리고 반복해서 물건을 올바르게 초기화 할 필요가 없다. 그러나, 내가 말할 수있는 것으로부터 이것은 불가능합니다. 이 문제를 해결하기 위해 initialize_me 메서드를 Example에 추가하고 확장 클래스에서 initialize_me을 호출하는 것 외에 어떤 방법이 있습니까? 나는 this example을 보았다. 그러나 이것을 수행하기 위해 Class에 내가 원숭이 패치를하는 방법은 없다.

답변

12
module Example 
    def self.included(base) 
    base.instance_variable_set :@example_ivar, :foo 
    end 
end 

편집 :이 클래스의 인스턴스 변수를 설정하고 있음을 유의하십시오. 해당 인스턴스가 아직 작성되지 않았으므로 클래스에 모듈을 혼합 할 때 인스턴스의 인스턴스 변수를 작성할 수 없습니다. 당신은,하지만,

module Example 
    def self.included(base) 
    base.class_exec do 
     def initialize 
     @example_ivar = :foo 
     end 
    end 
    end 
end 

가 포함 클래스의 initialize 메소드 호출하는 동안이 작업을 수행 할 수있는 방법이있을 수 있습니다 :의 믹스 인에 예컨대을 초기화 방법을 만들 수 있습니다 (누구를?). 확실하지 않다.

class Foo 
    include Example 

    def initialize 
    @foo = :bar 
    after_initialize 
    end 
end 

module Example 
    def after_initialize 
    @example_ivar = :foo 
    end 
end 
+0

우수 감사합니다. 나는 그것에 대해 백만 건의 기사가 있지만 어디서나 언급 된 접근법을 왜 보지 못했는지 궁금합니다. –

+0

불행하게도 이것은 작동하지 않는 것처럼 보입니다 -'@ example_ivar'를 참조하면'nil'으로 돌아옵니다. –

+0

아, 고맙습니다. - 클래스가 이미'initialize' 메소드를 지정했다면, 이것은 문제를 일으킬 것입니다, 그렇죠? –

2

modulesModule#included으로, 후크를 제공하지만, 여기에 대안입니다. 주제에 대한 루비 문서를 확인하거나 모듈에 대한 도움말을 제공하는 ActiveSupport::Concern을 사용하는 것이 좋습니다.

+0

저는 ActiveSupport를 사용하지 않고 올바른 방향으로 나를 가르쳐 주셔서 감사합니다. –

+0

당신을 진심으로 환영합니다. – ksol

1

는 아마도 이것은 조금 해키,하지만 당신은 원하는 동작 얻을 prepend를 사용할 수 있습니다

2.1.1 :011 > A.new 
=> #<A:0x00000101131788 @instance_var=[]> 
+0

include 대신 prepend를 사용하는 데 단점이 있습니까? –

+0

내가 생각할 수있는 한 가지 단점은 모듈이 계층 구조의 클래스 아래에 삽입된다는 것입니다. 보통, 우리의 타입 계층 구조는'A Max

+0

모든 권리 :) 글쎄 "prepend"나는 그것에 대해 아주 자명하다. 그래서 나는 당신의 솔루션을 좋아한다. (+1) –

0

내가있을 수 있습니다 생각 : 여기

module Foo 
    def initialize(*args) 
    @instance_var = [] 
    super 
    end 
end 

class A 
    prepend Foo 
end 

콘솔에서 출력을 이것에 대한 간단한 대답. 모듈에는 평상시대로 변수를 초기화하는 초기화 프로그램이 있어야합니다. 모듈을 포함하는 클래스의 초기화 프로그램에서 super()를 호출하여 포함 된 모듈에서 이니셜 라이저를 호출합니다. 이것은 단순히 Ruby의 메소드 디스패치 규칙을 따른 것입니다.

리플렉션에서는 모듈을 포함하는 클래스에도 초기화해야하는 수퍼 클래스가있는 경우 잘 작동하지 않습니다. 모듈의 초기화 프로그램은 가변 매개 변수 목록을 받아 들여이를 수퍼 클래스에 전달해야합니다. 그것은 좋은 길을 탐색하는 것처럼 보입니다.