rconditions 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README +26 -31
  2. data/lib/rconditions.rb +30 -27
  3. data/test/rconditions_test.rb +47 -17
  4. metadata +2 -2
data/README CHANGED
@@ -7,21 +7,7 @@ will throw an exception. The exception will include auto-generated modules
7
7
  that give information which predicate methods passed or failed in the bang
8
8
  method's execution.
9
9
 
10
- == A Simple Example
11
-
12
- require "rconditions"
13
-
14
- begin
15
- [1,2].empty!
16
- rescue Array::NotEmptyError => e
17
- p e
18
- end
19
-
20
- # => #<Exception: Array::NotEmptyError>
21
-
22
- The raised exception includes a module defined on the class where the method is defined. The module's name is derived from the predicate method's name.
23
-
24
- == A More Complicated Example
10
+ == Example
25
11
 
26
12
  require "rconditions"
27
13
 
@@ -71,32 +57,33 @@ The raised exception includes a module defined on the class where the method is
71
57
  # => #<Exception: User::NotAdminError, Posting::SpamError,
72
58
  # Posting::NotVisibleError, User::NotCanViewPostingError>
73
59
 
74
- As you can see, the exception includes modules for each of the evaluated
75
- predicate methods: <tt>User#can_view_posting!(posting)</tt> in the context
76
- above called <tt>User#can_view_posting?(posting)</tt>, which called
77
- <tt>User#admin?</tt> and <tt>Posting#can_view_posting?</tt>, which called
60
+ As you can see, the raised exception includes modules for each of the
61
+ evaluated predicate methods: <tt>User#can_view_posting!(posting)</tt> in the
62
+ context above called <tt>User#can_view_posting?(posting)</tt>, which called
63
+ <tt>User#admin?</tt> and <tt>Posting#visible?</tt>, which called
78
64
  <tt>Posting#spam?</tt>.
79
65
 
80
66
  The results were
81
67
 
82
- * <tt>User#admin? # => false</tt>
83
- * <tt>Posting#spam? # => true</tt>
84
- * <tt>Posting#can_view_posting? # => false</tt>
85
- * <tt>User#can_view_posting? # => false</tt>
68
+ * <tt>User#admin? # => false</tt>
69
+ * <tt>Posting#spam? # => true</tt>
70
+ * <tt>Posting#visible? # => false</tt>
71
+ * <tt>User#can_view_posting? # => false</tt>
86
72
 
87
- so the included exception modules are
73
+ The names of the included exception modules are derived from the names of the
74
+ predicate methods:
88
75
 
89
76
  * <tt>User::NotAdminError</tt>
90
77
  * <tt>Posting::SpamError</tt>
91
- * <tt>Posting::NotCanViewPostingError</tt>
78
+ * <tt>Posting::NotVisibleError</tt>
92
79
  * <tt>User::NotCanViewPostingError</tt>
93
80
 
94
81
  == Performance
95
82
 
96
83
  As almost all predicate methods are extended by default, RConditions carries
97
84
  a performance penalty which varies with the number of calls to predicate
98
- methods. A simple test setup using mongrel/rails was slowed down by 10-20%
99
- by adding <tt>require "rconditions"</tt> to the bottom of its
85
+ methods. A simple test setup using mongrel/rails was slowed down by more than
86
+ 30% by adding <tt>require "rconditions"</tt> to the bottom of its
100
87
  <tt>enviroment.rb</tt>.
101
88
 
102
89
  The performance penalty can be avoided by applying RConditions only to new
@@ -104,12 +91,20 @@ methods, that is, methods defined after <tt>require "rconditions"</tt>. To
104
91
  achieve this, set <tt>RCONDITIONS_ONLY_FOR_NEW_METHODS = true</tt> before
105
92
  the require call. Our test setup had no measurable slowdown afterwards.
106
93
 
94
+ == Supported Ruby Versions
95
+
96
+ The test suite of RConditions passes for
97
+ * ruby 1.8.6
98
+ * ruby 1.9.0 (tested with revision 13680)
99
+
100
+ If you use another ruby version, please check whether the tests pass.
101
+
107
102
  == Limitations
108
103
 
