module M def self.included(base) base.class_eval do def self.method_m puts "method_m calling----------" end end end end
require 'active_support/concern' module M extend ActiveSupport::Concern included do class_eval do def self.method_m puts "method_m calling----------" end end end end
module A def self.included(base) base.extend ClassMethods end module ClassMethods def method_a puts "method_a calling -------------" end end end module B include A def self.included(base) base.method_a end end class C include B end
concern2.rb:17:in `included': undefined method `method_a' for C:Class (NoMethodError)原因是 base 是 class c , 而 C 根本没有定义 method_a , 修改 C 的代码
class C include A include B end
require 'active_support/concern' module A extend ActiveSupport::Concern module ClassMethods def method_a puts "method_a calling -------------" end end end module B extend ActiveSupport::Concern include A included do self.method_a end end class C include B end
运行正确!
接着应该分析 concern的源码。。。
module ActiveSupport module Concern def self.extended(base) base.instance_variable_set("@_dependencies", []) end def append_features(base) if base.instance_variable_defined?("@_dependencies") base.instance_variable_get("@_dependencies") << self return false else return false if base < self @_dependencies.each { |dep| base.send(:include, dep) } super base.extend const_get("ClassMethods") if const_defined?("ClassMethods") base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block") end end def included(base = nil, &block) if base.nil? @_included_block = block else super end end end end
require 'test/unit' require 'active_support/concern' class ConcernTest < Test::Unit::TestCase def test_dependencies dependencies = Bar.instance_variable_get("@_dependencies") assert dependencies assert dependencies.is_a?Array assert_equal 1, dependencies.size assert_equal Foo, dependencies.first assert A.respond_to? :method_foo end end module Foo extend ActiveSupport::Concern included do def self.method_foo; puts "method_foo"; true;end end end module Bar extend ::ActiveSupport::Concern include Foo included do self.method_foo end end class A include Bar end