2014-11-14 2 views
2

ActiveModel의 직렬화, 특히 as_jsonserializable_hash의 꼬인 웹을 통해 많은 재미를 느낍니다.클래스/모듈을 포함하지 않는 경우에만 모듈에서 메서드를 정의하도록 허용합니다.

내 앱에는 모듈을 포함하여 동작을 공유하는 모델 모음이 많이 있으며, SharedBehavior이라고합니다.

팀에서는 JSON (레일즈 앱에서 렌더링하기 위해)으로 캐스팅 할 때 모든 클래스가 따라야하는 기본 형식을 결정했지만 일부는 약간 다르게 동작해야합니다. ActiveModel 라이브러리의이 두 메서드에서 이상한 동작으로 인해 모델 자체에 화이트리스트 또는 블랙리스트에 추가 된 속성을 추가하는 작업은이 모듈의 메서드 정의에 의해 무시되고 ActiveModel의 슈퍼 선언에 전달됩니다.

이러한 이유로이 모듈은 모델에서 명시 적으로 재정의되지 않은 경우에만 모델에 정의를 적용합니다 (본질적으로 모듈을 몇 가지 메소드 호출에 대해 조상 체인에서 가져 오십시오),하지만이 모듈에서 여전히 공유 된 동작이 필요합니다.

I 동적 IRB에서 모듈 개재물의 방법을 적용 조건부 이것을 해결 시도 : 제가

in C 
in A 

C.new.foo의 출력을 기대하지만 그것이

class A 
    def foo 
    puts 'in A' 
    end 
end 

module D 
    def self.included(base) 
    unless base.instance_methods(false).include?(:foo) 
     define_method(:foo) do 
     puts 'in D' 
     super() 
     end 
    end 
    end 
end 

class B < A 
    include D 
end 

class C < A 
    include D 
    def foo 
    puts 'in C' 
    super 
    end 
end 

을이 선언 대신

in C 
in D 
in A 

유일한 다른 생각은 이동하는 것입니다.

  1. 그것은 암시을 조금 소개 : 다른 모듈에 출력이 논리 및 명시 적으로이 메소드를 오버라이드 (override)하지 않는 (약 54 그들이있다),하지만 몇 가지 단점이 그에게가 모든 클래스에서 해당 모듈을 포함 새 모델에이 모듈이 포함되어있는 경우이 메서드를 재정의하지 않으려면이 모듈을 포함하십시오.
  2. 모듈에서 이러한 serialization 메서드의 현재 구현은 해당 모듈에서 설정 한 동작 및 특성과 관련이 있으므로 두 번째 모듈은 첫 번째 모듈과 거의 관련이 없지만 SharedBehavior의 구현 세부 사항을 알고 그에 따라 달라지는 두 번째 모듈을 갖는 것은 직관이 아닙니다.

included 후크에서 전화를 걸 수있는 위의 코드 예제에서 다른 사람이 다른 해결책을 생각해 보거나 어쩌면 나를 감독 할 수 있습니까? (또한 클래스가 foo 메서드를 정의하고 D 모듈을 포함하지만 정확히 동일한 동작을 보인 순서 전환 시도).

답변

3

두 가지 까다로운 버그가 있습니다.

  1. 루비 클래스를 평가하므로 표현 순서가 중요합니다. included 후크가 호출 될 때 include DCfoo을 정의, 그래서 전에 foobase에 정의되지 않습니다. 반의 include D이 필요합니다.
  2. fooD에 정의하고 있습니다. 그래서 DB에 넣으면 D#foo이 정의되어 이전 버그를 수정하더라도 여전히C에 포함됩니다.의 수신자가 되려면 base이 필요합니다.

그러나 두 번째 버그를 수정하면 첫 번째 버그가 무의미 해집니다. foobase에 직접 정의하면 이후 정의에서 덮어 씁니다. 그것은 당신은이 대단한

# in D.included 
base.class_eval do 
    define_method(:foo) do 
    puts 'in D' 
    super() 
    end 
end 
+0

필요, 그래서 요약

class C < A def foo puts 'in D' super() end # overwrites previous definition! def foo puts 'in C' super end end 

을하는 것과 같다. 사실 3 분 전에는 이와 비슷한 무언가에 실제로 도착했습니다. 단, 제 솔루션을 제외하고는 private 메소드'define_method'를'base'에 호출했습니다. 나는 아마도 * _eval 메소드를 제거하려고 노력했다. 아마 내 부분에 대한 지식 부족 때문일 것이다. 나는 그들이 매우 불안하고 보안 위험을 초래할 수 있다고 들었습니다. 이와 관련하여 귀하의 모범이 다른가요? –

+1

'eval' 메서드는 임의의 코드를 포함 할 수있는 문자열을 전달하는 경우에만 안전하지 않습니다. 이 경우에는 완벽하게 괜찮습니다. 암묵적인 수신기를'D'에서'base'로 변경하는 데 사용합니다. – Max

+0

달콤한. IRB에서 확인되었습니다. 이것은 매우 계몽적인 운동이었습니다. 감사! –