rconditions 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|