retroactive_module_inclusion 1.0.0

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/.autotest ADDED
@@ -0,0 +1,23 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.extra_files << "../some/external/dependency.rb"
7
+ #
8
+ # at.libs << ":../some/external"
9
+ #
10
+ # at.add_exception 'vendor'
11
+ #
12
+ # at.add_mapping(/dependency.rb/) do |f, _|
13
+ # at.files_matching(/test_.*rb$/)
14
+ # end
15
+ #
16
+ # %w(TestA TestB).each do |klass|
17
+ # at.extra_class_map[klass] = "test/test_misc.rb"
18
+ # end
19
+ # end
20
+
21
+ # Autotest.add_hook :run_command do |at|
22
+ # system "rake build"
23
+ # end
data/History.rdoc ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2011-01-19
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,7 @@
1
+ .autotest
2
+ History.rdoc
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/retroactive_module_inclusion.rb
7
+ test/test_retroactive_module_inclusion.rb
data/README.rdoc ADDED
@@ -0,0 +1,118 @@
1
+ = retroactive_module_inclusion
2
+
3
+ * http://github.com/adrianomitre/retroactive_module_inclusion
4
+
5
+ == DESCRIPTION:
6
+
7
+ This gem circumvents the "dynamic module include" (aka "double inclusion")
8
+ problem, which is the fact that
9
+ M.module_eval { include N }
10
+ does not make the methods of module N available to modules and classes which had
11
+ included module M beforehand, only to the ones that include it thereafter. This
12
+ behaviour hurts the least surprise principle, specially because if K is a
13
+ class, then
14
+ K.class_eval { include M }
15
+ _does_ make all methods of M available to all classes which had previously
16
+ inherited it. This inconsistency stems from implementation efficienty concerns
17
+ and characterize a limitation in Ruby's object model (see {Dynamic Module Include Problem}[http://eigenclass.org/hiki/The+double+inclusion+problem]).
18
+
19
+ == AN EXAMPLE OF THE "DYNAMIC MODULE INCLUDE" PROBLEM
20
+
21
+ Let's begin by defining a one-method module:
22
+
23
+ module Stats
24
+ def mean
25
+ n = 1
26
+ inject {|s,k| n += 1 ; s + k }.to_f / count
27
+ end
28
+ end
29
+
30
+ Including it in Array makes Stats#mean available to all arrays:
31
+
32
+ Array.class_eval { include Stats }
33
+ [1, 2].mean #=> 1.5
34
+
35
+ Therefore one could reasonably expect that if we include Stats into Enumerable,
36
+ Stats#mean would be available to all classes and modules who had previously
37
+ included Enumerable (e.g., the Range class). Unfortunately, this is not the case:
38
+
39
+ Enumerable.module_eval { include Stats }
40
+ (1..2).mean #=> NoMethodError: undefined method `mean' for 1..2:Range
41
+
42
+ Surely this behaviour does not conform to the least surprise principle.
43
+ In fact, this inconsistency stems from implementation efficienty concerns
44
+ and characterize a limitation in Ruby's object model (see {Dynamic Module Include Problem}[http://eigenclass.org/hiki/The+double+inclusion+problem]).
45
+
46
+ In face of that, one has basically two possible solutions. The first is to give up Stats and define the method directly inside Enumerable
47
+
48
+ module Enumerable
49
+ def mean
50
+ n = 1
51
+ inject {|s,k| n += 1 ; s + k }.to_f / count
52
+ end
53
+ end
54
+
55
+ The second, more concise and elegant, is to use this gem
56
+
57
+ Enumerable.module_eval { retroactively_include Stats }
58
+
59
+ == FEATURES/PROBLEMS:
60
+
61
+ * Tested on all major Ruby interpreters (100% coverage, 0% failures):
62
+ * ruby-1.9.2-p136
63
+ * ruby-1.8.7-p330
64
+ * ree-1.8.7-2010.02
65
+ * jruby-1.5.6
66
+ * rbx-1.2.0-20101221
67
+
68
+ == SYNOPSIS:
69
+
70
+ SomeModule.module_eval { retroactively_include AnotherModule }
71
+
72
+ or equivalently
73
+
74
+ module SomeModule
75
+ retroactively_include AnotherModule
76
+ end
77
+
78
+ == REQUIREMENTS:
79
+
80
+ * None: this gem does not depend on any other gem.
81
+
82
+ == INSTALL:
83
+
84
+ * sudo gem install retroactive_module_inclusion
85
+
86
+ == DEVELOPERS:
87
+
88
+ After checking out the source, run:
89
+
90
+ $ rake newb
91
+
92
+ This task will install any missing dependencies, run the tests/specs,
93
+ and generate the RDoc.
94
+
95
+ == LICENSE:
96
+
97
+ (The MIT License)
98
+
99
+ Copyright (c) 2011 Adriano Mitre
100
+
101
+ Permission is hereby granted, free of charge, to any person obtaining
102
+ a copy of this software and associated documentation files (the
103
+ 'Software'), to deal in the Software without restriction, including
104
+ without limitation the rights to use, copy, modify, merge, publish,
105
+ distribute, sublicense, and/or sell copies of the Software, and to
106
+ permit persons to whom the Software is furnished to do so, subject to
107
+ the following conditions:
108
+
109
+ The above copyright notice and this permission notice shall be
110
+ included in all copies or substantial portions of the Software.
111
+
112
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
113
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
114
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
115
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
116
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
117
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
118
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ # Hoe.plugin :compiler
7
+ # Hoe.plugin :cucumberfeatures
8
+ # Hoe.plugin :gem_prelude_sucks
9
+ # Hoe.plugin :inline
10
+ # Hoe.plugin :manifest
11
+ # Hoe.plugin :newgem
12
+ # Hoe.plugin :racc
13
+ # Hoe.plugin :rubyforge
14
+ # Hoe.plugin :website
15
+
16
+ Hoe.spec 'retroactive_module_inclusion' do
17
+ # HEY! If you fill these out in ~/.hoe_template/Rakefile.erb then
18
+ # you'll never have to touch them again!
19
+ # (delete this comment too, of course)
20
+
21
+ developer('Adriano Mitre', 'adriano.mitre@gmail.com')
22
+
23
+ self.version = '1.0.0'
24
+
25
+ self.readme_file = 'README.rdoc'
26
+ self.history_file = 'History.rdoc'
27
+ self.extra_rdoc_files += ['README.rdoc', 'History.rdoc']
28
+ self.extra_rdoc_files << ['Wishlist.rdoc'] if File.exist? 'Wishlist.rdoc'
29
+
30
+ # self.rubyforge_name = 'retroactive_module_inclusionx' # if different than 'retroactive_module_inclusion'
31
+ end
32
+
33
+ # vim: syntax=ruby
34
+
35
+ task :tests => [:test] do
36
+ # aliasing :test with :tests for RVM ('rvm tests')
37
+ end
38
+
@@ -0,0 +1,38 @@
1
+ class Module
2
+
3
+ private
4
+
5
+ def retroactively_include(mod)
6
+ raise TypeError, 'wrong argument type #{mod.class} (expected Module)' unless mod.is_a? Module
7
+
8
+ # Although one would expect +A.module_eval("include B")+ to make methods
9
+ # from module +B+ available to all classes and modules that had previously
10
+ # included module +A+, this is not the case due to a limitation in Ruby's
11
+ # object model (see [dynamic module include problem][1]). Thus, one has two
12
+ # possible solutions:
13
+ # * use Module#include_retroactively instead of Module#include
14
+ # * reopen +A+ and define the methods directly inside it
15
+ #
16
+ # JRuby (at least up to version 1.5.6) has ObjectSpace disabled by default,
17
+ # thus it must be enabled manually ([reference][2]).
18
+ #
19
+ # [1]: http://eigenclass.org/hiki/The+double+inclusion+problem "Dynamic Module Include Problem"
20
+ # [2]: http://ola-bini.blogspot.com/2007/07/objectspace-to-have-or-not-to-have.html "ObjectSpace: to have or not to have"
21
+ #
22
+ prev_jruby_objectspace_state = nil # only for scope reasons
23
+ if defined?(RUBY_DESCRIPTION) && RUBY_DESCRIPTION =~ /jruby/i
24
+ require 'jruby'
25
+ prev_jruby_objectspace_state = JRuby.objectspace
26
+ JRuby.objectspace = true
27
+ end
28
+ ObjectSpace.each_object(Module) do |m|
29
+ if m <= self # equiv. to "if m.include?(self) || m == self"
30
+ m.module_eval { include mod }
31
+ end
32
+ end
33
+ if defined?(RUBY_DESCRIPTION) && RUBY_DESCRIPTION =~ /jruby/i
34
+ JRuby.objectspace = prev_jruby_objectspace_state
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,46 @@
1
+ require "test/unit"
2
+ require File.expand_path("../../lib/retroactive_module_inclusion", __FILE__)
3
+
4
+ module Stats
5
+ def mean
6
+ n = 1
7
+ inject {|s,k| n += 1 ; s + k }.to_f / count
8
+ end
9
+ end
10
+
11
+ class TestRetroactiveInclude < Test::Unit::TestCase
12
+
13
+ def test_argument_type
14
+ type_error(Enumerable, Array)
15
+ type_error(Comparable, Range)
16
+ type_error(Math, Numeric)
17
+ type_ok(Array, Enumerable)
18
+ type_ok(Range, Comparable)
19
+ type_ok(Numeric, Math)
20
+ end
21
+
22
+ def test_retroactively_include
23
+ assert_raise(NoMethodError) { (1..2).mean }
24
+ Enumerable.module_eval { include Stats }
25
+ assert_raise(NoMethodError, 'include should not work retroactively ') { (1..2).mean }
26
+ Enumerable.module_eval { retroactively_include Stats }
27
+ assert_nothing_raised('retroactively_include should do the job') { (1..2).mean }
28
+ assert_equal 1.5, (1..2).mean
29
+ end
30
+
31
+ private
32
+
33
+ def type_error(a, b)
34
+ assert_raise(TypeError, 'shold raise TypeError for non Module inclusion') do
35
+ a.module_eval { retroactively_include b }
36
+ end
37
+ end
38
+
39
+ def type_ok(a, b)
40
+ assert_nothing_raised('shold not raise Exception for Module inclusion') do
41
+ a.module_eval { retroactively_include b }
42
+ end
43
+ end
44
+
45
+ end
46
+
data.tar.gz.sig ADDED
Binary file
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: retroactive_module_inclusion
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Adriano Mitre
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain:
16
+ - |
17
+ -----BEGIN CERTIFICATE-----
18
+ MIIDPDCCAiSgAwIBAgIBADANBgkqhkiG9w0BAQUFADBEMRYwFAYDVQQDDA1hZHJp
19
+ YW5vLm1pdHJlMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
20
+ FgNjb20wHhcNMTEwMTE3MDYxMzM3WhcNMTIwMTE3MDYxMzM3WjBEMRYwFAYDVQQD
21
+ DA1hZHJpYW5vLm1pdHJlMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJ
22
+ k/IsZAEZFgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0jjDX
23
+ O0FdShLgTHub1H7nzZEM/6V26DwJwAn9J8SsLP2tPMO3m0whJF1/cCsy7kofevpo
24
+ uoJVhLC3W8FxQ30Kue9CpMTBGLeHkGlB9hqIRSuHUPtG49nqCSbtew3/uZb7NTYk
25
+ i4guvx/AKwVIJkvgzoxp+BVdmslylZW8kf6xvM6TI+lI3b1oISJRnZaJ5wSG1e62
26
+ A0jJ6rCWowdBhfV2Lc3GvqGWKpFkZiujRWEjf3DuyRJALUjRFt3og5S1LFTC/E8k
27
+ 22l7fNhFtQQWIQ0bvWgr1CVjaC+Mj7Yd/dH1YjFjNSGjeVC70uvwcpa+GAHUTvW+
28
+ 3MJwcABAEx3/k1dVAgMBAAGjOTA3MAkGA1UdEwQCMAAwHQYDVR0OBBYEFCe5ttae
29
+ SfcWo9+mP1ZzFzPfo2N1MAsGA1UdDwQEAwIEsDANBgkqhkiG9w0BAQUFAAOCAQEA
30
+ NWDEWS5ZRu2tDbMmAHsNqjxQmdbICp0GiUa4LT5ihdRpaObHp9r+DbcHXmshYiTg
31
+ rJmflLzpoUvuNYkvgMOXtu7zzLlrm7RsY2r0Z/BbHkxSdX0dpg6s0PcROUnUCdbZ
32
+ CLiXywPX9ZUvOcF35Cz/HZkWo1zSBz5WtUjSodH9L0ASMboQIIWM8aNd80aV+qYC
33
+ 3UBBz1sni91YXPxf7FQxJtIXlNO04DAAHy9J7M22zWpUFRZOJynnDZWsOx3lifKX
34
+ 1PyL4lke+kXB8DF3QA2RqMPmEZp/FgDqYZy1VT1ROlxyatgDgcFBmrsyjnb9hi9N
35
+ 9u8N6mQNneIVRh6Xfdko/Q==
36
+ -----END CERTIFICATE-----
37
+
38
+ date: 2011-01-20 00:00:00 -02:00
39
+ default_executable:
40
+ dependencies:
41
+ - !ruby/object:Gem::Dependency
42
+ name: hoe
43
+ prerelease: false
44
+ requirement: &id001 !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ segments:
50
+ - 2
51
+ - 8
52
+ - 0
53
+ version: 2.8.0
54
+ type: :development
55
+ version_requirements: *id001
56
+ description: |-
57
+ This gem circumvents the "dynamic module include" (aka "double inclusion")
58
+ problem, which is the fact that
59
+ M.module_eval { include N }
60
+ does not make the methods of module N available to modules and classes which had
61
+ included module M beforehand, only to the ones that include it thereafter. This
62
+ behaviour hurts the least surprise principle, specially because if K is a
63
+ class, then
64
+ K.class_eval { include M }
65
+ _does_ make all methods of M available to all classes which had previously
66
+ inherited it. This inconsistency stems from implementation efficienty concerns
67
+ and characterize a limitation in Ruby's object model (see {Dynamic Module Include Problem}[http://eigenclass.org/hiki/The+double+inclusion+problem]).
68
+ email:
69
+ - adriano.mitre@gmail.com
70
+ executables: []
71
+
72
+ extensions: []
73
+
74
+ extra_rdoc_files:
75
+ - Manifest.txt
76
+ - README.rdoc
77
+ - History.rdoc
78
+ files:
79
+ - .autotest
80
+ - History.rdoc
81
+ - Manifest.txt
82
+ - README.rdoc
83
+ - Rakefile
84
+ - lib/retroactive_module_inclusion.rb
85
+ - test/test_retroactive_module_inclusion.rb
86
+ has_rdoc: true
87
+ homepage: http://github.com/adrianomitre/retroactive_module_inclusion
88
+ licenses: []
89
+
90
+ post_install_message:
91
+ rdoc_options:
92
+ - --main
93
+ - README.rdoc
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ requirements: []
113
+
114
+ rubyforge_project: retroactive_module_inclusion
115
+ rubygems_version: 1.3.7
116
+ signing_key:
117
+ specification_version: 3
118
+ summary: This gem circumvents the "dynamic module include" (aka "double inclusion") problem, which is the fact that M.module_eval { include N } does not make the methods of module N available to modules and classes which had included module M beforehand, only to the ones that include it thereafter
119
+ test_files:
120
+ - test/test_retroactive_module_inclusion.rb
metadata.gz.sig ADDED
@@ -0,0 +1,2 @@
1
+ ��k?�1w-
2
+ 68S�K��;�c��ȫ��rC"yn�Šnu��v�~׿����F�[|{"3���k��:�[��R�Ic�!Vf7���?�و�������̅Mq� �/悢vId��c2��u���5O���r�0V� �Ĕq�-Yf�d�o‡����`�Yp����?�-�5�F����~�/� #��