[英]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
或類的末尾。 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.