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 +4 -0
- data/HISTORY.txt +3 -0
- data/extended_include.gemspec +16 -0
- data/lib/extended_include.rb +116 -0
- metadata +68 -0
data/.yardopts
ADDED
data/HISTORY.txt
ADDED
@@ -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
|
+
|