retroactive_module_inclusion 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.yardopts CHANGED
@@ -1,3 +1,2 @@
1
- --private
2
1
  lib/*.rb
3
2
  README.rdoc History.rdoc
data/History.rdoc CHANGED
@@ -1,9 +1,20 @@
1
+ === 1.2.0 / 2011-01-23
2
+
3
+ * 2 minor enhancements
4
+
5
+ * Chaged #retroactively_include visibility to public and updated tests accordingly
6
+ * motivated by this discussion {this discussion}[https://github.com/lsegal/yard/issues/issue/242/] with Loren Segal
7
+
8
+ * Better documentation:
9
+ * many README and History fixes and improvements
10
+ * code documentation enhanced with YARD tags
11
+
1
12
  === 1.1.0 / 2011-01-22
2
13
 
3
14
  * 2 minor enhancements
4
15
 
5
16
  * Refactored retroactively_include method from ::Module to
6
- CoreExt::Module::RetroactiveModuleInclusion, after Ola Bini suggested
17
+ CoreExt::Module::RetroactiveModuleInclusion, after what Ola Bini suggested
7
18
  in his post {SAFE(R) MONKEY PATCHING}[http://olabini.com/blog/2011/01/safeer-monkey-patching/]
8
19
 
9
20
  * Finally, managed to make yard document the retroactively_include method:
data/README.rdoc CHANGED
@@ -39,7 +39,7 @@ included Enumerable (e.g., the Range class). Unfortunately, this is not the case
39
39
  (1..2).mean #=> NoMethodError: undefined method `mean' for 1..2:Range
40
40
 
41
41
  Surely this behaviour does not conform to the least surprise principle.
42
- In fact, this inconsistency stems from implementation efficienty concerns
42
+ In fact, this inconsistency stems from efficienty concerns
43
43
  and characterize a limitation in Ruby's object model (see {Dynamic Module Include Problem}[http://eigenclass.org/hiki/The+double+inclusion+problem]).
44
44
 
45
45
  In face of that, one has basically two possible solutions. The first is to give up Stats and define the method directly inside Enumerable
@@ -53,8 +53,19 @@ In face of that, one has basically two possible solutions. The first is to give
53
53
 
54
54
  The second, more concise and elegant, is to use this gem
55
55
 
56
+ Enumerable.retroactively_include Stats
57
+
58
+ Note that the method retroactively_include, which was private in early
59
+ versions, became public in v1.2.0. Nevertheless, one can still call
60
+
56
61
  Enumerable.module_eval { retroactively_include Stats }
57
62
 
63
+ or
64
+
65
+ Enumerable.send :retroactively_include, Stats
66
+
67
+ to the same effect.
68
+
58
69
  == FEATURES:
59
70
 
60
71
  * Tested on all major Ruby interpreters (100% coverage, 0% failure):
@@ -67,9 +78,13 @@ The second, more concise and elegant, is to use this gem
67
78
  == SYNOPSIS:
68
79
 
69
80
  SomeModule.module_eval { retroactively_include AnotherModule }
70
-
81
+
71
82
  or equivalently
72
83
 
84
+ SomeModule.send :retroactively_include, AnotherModule
85
+
86
+ or still
87
+
73
88
  module SomeModule
74
89
  retroactively_include AnotherModule
75
90
  end
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ require 'hoe'
8
8
  Hoe.spec 'retroactive_module_inclusion' do
9
9
  developer('Adriano Mitre', 'adriano.mitre@gmail.com')
10
10
 
11
- self.version = '1.1.0'
11
+ self.version = '1.2.0'
12
12
 
13
13
  self.readme_file = 'README.rdoc'
14
14
  self.history_file = 'History.rdoc'
@@ -2,12 +2,10 @@ module CoreExt
2
2
  module Module
3
3
  module RetroactiveModuleInclusion
4
4
 
5
- private
6
-
7
5
  # Includes +mod+ retroactively, i.e., extending to all classes and modules which
8
6
  # had included +self+ _beforehand_.
9
7
  #
10
- # Example:
8
+ # @example Retroactively include a module in Enumerable.
11
9
  #
12
10
  # module Stats
13
11
  # def mean
@@ -19,17 +17,23 @@ module CoreExt
19
17
  #
20
18
  # (1..2).mean #=> 1.5
21
19
  #
22
- def retroactively_include(mod) # :doc:
20
+ # @return self
21
+ #
22
+ def retroactively_include(mod)
23
23
  raise TypeError, "wrong argument type #{mod.class} (expected Module)" unless mod.is_a? ::Module # ::Module would in general be equivalent to Object::Module and simply Module would mean CoreExt::Module in this context
24
-
25
- # Although one would expect +A.module_eval("include B")+ to make methods
26
- # from module +B+ available to all classes and modules that had previously
27
- # included module +A+, this is not the case due to a limitation in Ruby's
28
- # object model (see [dynamic module include problem][1]). Thus, one has two
29
- # possible solutions:
30
- # * use Module#include_retroactively instead of Module#include
31
- # * reopen +A+ and define the methods directly inside it
32
- #
24
+
25
+ pseudo_descendants.each do |pd|
26
+ pd.module_eval { include mod }
27
+ end
28
+
29
+ self
30
+ end
31
+
32
+ private
33
+
34
+ # @return [Array] All modules and classes which have self in its ancestors tree, including self itself.
35
+ #
36
+ def pseudo_descendants
33
37
  # JRuby (at least up to version 1.5.6) has ObjectSpace disabled by default,
34
38
  # thus it must be enabled manually ([reference][2]).
35
39
  #
@@ -42,14 +46,16 @@ module CoreExt
42
46
  prev_jruby_objectspace_state = JRuby.objectspace
43
47
  JRuby.objectspace = true
44
48
  end
49
+ result = []
45
50
  ObjectSpace.each_object(::Module) do |m|
46
51
  if m <= self # equiv. to "if m.include?(self) || m == self"
47
- m.module_eval { include mod }
52
+ result << m
48
53
  end
49
54
  end
50
55
  if defined?(RUBY_DESCRIPTION) && RUBY_DESCRIPTION =~ /jruby/i
51
56
  JRuby.objectspace = prev_jruby_objectspace_state
52
57
  end
58
+ result
53
59
  end
54
60
 
55
61
  ::Module.class_eval { include CoreExt::Module::RetroactiveModuleInclusion }
@@ -1,10 +1,14 @@
1
1
  require "test/unit"
2
2
  require File.expand_path("../../lib/retroactive_module_inclusion", __FILE__)
3
3
 
4
- module Stats
5
- def mean
6
- inject(&:+) / count.to_f
7
- end
4
+ [1,2].each do |n|
5
+ eval <<-EOS
6
+ module Stats#{n}
7
+ def mean#{n}
8
+ inject(&:+) / count.to_f
9
+ end
10
+ end
11
+ EOS
8
12
  end
9
13
 
10
14
  class TestRetroactiveInclude < Test::Unit::TestCase
@@ -18,13 +22,33 @@ class TestRetroactiveInclude < Test::Unit::TestCase
18
22
  type_ok(Numeric, Math)
19
23
  end
20
24
 
21
- def test_retroactively_include
22
- assert_raise(NoMethodError) { (1..2).mean }
23
- Enumerable.module_eval { include Stats }
24
- assert_raise(NoMethodError, 'include should not work retroactively ') { (1..2).mean }
25
- Enumerable.module_eval { retroactively_include Stats }
26
- assert_nothing_raised('retroactively_include should do the job') { (1..2).mean }
27
- assert_equal 1.5, (1..2).mean
25
+ def test_retroactively_include_private
26
+ assert_raise(NoMethodError) { (1..2).mean1 }
27
+ Enumerable.module_eval { include Stats1 }
28
+ assert_raise(NoMethodError, 'include should not work retroactively ') { (1..2).mean1 }
29
+ Enumerable.module_eval { retroactively_include Stats1 }
30
+ assert_nothing_raised('retroactively_include should do the job') { (1..2).mean1 }
31
+ assert_equal 1.5, (1..2).mean1
32
+ end
33
+
34
+ def test_retroactively_include_public
35
+ assert_raise(NoMethodError) { (1..2).mean2 }
36
+ Enumerable.module_eval { include Stats2 }
37
+ assert_raise(NoMethodError, 'include should not work retroactively ') { (1..2).mean2 }
38
+ Enumerable.module_eval { retroactively_include Stats2 }
39
+ assert_nothing_raised('retroactively_include should do the job') { (1..2).mean2 }
40
+ assert_equal 1.5, (1..2).mean2
41
+ end
42
+
43
+ def test_jruby_object_space_prev_state
44
+ if defined?(RUBY_DESCRIPTION) && RUBY_DESCRIPTION =~ /jruby/i
45
+ require 'jruby'
46
+ [true, false].each do |prev_state|
47
+ JRuby.objectspace = prev_state
48
+ Enumerable.module_eval { include Stats1 }
49
+ assert_equal prev_state, JRuby.objectspace
50
+ end
51
+ end
28
52
  end
29
53
 
30
54
  private
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: retroactive_module_inclusion
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
5
- prerelease:
4
+ prerelease: false
6
5
  segments:
7
6
  - 1
8
- - 1
7
+ - 2
9
8
  - 0
10
- version: 1.1.0
9
+ version: 1.2.0
11
10
  platform: ruby
12
11
  authors:
13
12
  - Adriano Mitre
@@ -36,7 +35,7 @@ cert_chain:
36
35
  9u8N6mQNneIVRh6Xfdko/Q==
37
36
  -----END CERTIFICATE-----
38
37
 
39
- date: 2011-01-22 00:00:00 -02:00
38
+ date: 2011-01-23 00:00:00 -02:00
40
39
  default_executable:
41
40
  dependencies:
42
41
  - !ruby/object:Gem::Dependency
@@ -47,7 +46,6 @@ dependencies:
47
46
  requirements:
48
47
  - - ">="
49
48
  - !ruby/object:Gem::Version
50
- hash: 47
51
49
  segments:
52
50
  - 2
53
51
  - 8
@@ -98,7 +96,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
98
96
  requirements:
99
97
  - - ">="
100
98
  - !ruby/object:Gem::Version
101
- hash: 3
102
99
  segments:
103
100
  - 0
104
101
  version: "0"
@@ -107,14 +104,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
104
  requirements:
108
105
  - - ">="
109
106
  - !ruby/object:Gem::Version
110
- hash: 3
111
107
  segments:
112
108
  - 0
113
109
  version: "0"
114
110
  requirements: []
115
111
 
116
112
  rubyforge_project: retroactive_module_inclusion
117
- rubygems_version: 1.4.1
113
+ rubygems_version: 1.3.7
118
114
  signing_key:
119
115
  specification_version: 3
120
116
  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
metadata.gz.sig CHANGED
Binary file