extended_include 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+