rconditions 0.1.1 → 0.2.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/README +26 -31
- data/lib/rconditions.rb +30 -27
- data/test/rconditions_test.rb +47 -17
- 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
|
-
==
|
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
|
75
|
-
predicate methods: <tt>User#can_view_posting!(posting)</tt> in the
|
76
|
-
above called <tt>User#can_view_posting?(posting)</tt>, which called
|
77
|
-
<tt>User#admin?</tt> and <tt>Posting#
|
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?
|
83
|
-
* <tt>Posting#spam?
|
84
|
-
* <tt>Posting#
|
85
|
-
* <tt>User#can_view_posting?
|
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
|
-
|
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::
|
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
|
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
|
110
|
-
|
111
|
-
*
|
112
|
-
|
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
|
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
|
43
|
-
Thread.current[:rconditions_exception_modules]
|
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.
|
74
|
+
::RConditions.add_exception_module(result ? #{positive_module} : #{negative_module})
|
69
75
|
result
|
70
76
|
end
|
71
|
-
#{
|
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.
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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)
|
104
|
-
|
105
|
-
when *mod.
|
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
|
data/test/rconditions_test.rb
CHANGED
@@ -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
|
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
|
-
|
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.
|
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 :
|
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.
|
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.
|
7
|
-
date: 2007-
|
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
|