summaryrefslogtreecommitdiff
path: root/lib/chef/mixin/lazy_module_include.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef/mixin/lazy_module_include.rb')
-rw-r--r--lib/chef/mixin/lazy_module_include.rb77
1 files changed, 77 insertions, 0 deletions
diff --git a/lib/chef/mixin/lazy_module_include.rb b/lib/chef/mixin/lazy_module_include.rb
new file mode 100644
index 0000000000..34e1fce4f1
--- /dev/null
+++ b/lib/chef/mixin/lazy_module_include.rb
@@ -0,0 +1,77 @@
+#
+# Copyright:: Copyright 2011-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ module Mixin
+ # If you have:
+ #
+ # module A
+ # extend LazyModuleInclude
+ # end
+ #
+ # module B
+ # include A
+ # end
+ #
+ # module C
+ # include B
+ # end
+ #
+ # module Monkeypatches
+ # def monkey
+ # puts "monkey!"
+ # end
+ # end
+ #
+ # A.send(:include, Monkeypatches)
+ #
+ # Then B and C and any classes that they're included in will also get the #monkey method patched into them.
+ #
+ module LazyModuleInclude
+
+ # Most of the magick is in this hook which creates a closure over the parent class and then builds an
+ # "infector" module which infects all descendants and which is responsible for updating the list of
+ # descendants in the parent class.
+ def included(klass)
+ super
+ parent_klass = self
+ infector = Module.new do
+ define_method(:included) do |subklass|
+ super(subklass)
+ subklass.extend(infector)
+ parent_klass.descendants.push(subklass)
+ end
+ end
+ klass.extend(infector)
+ parent_klass.descendants.push(klass)
+ end
+
+ def descendants
+ @descendants ||= []
+ end
+
+ def include(*classes)
+ super
+ classes.each do |klass|
+ descendants.each do |descendant|
+ descendant.send(:include, klass)
+ end
+ end
+ end
+ end
+ end
+end