109
- * RConditions currently only support predicate methods starting with a letter
110
- and containing only word characters except the question mark at the end.
111
- * RConditions does not support singleton methods. These includes all class
112
- methods.
104
+ * RConditions only processes predicate methods starting with a letter and
105
+ containing only word characters except the question mark at the end.
106
+ * <tt>Kernel#block_given?</tt> is not processed.
107
+ * Singleton methods are not processed. These includes all class methods.
113
108
 
114
109
  == License
115
110
 
data/lib/rconditions.rb CHANGED
@@ -12,6 +12,7 @@ module RConditions
12
12
  def extend_predicate_method_and_define_bang_method(mod, id)
13
13
  return if Thread.current[:rconditions_processing_predicate_method]
14
14
  return if (predicate_method = id.to_s) !~ PREDICATE_METHOD
15
+ return if mod == Kernel && predicate_method == 'block_given?'
15
16
 
16
17
  begin
17
18
  Thread.current[:rconditions_processing_predicate_method] = true
@@ -29,18 +30,24 @@ module RConditions
29
30
  # requiring RConditions.
30
31
  def extend_predicate_methods_and_define_bang_methods
31
32
  ObjectSpace.each_object(Module) do |mod|
32
- mod.instance_methods(false).each do |id|
33
+ (mod.instance_methods(false) + mod.private_instance_methods(false)).each do |id|
33
34
  RConditions.extend_predicate_method_and_define_bang_method(mod, id)
34
35
  end
35
36
  end
36
37
  end
37
-
38
- def exception_modules #:nodoc:
39
- Thread.current[:rconditions_exception_modules]
38
+
39
+ def collect_exception_modules #:nodoc:
40
+ mods = Thread.current[:rconditions_exception_modules] = []
41
+ yield
42
+ mods
43
+ ensure
44
+ Thread.current[:rconditions_exception_modules] = nil
40
45
  end
41
46
 
42
- def exception_modules=(value) #:nodoc:
43
- Thread.current[:rconditions_exception_modules] = value
47
+ def add_exception_module(mod) #:nodoc:
48
+ if mods = Thread.current[:rconditions_exception_modules]
49
+ mods << mod
50
+ end
44
51
  end
45
52
 
46
53
  private
@@ -57,7 +64,6 @@ module RConditions
57
64
 
58
65
  time = Time.now
59
66
  predicate_method_without_rconditions = "#{predicate_method_without_questionmark}_rconditions_#{mod.object_id}_#{time.to_i}_#{time.usec}"
60
- original_visibility = visibility(mod, predicate_method)
61
67
 
62
68
  mod.class_eval <<-EOS, __FILE__, __LINE__ + 1
63
69
  alias_method :#{predicate_method_without_rconditions}, :#{predicate_method}
@@ -65,10 +71,10 @@ module RConditions
65
71
 
66
72
  def #{predicate_method}(*args, &block)
67
73
  result = #{predicate_method_without_rconditions}(*args, &block)
