2012-05-08 6 views
2

일반적인 메서드를 모듈이나 클래스로 옮겨서 다른 모듈 아래에있는 네임 스페이스 인 새 클래스에 포함 시키거나 상속 시키려고합니다. 동일한 모듈 아래에 두 개의 클래스 네임 스페이스가있는 경우 동일한 네임 스페이스 아래에있는 한 모듈 이름을 포함하지 않고이를 호출 할 수 있습니다. 하지만 내 네임 스페이스 범위 변경과 다른 모듈에서 포함 된 메서드가 있고 왜 또는 어떻게 피하기 위해 모릅니다.모듈 스코프가 올바르지 않습니다.

예를 들면. 이 코드는 작동 반환 '바'

module Foo 
    class Bar 
    def test_it 
     Helper.new.foo 
    end 
    end 
end 

module Foo 
    class Helper 
    def foo 
     'bar' 
    end 
    end 
end 

Foo::Bar.new.test_it 

하지만 난 모듈에 방법 test_it을 이동하는 경우, 그것은 더 이상 작동 나던 : 나가서 설명하자면 NameError : 일정하게 유지 mixin 초기화되지 않은 :: :: 도우미.

module Mixins; end 

module Mixins::A 
    def self.included(base) 
    base.class_eval do 
     def test_it 
     Helper.new.foo 
     end 
    end 
    end 
end 

module Foo 
    class Bar 
    include Mixins::A 
    end 
end 

module Foo 
    class Helper 
    def foo 
     'bar' 
    end 
    end 
end 

Foo::Bar.new.test_it 

또한 class_eval이 block 대신 evaling 문자열 인 경우 scope는 Foo 대신 Foo :: Bar가됩니다.

module Mixins; end 

module Mixins::A 
    def self.included(base) 
    base.class_eval %q{ 
     def test_it 
     Helper.new.foo 
     end 
    } 
    end 
end 

module Foo 
    class Bar 
    include Mixins::A 
    end 
end 

module Foo 
    class Helper 
    def foo 
     'bar' 
    end 
    end 
end 

Foo::Bar.new.test_it 

누구든지 아이디어가 있습니까?

편집 :

module Mixins; end 

module Mixins::A 
    def self.included(base) 
    base.class_eval do 
     def test_it 
     _nesting::Helper 
     end 
     def _nesting 
     @_nesting ||= self.class.name.split('::')[0..-2].join('::').constantize 
     end 
    end 
    end 
end 

module Foo 
    class Helper 
    end 

    class Bar 
    include Mixins::A 
    end 
end 

module Foo2 
    class Helper 
    end 

    class Bar 
    include Mixins::A 
    end 
end 

Foo::Bar.new.test_it #=> returns Foo::Helper 
Foo2::Bar.new.test_it #=> returns Foo2::Helper 
+0

솔루션을 얻을 수있어서 다행입니다! –

답변

1

이 문제를 이해하려면 루비에서 상수 조회가 작동하는 방식을 이해해야합니다. 메서드 조회와 같지 않습니다. 이 코드에서 :

module Mixins::A 
    def self.included(base) 
    base.class_eval do 
     def test_it 
     Helper.new.foo 
     end 
    end 
    end 
end 

Helper 중 하나에 "도우미"라는 상수를 의미 A, Mixins 또는 최상위 수준에 정의 하지 "도우미"라는 상수 (즉 Object인치) Bar에 정의되어 있습니다. 단지 당신 class_eval이 코드는 Bar 클래스로 변경되었으므로 변경되지 않습니다. "어휘 바인딩"과 "동적 바인딩"의 차이점을 알고 있다면 Ruby의 상수 해상도가 어휘 바인딩을 사용한다고 말할 수 있습니다. 동적 바인딩을 사용할 것으로 예상됩니다.

당신이 base.class_eval에 전달하는 블록의 included 후크가 호출 될 때마다하면 바이트 코드 로 컴파일하고,이어서 것을 기억하십시오 (Helper에 대한 참조 포함) 같은 미리 컴파일 된 블록은 다른 클래스와 실행 (base)를 self으로한다. 인터프리터는 base.class_eval을 실행할 때마다 블록을 새로 고치고 컴파일하지 않습니다.당신이class_eval문자열을 전달하면 다른 한편으로

