aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/concern.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support/concern.rb')
-rw-r--r--activesupport/lib/active_support/concern.rb140
1 files changed, 105 insertions, 35 deletions
diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb
index 2d87e8d0e5..81fb859334 100644
--- a/activesupport/lib/active_support/concern.rb
+++ b/activesupport/lib/active_support/concern.rb
@@ -1,39 +1,109 @@
-# A typical module looks like this
-#
-# module M
-# def self.included(base)
-# base.send(:extend, ClassMethods)
-# base.send(:include, InstanceMethods)
-# scope :foo, :conditions => { :created_at => nil }
-# end
-#
-# module ClassMethods
-# def cm; puts 'I am a class method'; end
-# end
-#
-# module InstanceMethods
-# def im; puts 'I am an instance method'; end
-# end
-# end
-#
-# By using <tt>ActiveSupport::Concern</tt> the above module could instead be written as:
-#
-# module M
-# extend ActiveSupport::Concern
-#
-# included do
-# scope :foo, :conditions => { :created_at => nil }
-# end
-#
-# module ClassMethods
-# def cm; puts 'I am a class method'; end
-# end
-#
-# module InstanceMethods
-# def im; puts 'I am an instance method'; end
-# end
-# end
module ActiveSupport
+ # A typical module looks like this:
+ #
+ # module M
+ # def self.included(base)
+ # base.extend ClassMethods
+ # base.send(:include, InstanceMethods)
+ # scope :disabled, where(:disabled => true)
+ # end
+ #
+ # module ClassMethods
+ # ...
+ # end
+ #
+ # module InstanceMethods
+ # ...
+ # end
+ # end
+ #
+ # By using <tt>ActiveSupport::Concern</tt> the above module could instead be written as:
+ #
+ # require 'active_support/concern'
+ #
+ # module M
+ # extend ActiveSupport::Concern
+ #
+ # included do
+ # scope :disabled, where(:disabled => true)
+ # end
+ #
+ # module ClassMethods
+ # ...
+ # end
+ #
+ # module InstanceMethods
+ # ...
+ # end
+ # end
+ #
+ # Moreover, it gracefully handles module dependencies. Given a +Foo+ module and a +Bar+
+ # module which depends on the former, we would typically write the following:
+ #
+ # module Foo
+ # def self.included(base)
+ # base.class_eval do
+ # def self.method_injected_by_foo
+ # ...
+ # end
+ # end
+ # end
+ # end
+ #
+ # module Bar
+ # def self.included(base)
+ # base.method_injected_by_foo
+ # end
+ # end
+ #
+ # class Host
+ # include Foo # We need to include this dependency for Bar
+ # include Bar # Bar is the module that Host really needs
+ # end
+ #
+ # But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We could try to hide
+ # these from +Host+ directly including +Foo+ in +Bar+:
+ #
+ # module Bar
+ # include Foo
+ # def self.included(base)
+ # base.method_injected_by_foo
+ # end
+ # end
+ #
+ # class Host
+ # include Bar
+ # end
+ #
+ # Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt> is the +Bar+ module,
+ # not the +Host+ class. With <tt>ActiveSupport::Concern</tt>, module dependencies are properly resolved:
+ #
+ # require 'active_support/concern'
+ #
+ # module Foo
+ # extend ActiveSupport::Concern
+ # included do
+ # class_eval do
+ # def self.method_injected_by_foo
+ # ...
+ # end
+ # end
+ # end
+ # end
+ #
+ # module Bar
+ # extend ActiveSupport::Concern
+ # include Foo
+ #
+ # included do
+ # self.method_injected_by_foo
+ # end
+ # end
+ #
+ # class Host
+ # include Bar # works, Bar takes care now of its dependencies
+ # end
+ #
module Concern
def self.extended(base)
base.instance_variable_set("@_dependencies", [])