2014-04-25 1 views
1

여기에 제가 가진 작은 문제가 있습니다. 이것은 단순화 된 예입니다. 의 내가 여러 인스턴스 메소드와 클래스가 있다고 가정하자 나는 ActiveSupport::Concern 사용하여 인스턴스 방법 중 하나를 기록하려면 :ActiveSupport :: Concern and alias_method_chain

module LogStartEngine 
    extend ActiveSupport::Concern 

    included do 
    alias_method_chain :start_engine, :logging 
    end 

    def start_engine_with_logging 
    Rails.logger.info("Starting engine!") 

    start_engine_without_logging 

    Rails.logger.info("Engine started!") 
    end 
end 

:

여기
class Car 
    include LogStartEngine 

    def start_engine 
    # useful thing 
    end 

    def check_oil 
    # useful thing 
    end 

    def open_doors 
    # useful thing 
    end  
end 

내가 먼저 우려까지 온 것입니다 그러나 이것은 LogStartEngine이 포함될 때부터

NameError: undefined method `start_engine' for class `Car' 
    from /Users/david/.gem/ruby/1.9.3/gems/activesupport-4.0.3/lib/active_support/core_ext/module/aliasing.rb:32:in `alias_method' 

이 이해할으로 이어질 것입니다, 클래스,에는 start_engine이라는 메서드가 없습니다.

start_engine 뒤에 include LogStartEngine을 넣을 수는 있지만이 진술을 그대로 유지하고 싶습니다.

그래서 제약은 다음과 같습니다

  • 로그 유일한 방법 start_engine, 모든 방법.
  • Car 단지 LogStartEngine이 필요합니다. 난 관심이 추가 된 사용자 정의 도우미 메서드를 호출하는 것을 피하기 위해, 같은 것 log_method :start_engine.
  • include LogStartEngine 진술을 그대로 유지하고 싶습니다. 나는 그것을 방법 start_engine 밑이나 수업 마지막에 있기를 원하지 않습니다.
  • 이것은 Ruby 1.9를 사용하고 있습니다. 그것을 달성하기 위해 다른 방법이 :

    module LogStartEngine 
        extend ActiveSupport::Concern 
    
        module ClassMethods 
        def method_added(_) 
         unless instance_methods.include?(:start_engine_without_logging) 
         alias_method_chain :start_engine, :logging 
         end 
        end 
        end 
    
        def start_engine_with_logging 
        Rails.logger.info("Starting engine!") 
    
        start_engine_without_logging 
    
        Rails.logger.info("Engine started!") 
        end 
    end 
    

    내 하위 질문은 : 그래서 Module#prepend 내 솔루션은 여기에, 일부 시험 후 유효한 솔루션 :

답변

1

이전 질문이지만 답을 찾았고 누군가에게 글을 남겼습니다.

module LogStartEngine 
    extend ActiveSupport::Concern 

    define_method :start_engine_with_logging do 
    Rails.logger.info("Starting engine!") 
    start_engine_without_logging 
    Rails.logger.info("Engine started!") 
    end 

    included do 
    alias_method_chain :start_engine, :logging 
    end 
end 

define_method이 방법의 포인트입니다, 그것이 내가 제약에서 말했듯이

+1

그것은 훌륭하고 분명한 해결책입니다. 좋은 분! – David

0

위임을 사용하거나 http://www.ruby-doc.org/stdlib-2.0/libdoc/forwardable/rdoc/Forwardable.html을 사용하는 것이 좋습니다. 그런 다음 객체를 구성하고 결합 할 수 있습니다. 또한 method_missing 또는 이와 비슷한 것을 "자동"로거를 제공 할 수 있다고 가정합니다.

class Car 
    def start_engine 
    # wrooom 
    end 
end 

class CarWithLogging 
    attr_reader :car 

    def initialize(car) 
    @car = car 
    end 

    def start_engine 
    Rails.logger.info "starting engine" 
    car.start_engine 
    Rails.logger.info "engine started" 
    end 
end 

car = CarWithLogging.new(Car.new) 
car.start_engine 

업데이트는 : 다른 방법으로는 사용할 수 루비의 prepend (만 사용할 수 2.0 이후), 그래서 실제로 :: 우려는 필요하지 않습니다.

class Car 
    prepend CarLogging 
    def start_engine; end 
end 

module CarLogging 
    def start_engine 
    puts "before" 
    super 
    puts "after" 
    end 
end 
+0

(alias_method_chain 전) 포함, 루비 1.9 사용하고 있습니다, 그래서'prepend'이 옵션을 선택하지 않습니다에 동적으로 메소드를 정의하지만, '전달할 수있는'은 재미있는 것이다. – David

+0

아, 내 나쁜 :) prepend는 좋았을 것입니다 - 그때 그것은 alias_method_chain 또는'Forwardable'/Proxy 중 하나라고 생각합니다 ... – lwe

관련 문제