는, 해당 문자열을 구문 분석하고 때마다 새롭게included 후크 실행을 컴파일됩니다. 중요 : 문자열에서 eval을 가져온 코드는 null 어휘 환경에서 평가됩니다. 즉, 주변 메소드의 로컬 변수는 코드에서 사용 가능한 이 아니며이 아님을 의미합니다. 더 중요한 점은 주변 범위가 eval 코드에서 상수 조회에 영향을 미치지 않는다는 것입니다.

상수 참조를 동적으로 확인하려면 명시 적 상수 참조가 작동하지 않습니다. 그것은 단순히 언어가 작동하기위한 방법이 아닙니다 (그리고 정당한 이유가 있습니다). 그것에 대해 생각해보십시오 : 상수 참조가 동적으로 해결 된 경우 self의 클래스에 따라 Array 또는 Hash과 같은 참조가 런타임에 해결되는 방법을 예측할 수 없습니다. 당신이 모듈이 같은 코드 ...

hash = Hash[array.map { |x| ... }] 

을 가지고 ... 그리고 모듈이 중첩 Hash 클래스와 클래스에 혼합 된 경우, Hash.[]중첩 클래스을 참조보다는 Hash에서 것이다 표준 라이브러리! 명확하게, 상수 참조를 동적으로 결정하는 것은 이름 충돌 및 관련 버그에 너무 많은 잠재력을 가지고 있습니다.

이제 메소드 조회를 사용하면 다른 점이 있습니다. OOP의 전체 개념 (적어도 OOP의 루비 풍미)은 메소드 호출 (즉, 메시지)이 수신기의 클래스에 의존한다는 것입니다.

수신기의 클래스에 따라 상수를 동적으로 찾고 싶다면 self.class.const_get을 사용하면됩니다. 같은 효과를 내기 위해서는 문자열이 eval보다 깔끔한 것 같습니다.

+0

감사. 그것은 많은 것을 설명합니다. 여전히 블록 대신 문자열로 class_eval을 수행하면 왜 중첩이 변경되는지 혼란 스럽습니다. Class_eval이라는 문자열과 [Mixins :: A]라는 문자열을 가진 [Mixins :: A, Foo :: Bar]를 얻습니다. –

+0

좋은 정보 주셔서 감사합니다. 범위 지정은 의미가 있습니다. 단지 그것에 대해 생각하고 주위를 둘러 봐야했습니다. –

+0

@ luriG,이 일로 돌아가려면 꽤 오랜 시간이 걸렸습니다. 추가 된 정보가 블록과 문자열의 평가가 다른 결과를 얻을 수있는 이유를 이해하는 데 도움이되기를 바랍니다. –

0
module Mixins::A 
    def self.included(base) 
    base.class_eval do 
     def test_it 
     Foo::Helper.new.foo 
: 마법사와 알렉스에

덕분에, 나는 아름답 지하지만 일을이 코드 (그것이 레일 도우미 constantize을 사용주의)으로 돌아가 셨습니다


편집 :

차를 점점 후 nce가 코드를 가지고 놀아보기에 조금 더 문제가 있습니다. 난 당신이 시도했던 정확히 무엇을 할 수있을 거라고 생각하지 않는다, 그러나 이것은 가까운 : 도우미 클래스에 의한 방법으로 상수에 푸 :: 바 아래에 네임 스페이스 할 필요가 있다고

module Mixins::A 
    def self.included(base) 
    base.class_eval do 
     def test_it 
     self.class.const_get(:Helper).new.foo 
     end 
    end 
    end 
end 

module Foo 
    class Bar 
    include Mixins::A 
    end 
end 

module Foo 
    class Bar::Helper 
    def foo 
     'bar' 
    end 
    end 
end 

참고 해결하세요 루비에서.

+0

코드가 복잡하지 않은 경우 문제가 너무 복잡해 보일 정도로 사례를 단순화했습니다. 당신의 대답은 내 문제를 해결하지 못합니다. 만약 내가 당신의 mixin을 모듈 "Foo2"에 포함 시킨다면, Foo :: Helper 대신에 Foo :: Helper를 호출 할 것이고, 이것이 최상위 네임 스페이스를 명시 적으로 지정하고 동적으로 해결하고 싶지 않은 이유입니다. 나는 –

+0

를 본다. 'Mixens :: A'를 포함하기 전에 모듈에서 Helper를 정의 해보십시오. –

+0

방금 ​​시도했습니다. 같은 문제. 여전히 Mixins :: A :: 도우미가 정의되지 않았습니다 ... –

관련 문제