ActiveSupport::Concern 用于模块的引用,具体有两个应用:
使用concern的代码
2, 有两个module:A 和 B, A定义了一个方法: method_a, B引用了method_a , 然后 class C 需要应用 B ,该如何做?
如上代码是与错误的,提示如下:
在C中需要 先引入 A,才能引入 B,因为B依赖 A,显然这种方式不是很合适。不应该由调用对象C来关注这些因素。 引入 ActiveSupport::Concern的代码如下:
用test_unit来测试一下吧
- 简化 self.included 方法
- 用于模块之间方法的相互调用
module M
def self.included(base)
base.class_eval do
def self.method_m
puts "method_m calling----------"
end
end
end
end
使用concern的代码
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
2, 有两个module:A 和 B, A定义了一个方法: method_a, B引用了method_a , 然后 class C 需要应用 B ,该如何做?
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
在C中需要 先引入 A,才能引入 B,因为B依赖 A,显然这种方式不是很合适。不应该由调用对象C来关注这些因素。 引入 ActiveSupport::Concern的代码如下:
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
用test_unit来测试一下吧
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