retroactive_module_inclusion 1.0.0

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