extended_include 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.yardopts ADDED
@@ -0,0 +1,4 @@
1
+ --tag copyright:Copyright
2
+ --tag license:License
3
+ -
4
+ HISTORY.txt
data/HISTORY.txt ADDED
@@ -0,0 +1,3 @@
1
+ 2014-04-17 Version 0.0.1
2
+
3
+ Initial version.
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "extended_include"
3
+ s.version = "0.0.1"
4
+ s.date = "2014-04-17"
5
+ s.authors = ["Brian Katzung"]
6
+ s.email = ["briank@kappacs.com"]
7
+ s.homepage = "http://rubygems.org/gems/extended_include"
8
+ s.summary = "Include both class and instance methods on module include"
9
+ s.description = "This module assists with some of the finer details in the extend-on-included idiom for importing class methods in addition to instance methods when including a module."
10
+ s.license = "Public Domain"
11
+
12
+ s.files = Dir.glob("lib/**/*") +
13
+ %w{extended_include.gemspec .yardopts HISTORY.txt}
14
+ s.test_files = Dir.glob("test/**/*.rb")
15
+ s.require_path = 'lib'
16
+ end
@@ -0,0 +1,116 @@
1
+ # Extended_Include - Deals with some of the finer details of the
2
+ # extend-on-included idiom for adding both class and instance methods
3
+ # on module import.
4
+ #
5
+ # Based on these posts:
6
+ # * http://stackoverflow.com/questions/15905270/can-you-extend-self-included
7
+ # * http://www.kappacs.com/2014/ruby-sub-classes-inheritance-include-extend/
8
+ # and hundreds of other posts about the ::included extend ClassMethods hack.
9
+ #
10
+ # The Extended_Include module is a back-end support module. See the Module
11
+ # module extensions for the user interface.
12
+ #
13
+ # Version 0.0.1, 2014-04-17
14
+ #
15
+ # @author Brian Katzung (briank@kappacs.com), Kappa Computer Solutions, LLC
16
+ # @license Public Domain
17
+
18
+ module Extended_Include
19
+
20
+ VERSION = "0.0.1"
21
+
22
+ # The extended_include list, by module
23
+ @include_list = {}
24
+
25
+ # The class methods list, by module
26
+ @class_methods = {}
27
+
28
+ # Include additional modules.
29
+ def self.add_includes (base, *modules)
30
+ (@include_list[base] ||= []).concat modules
31
+ base.class_exec do
32
+ # Note that we reverse here to counter ::include's
33
+ # last-to-first behavior in order to achieve first-to-last
34
+ # behavior.
35
+ include *modules.reverse
36
+ extend Extended_Include
37
+ end
38
+ end
39
+
40
+ # Return a module's class method list.
41
+ def self.class_methods_for (base)
42
+ (@class_methods[base] ||= []).uniq!
43
+ @class_methods[base].reverse
44
+ end
45
+
46
+ # Include a module's class methods when included.
47
+ def self.include_class_methods (base, *modules, &block)
48
+ (@class_methods[base] ||= []).concat modules
49
+ @class_methods[base].push Module.new(&block) if block
50
+ base.class_exec { extend Extended_Include }
51
+ end
52
+
53
+ # Return a module's extended_include list.
54
+ def self.includes_for (base)
55
+ (@include_list[base] ||= []).uniq!
56
+ @include_list[base]
57
+ end
58
+
59
+ # The #included method extended to other modules' ::included method.
60
+ def included (base)
61
+ Extended_Include.includes_for(self).each do |mod|
62
+ mod.included base if mod.respond_to?(:included) &&
63
+ (!base.respond_to?(:superclass) ||
64
+ !base.superclass.include?(mod))
65
+ end
66
+
67
+ # Note that we reverse here to counter ::extend's
68
+ # last-to-first behavior in order to achieve first-to-last
69
+ # behavior.
70
+ sources = Extended_Include.class_methods_for self
71
+ base.class_exec { extend *sources.reverse } unless sources.empty?
72
+
73
+ super base rescue nil
74
+ end
75
+
76
+ end
77
+
78
+ # Extend class Module to support additional "include" functionality.
79
+ class Module
80
+
81
+ # Include additional modules.
82
+ #
83
+ # Unlike a traditional #include, the modules' ::included methods
84
+ # (if present) will be called when the current module is
85
+ # included if they have not already been previously included by
86
+ # the including object's ancestors.
87
+ #
88
+ # Another difference is that multiple modules are always included
89
+ # first-to-last, so it doesn't matter if you
90
+ # "extended_include M1, M2, M3" or
91
+ # "extended_include M1; extended_include M2; extended_include M3"
92
+ # or any other variant with the same reference order. Methods will
93
+ # always be sought in last-to-first order (M3, M2, M1).
94
+ def extended_include (*modules)
95
+ Extended_Include.add_includes self, *modules
96
+ end
97
+
98
+ # Extend class methods into the including object when including this
99
+ # module.
100
+ #
101
+ # include_class_methods # from sub-module ClassMethods, if present
102
+ # include_class_methods M1, M2 # from specified sub-modules
103
+ # include_class_methods do # defined in a block
104
+ # def some_class_method; end
105
+ # end
106
+ #
107
+ # As usual, sub-modules must be defined before reference.
108
+ def include_class_methods (*modules, &block)
109
+ if !block && modules.empty? && const_defined?(:ClassMethods)
110
+ Extended_Include.include_class_methods self, self::ClassMethods,
111
+ &block
112
+ else Extended_Include.include_class_methods self, *modules, &block
113
+ end
114
+ end
115
+
116
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: extended_include
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Brian Katzung
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2014-04-17 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: This module assists with some of the finer details in the extend-on-included idiom for importing class methods in addition to instance methods when including a module.
22
+ email:
23
+ - briank@kappacs.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - lib/extended_include.rb
32
+ - extended_include.gemspec
33
+ - .yardopts
34
+ - HISTORY.txt
35
+ has_rdoc: true
36
+ homepage: http://rubygems.org/gems/extended_include
37
+ licenses:
38
+ - Public Domain
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ segments:
50
+ - 0
51
+ version: "0"
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.3.7
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: Include both class and instance methods on module include
67
+ test_files: []
68
+