簡體   English   中英

ActiveSupport ::關注和alias_method_chain

[英]ActiveSupport::Concern and alias_method_chain

這是我的一個小問題。 請注意,這是一個簡化的示例。 假設我有一個包含多個實例方法的類,並且想使用ActiveSupport::Concern記錄其中一個實例方法:

class Car
  include LogStartEngine

  def start_engine
    # useful thing
  end

  def check_oil
    # useful thing
  end

  def open_doors
    # useful thing
  end      
end

這是我首先想到的問題:

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

但這將導致

  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'

這是可以理解的,因為當包含LogStartEngine時, Car類沒有名為start_engine任何方法。

我知道我可以解決這個投入include LogStartEngine方法之后start_engine但我想保持這種說法在哪里。

因此約束是:

  • 僅記錄方法start_engine ,而不記錄所有方法。
  • Car只需要包括LogStartEngine關注。 我想避免調用由顧慮添加的任何自定義幫助程序方法,例如log_method :start_engine
  • 我想將include LogStartEngine語句保留在include LogStartEngine處。 我不希望它位於方法start_engine或類的末尾。
  • 這是使用Ruby 1.9。 所以Module#prepend不是有效的解決方案:)

經過一些試驗,這是我的解決方案:

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 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是此方法的重點,它在包含時動態定義方法( alias_method_chain之前)

一種替代方法是使用委托或使用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

更新 :作為替代,您可以使用Ruby的prepend (僅自2.0起可用),因此實際上不需要AS :: Concern。

class Car
  prepend CarLogging
  def start_engine; end
end

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM