Hi all, 这个问题我难以用一句话来讲清楚,就干脆上代码吧 我的问题是这样的:
# 假设现在有若干个模块 A B C D E ...
# 先给类F include进若干个模块
class F
include A
include B
include C
end
# 此时看ancestors,可以发现,C B A按照include时相反顺序出现在了F的后面
F.ancestors # => [F, C, B, A, Object, PP::ObjectMixin, Kernel, BasicObject]
# 如果之后(在自定义的某个插件中,不能修改原来的代码)希望给F补充一个模块D
class F
include D
end
# 那么此时D模块就会出现在F的后面,C B A的前面,意思是将按照这个顺序搜索方法
F.ancestors # => [F, D, C, B, A, Object, PP::ObjectMixin, Kernel, BasicObject]
# 但是此时我希望给F再补充一个模块E
class F
??? include E ???
end
# 然后希望在输出ancestors的时候得到这样一个结果,即E被include在了B和A之间的位置,该如何实现?
F.ancestors # => [F, D, C, B, E, A, Object, PP::ObjectMixin, Kernel, BasicObject]
也许把 E 这个模块 include 在 B 模块里会是个解决方案
module B
include E
end
但是这样毫无疑问的会影响到所有 include B 这个模块的类,并不是我希望得到的效果。 当然肯定不能修改 class F 的源代码。 所以请教大家,对此有什么好的并且比较简单的解决方案吗?谢谢
#1 楼 @sevk 这个问题是个很理论很元编程的问题。。而且 include 这个关键字用得非常厉害
module ActionController
class Base < Metal
abstract!
def self.without_modules(*modules)
modules = modules.map do |m|
m.is_a?(Symbol) ? ActionController.const_get(m) : m
end
MODULES - modules
end
MODULES = [
AbstractController::Layouts,
AbstractController::Translation,
AbstractController::AssetPaths,
Helpers,
HideActions,
UrlFor,
Redirecting,
Rendering,
Renderers::All,
ConditionalGet,
RackDelegation,
Caching,
MimeResponds,
ImplicitRender,
Cookies,
Flash,
RequestForgeryProtection,
ForceSSL,
Streaming,
DataStreaming,
RecordIdentifier,
HttpAuthentication::Basic::ControllerMethods,
HttpAuthentication::Digest::ControllerMethods,
HttpAuthentication::Token::ControllerMethods,
# Before callbacks should also be executed the earliest as possible, so
# also include them at the bottom.
AbstractController::Callbacks,
# Append rescue at the bottom to wrap as much as possible.
Rescue,
# Add instrumentations hooks at the bottom, to ensure they instrument
# all the methods properly.
Instrumentation,
# Params wrapper should come before instrumentation so they are
# properly showed in logs
ParamsWrapper
]
MODULES.each do |mod|
include mod
end
# Rails 2.x compatibility
include ActionController::Compatibility
ActiveSupport.run_load_hooks(:action_controller, self)
end
end
看看 ActionController::Base 的代码,几乎都是在 include 各种模块,本身根本没有写什么东西。。
#1 楼 @sevk 在看看 ActiveRecord::Base 的模块
include ActiveRecord::Persistence
extend ActiveModel::Naming
extend QueryCache::ClassMethods
extend ActiveSupport::Benchmarkable
extend ActiveSupport::DescendantsTracker
extend Querying
include ReadonlyAttributes
include ModelSchema
extend Translation
include Inheritance
include Scoping
extend DynamicMatchers
include Sanitization
include AttributeAssignment
include ActiveModel::Conversion
include Integration
include Validations
extend CounterCache
include Locking::Optimistic, Locking::Pessimistic
include AttributeMethods
include Callbacks, ActiveModel::Observing, Timestamp
include Associations
include IdentityMap
include ActiveModel::SecurePassword
extend Explain
# AutosaveAssociation needs to be included before Transactions, because we want
# #save_with_autosave_associations to be wrapped inside a transaction.
include AutosaveAssociation, NestedAttributes
include Aggregations, Transactions, Reflection, Serialization, Store
都是在 include 或者 extend,你怎么能说 include 很少用到呢?
我只知道 include 的时候会把那个 module 直接加到 class 的上部,所以产生了那个顺序,不过还不知道能不能去动态地调整顺序。。等待高手:)
module A
def self.included(base)
if base == F
base.send :include, E
end
end
end
按照 ActiveSupport::Concern 的解决方案 应该是都 include Concern 在 module 中解决顺序问题,然后 class 无需关心顺序