2014-11-14 1 views
2

나는 다음과 같은 클래스가 :루비 메타 프로그래밍 Q :에 외부 클래스의 메소드를 호출 after_save

class AwardBase 
class AwardOne < AwardBase 
class Post < ActiveRecord::Base 

포스트는 액티브하고, 수상이 can_award을 가지고 있습니까? 게시물 객체를 취하여 어떤 기준을 충족하는지 확인하는 클래스 메소드입니다. 그렇다면 post.owner.awards를 업데이트합니다.

Observer 패턴을 사용하여이 작업을 수행 할 수 있음을 알았습니다. 그러나, 그 모델에 추가 코드를 추가해야합니다. 가능하다면 모델을 전혀 건드리지 않으려합니다. 내가이 (트리거가 클래스로드시에 호출됩니다) 같은 상 검사를 실행 할 싶습니다

class AwardOne < AwardBase 
    trigger :post, :after_save 

    def self.can_award?(post) 
    ... 
    end 
end 

위의 코드와 의도가 자동으로 AwardOne.can_award를 추가해야한다는 것입니다? 기본적으로

class Post < ActiveRecord::Base 
    after_save AwardOne.can_award?(self) 
    ... 
end 

인 : 포스트의 after_save 방법에

그래서 기본적으로 내가 할 노력하고있어하면 trigger 전화가 동등 얻을 수 있습니다

class Post < ActiveRecord::Base 
    after_save :check_award 

    def check_award 
    AwardOne.can_award?(self) 
    end 
end 

어떻게 Post 클래스를 수정하지 않고이 작업을 수행합니까? 여기


는 (작업 표시되지 않음) 내가 무슨 짓을했는지의 :

NameError (undefined local variable or method `award_callback' for #<Post:0x002b57c04d52e0>): 
+0

이 오류는 스택 추적을 제공하십시오. –

+0

여기에 수락 된 대답과 비슷한 ActiveSupport :: Concerns를 사용할 수 있습니다. http://stackoverflow.com/questions/12084234/how-do-i-use-ruby-metaprogramming-to-add-callbacks-to-a- rails-model? rq = 1 – Anand

+0

하지만 여전히 모델에 포함시켜야합니다 !! 나는 확실하지 않다. – argentum47

답변

0

당신이 award_callback으로 추가되기 때문에 :

class AwardBase 

    def self.trigger (klass, active_record_event) 
    model_class = klass.to_class 

    this = self 
    model_class.instance_eval do 
     def award_callback 
     this.can_award?(self) 
     end 
    end 

    model_class.class_eval do 
     self.send(active_record_event, :award_callback) 
    end 
    end 

    def self.can_award? (model) 
    raise NotImplementedError 
    end 
end 

위의 코드가 오류와 함께 실패 class 방법. 클래스 메소드를 grep하면 등록 될 것입니다.

아래와 같이 코드를 변경하십시오. 그것은 잘 작동합니다.

model_class.class_eval do ## Changed to class_eval 
    def award_callback 
    this.can_award?(self) 
    end 
end 

혼란 스러울 경우 자세한 예를 들어 보겠습니다.

class Test 
end 

Test.instance_eval do 
    def class_fun 
    p "from class method " 
    end 
end 

Test.class_eval do 
    def instance_fun 
    p "from instance method " 
    end 
end 


Test.methods.grep /class_fun/ 
# => [:class_fun] 

Test.instance_methods.grep /instance_fun/ 
# => [:instance_fun] 

Test.class_fun 
# => "from class method " 

Test.new.instance_fun 
# => "from instance method " 
1

왜 이런 식으로할지 생각해야합니다. 나는 그것이 관찰자 패턴을 사용하는 것보다 더 나쁘다고 주장 할 것이다. 당신은 최소한의 놀라움의 원칙을 위반하고 있습니다 (또한 최소한의 경악 원칙).

이 프로젝트가 더 큰 프로젝트이고이 프로젝트의 새 개발자가되었다고 상상해보십시오. 게시물이 올바르게 저장되지 않는 문제를 디버깅하고 있습니다. 당연히 모델의 코드를 먼저 살펴 보겠습니다. 게시물 컨트롤러의 코드를 살펴볼 수도 있습니다. 그렇게하면 게시물 저장에 관련된 두 번째 등급이 있다는 표시가 없습니다. AwardOne의 코드가 포함되어 있는지 전혀 알지 못하기 때문에 문제가 무엇인지 파악하는 것이 훨씬 더 어려울 것입니다. 이 경우 실제로 컨트롤러에서이 작업을 수행하는 것이 가장 바람직합니다. 디버깅과 이해가 가장 쉬운 곳입니다 (모델은 이미 충분한 책임이 있고 일반적으로 더 크기 때문에).

이것은 메타 프로그래밍의 일반적인 문제입니다. 대부분의 경우, 최소한의 놀라움의 원칙 때문에 정확하게 피하는 것이 좋습니다. 디버깅해야 할 몇 가지 문제로 인해이 코드로 돌아 가면 1 년 후에 사용하지 않았기 때문에 기쁠 것입니다. 당신이 한 "영리한"것이 무엇인지를 잊을 것입니다. 당신이 정당한 규정을 지키지 않는 이유가 없다면, 그들은 이유가 있습니다.

그 밖의 것이 없다면 Post 모델에서 무엇인가를 선언하여 우아하게이 작업을 수행하는 방법을 찾아야합니다. 예를 들어, ActiveRecord::Baseawardable 클래스 메소드를 등록하십시오. 그러나 최선의 방법은 컨트롤러 나 서비스 객체를 통해 수행하는 것이 가장 좋습니다. 이 아닌 을 어떻게 처리해야하는지에 대한 책임은 AwardOne입니다!

관련 문제