68
- RConditions.exception_modules << (result ? #{positive_module} : #{negative_module}) if RConditions.exception_modules
74
+ ::RConditions.add_exception_module(result ? #{positive_module} : #{negative_module})
69
75
  result
70
76
  end
71
- #{original_visibility} :#{predicate_method}
77
+ #{visibility(mod, predicate_method)} :#{predicate_method}
72
78
  EOS
73
79
  end
74
80
 
@@ -76,21 +82,15 @@ module RConditions
76
82
  bang_method = predicate_method[0...-1] + '!'
77
83
  return if mod.method_defined?(bang_method) || mod.private_method_defined?(bang_method)
78
84
 
79
- visibility = visibility(mod, predicate_method)
80
-
81
85
  mod.class_eval <<-EOS, __FILE__, __LINE__ + 1
82
86
  def #{bang_method}(*args, &block)
83
- RConditions.exception_modules = []
84
- result = #{predicate_method}(*args, &block)
85
- return result if result
86
-
87
- e = Exception.new(RConditions.exception_modules.map { |m| m.name }.join(', '))
88
- RConditions.exception_modules.each { |m| e.extend(m) }
89
- raise e
90
- ensure
91
- RConditions.exception_modules = nil
87
+ mods = ::RConditions.collect_exception_modules do
88
+ result = #{predicate_method}(*args, &block)
89
+ return result if result
90
+ end
91
+ raise Exception.new(mods.join(', ')).extend(*mods)
92
92
  end
93
- #{visibility} :#{bang_method}
93
+ #{visibility(mod, predicate_method)} :#{bang_method}
94
94
  EOS
95
95
  end
96
96
 
@@ -99,10 +99,13 @@ module RConditions
99
99
  end
100
100
 
101
101
  def visibility(mod, id)
102
- case id
103
- when *mod.public_instance_methods(false): 'public'
104
- when *mod.protected_instance_methods(false): 'protected'
105
- when *mod.private_instance_methods(false): 'private'
102
+ case id
103
+ when *mod.public_instance_methods(false)
104
+ 'public'
105
+ when *mod.protected_instance_methods(false)
106
+ 'protected'
107
+ when *mod.private_instance_methods(false)
108
+ 'private'
106
109
  end
107
110
  end
108
111
  end
@@ -123,7 +126,7 @@ class Module #:nodoc:
123
126
  private :method_added_without_rconditions
124
127
 
125
128
  def method_added(id) #:nodoc:
126
- RConditions.extend_predicate_method_and_define_bang_method(self, id)
129
+ ::RConditions.extend_predicate_method_and_define_bang_method(self, id)
127
130
  method_added_without_rconditions(id)
128
131
  end
129
- end
132
+ end
@@ -1,6 +1,14 @@
1
1
  require 'test/unit'
2
2
  require 'rconditions'
3
3
 
4
+ class Array
5
+ # This method is used in the tests below to convert Arrays of Symbols to
6
+ # Arrays of Strings, so that the tests can be run with Ruby 1.9 as well.
7
+ def map_to_s
8
+ map {|each| each.to_s}
9
+ end
10
+ end
11
+
4
12
  class RConditionsTest < Test::Unit::TestCase
5
13
 
6
14
  class Foo
@@ -16,7 +24,18 @@ class RConditionsTest < Test::Unit::TestCase
16
24
  end
17
25
 
18
26
  def test_defines_bang_method_on_predicate_methods_defined_before_r_conditions_was_required
19
- assert [].class.method_defined?(:empty!)
27
+ assert Array.method_defined?(:empty!)
28
+ end
29
+
30
+ def test_defines_bang_method_on_private_predicate_methods_defined_before_r_conditions_was_required
31
+ assert Kernel.private_method_defined?(:autoload!)
32
+ end
33
+
34
+ # we cannot apply RConditions to Kernel#block_given?, since the
35
+ # implementation would require Binding.of_caller, which is broken since
36
+ # Ruby 1.8.5.
37
+ def test_defines_no_bang_method_for_block_given
38
+ assert !Kernel.private_method_defined?(:block_given!)
20
39
  end
21
40
 
22
41
  def test_defines_bang_method_on_predicate_methods_defined_after_r_conditions_was_required
@@ -24,13 +43,13 @@ class RConditionsTest < Test::Unit::TestCase
24
43
  end
25
44
 
26
45
  def test_does_not_define_methods_for_non_predicate_methods
27
- foo_methods_with_new_method = Foo.new.methods + ['new_method']
46
+ foo_methods_with_new_method = Foo.new.methods.map_to_s + ['new_method']
28
47
  eval "class Foo;def new_method; end; end"
29
- assert_equal foo_methods_with_new_method.sort, Foo.new.methods.sort
48
+ assert_equal foo_methods_with_new_method.sort, Foo.new.methods.map_to_s.sort
30
49
  end
31
50
 
32
51
  def test_bang_method_returns_true_if_predicate_method_returns_true
33
- assert Foo.new.d!
52
+ assert_equal true, Foo.new.d!
34
53
  end
35
54
 
36
55
  def test_bang_method_passes_predicate_method_result
@@ -82,9 +101,9 @@ class RConditionsTest < Test::Unit::TestCase
82
101
  end
83
102
 
84
103
  def test_no_visible_methods_added_except_bang_method
85
- foo_methods_with_predicate_method = Foo.new.methods + ['new_predicate_method?']
104
+ foo_methods_with_predicate_method = Foo.new.methods.map_to_s + ['new_predicate_method?']
86
105
  eval "class Foo;def new_predicate_method?; false; end; end"
87
- assert_equal ['new_predicate_method!'], Foo.new.methods - foo_methods_with_predicate_method
106
+ assert_equal ['new_predicate_method!'], Foo.new.methods.map_to_s - foo_methods_with_predicate_method
88
107
  end
89
108
 
90
109
  def test_no_bang_method_will_be_overridden
@@ -97,7 +116,7 @@ class RConditionsTest < Test::Unit::TestCase
97
116
  def test_no_bang_method_will_be_overridden_even_if_it_is_private
98
117
  eval "class Foo;private;def private_do_not_override_me!; 'private'; end; end"
99
118
  eval "class Foo;def private_do_not_override_me?; true; end; end"
100
- assert_equal 'private', Foo.new.__send__(:private_do_not_override_me!)
119
+ assert_equal 'private', Foo.new.instance_eval { private_do_not_override_me! }
101
120
  end
102
121
 
103
122
  def test_no_bang_method_will_be_overridden_even_if_it_is_in_a_superclas
@@ -128,8 +147,8 @@ class RConditionsTest < Test::Unit::TestCase
128
147
 
129
148
  def test_does_work_for_modules
130
149
  # any? is defined in the module Enumerable and included in Array
131
- assert Enumerable.instance_methods(false).include?('any?')
132
- assert !Array.instance_methods(false).include?('any?')
150
+ assert Enumerable.instance_methods(false).map_to_s.include?('any?')
151
+ assert !Array.instance_methods(false).map_to_s.include?('any?')
133
152
  assert Array.ancestors.include?(Enumerable)
134
153
 
135
154
  e = exception_raised_by { [].any! }
@@ -140,7 +159,18 @@ class RConditionsTest < Test::Unit::TestCase
140
159
  def test_does_work_for_alias_method
141
160
  eval <<-EOS
142
161
  class Foo
143
- alias_method :a_alias?, :a?
162
+ alias_method :a_alias_method?, :a?
163
+ end
164
+ EOS
165
+ e = exception_raised_by { Foo.new.a_alias_method! }
166
+ assert e.kind_of?(Foo::NotAAliasMethodError)
167
+ assert e.kind_of?(Foo::NotAError)
168
+ end
169
+
170
+ def test_does_work_for_alias
171
+ eval <<-EOS
172
+ class Foo
173
+ alias a_alias? a?
144
174
  end
145
175
  EOS
146
176
  e = exception_raised_by { Foo.new.a_alias! }
@@ -202,7 +232,7 @@ class RConditionsTest < Test::Unit::TestCase
202
232
  false
203
233
  end
204
234
  EOS
205
- Foo.send(:include, m)
235
+ Foo.instance_eval { include m }
206
236
  e = exception_raised_by { Foo.new.activated! }
207
237
  assert e.kind_of?(m.const_get(:NotActivatedError))
208
238
  self.class.const_set(:NowANamedModule, m)
@@ -241,13 +271,13 @@ class RConditionsTest < Test::Unit::TestCase
241
271
  end
242
272
  EOS
243
273
 
244
- assert Foo.public_instance_methods(false).include?('public_p?')
245
- assert Foo.protected_instance_methods(false).include?('protected_p?')
246
- assert Foo.private_instance_methods(false).include?('private_p?')
274
+ assert Foo.public_instance_methods(false).map_to_s.include?('public_p?')
275
+ assert Foo.protected_instance_methods(false).map_to_s.include?('protected_p?')
276
+ assert Foo.private_instance_methods(false).map_to_s.include?('private_p?')
247
277
 
248
- assert Foo.public_instance_methods(false).include?('public_p!')
249
- assert Foo.protected_instance_methods(false).include?('protected_p!')
250
- assert Foo.private_instance_methods(false).include?('private_p!')
278
+ assert Foo.public_instance_methods(false).map_to_s.include?('public_p!')
279
+ assert Foo.protected_instance_methods(false).map_to_s.include?('protected_p!')
280
+ assert Foo.private_instance_methods(false).map_to_s.include?('private_p!')
251
281
  end
252
282
 
253
283
  private
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: rconditions
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.1
7
- date: 2007-09-27 00:00:00 +02:00
6
+ version: 0.2.0
7
+ date: 2007-10-14 00:00:00 +02:00
8
8
  summary: RConditions generates a bang method (for example, Array#empty!) for each predicate method (for example, Array#empty?). The bang method will return true if the predicate method returns true, otherwise it will throw an exception. The exception will include auto-generated modules that give information on predicate methods passed or failed in the bang method's execution.
9
9
  require_paths:
10
10
  - lib