meta_programming 0.0.8 → 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.rdoc +36 -15
- data/lib/meta_programming/class.rb +2 -1
- data/lib/meta_programming/object.rb +35 -17
- data/spec/blank_slate_spec.rb +5 -3
- data/spec/ghost_methods_spec.rb +87 -36
- metadata +3 -3
data/README.rdoc
CHANGED
@@ -16,6 +16,11 @@ Concepts/Terminology
|
|
16
16
|
|
17
17
|
* Metaprogramming_Ruby[http://pragprog.com/titles/ppmetr/metaprogramming-ruby]
|
18
18
|
|
19
|
+
Example Code
|
20
|
+
|
21
|
+
* why_not[http://github.com/jeffp/why_not/blob/master/lib/why_not.rb]
|
22
|
+
* easy_nils[http://github.com/jeffp/easy_nils/blob/master/lib/easy_nils.rb]
|
23
|
+
|
19
24
|
== Beta Status
|
20
25
|
|
21
26
|
I consider this Gem in personal beta and suspect it will be in this state for
|
@@ -26,6 +31,21 @@ I welcome comments and collaborators who
|
|
26
31
|
would like to develop a library for simplifying common patterns and limit
|
27
32
|
the pitfalls of Ruby meta-programming.
|
28
33
|
|
34
|
+
== Changelog
|
35
|
+
|
36
|
+
* 0.2.0
|
37
|
+
|
38
|
+
Removes object parameter from method blocks and lambda matchers in define_ghost_method, and
|
39
|
+
adds more descriptive error handling. define_ghost_method block and matchers are now
|
40
|
+
evaluated in the context of the object instance.
|
41
|
+
|
42
|
+
I've bumped the version to 0.2.0 because this changes the interface.
|
43
|
+
|
44
|
+
* 0.0.8
|
45
|
+
|
46
|
+
initial implementation
|
47
|
+
|
48
|
+
|
29
49
|
== Issues
|
30
50
|
|
31
51
|
=== 1.9 vs 1.8
|
@@ -54,7 +74,6 @@ Current Methods
|
|
54
74
|
* blank_slate
|
55
75
|
|
56
76
|
|
57
|
-
|
58
77
|
== Usage
|
59
78
|
|
60
79
|
=== define_ghost_method
|
@@ -62,20 +81,21 @@ Current Methods
|
|
62
81
|
+define_ghost_method+ creates a 'ghost' method for an undefined method that corresponds
|
63
82
|
to the 'matcher' parameter.
|
64
83
|
|
65
|
-
define_ghost_method(matcher) do |
|
84
|
+
define_ghost_method(matcher) do |method_name_symbol, *args|
|
66
85
|
...method body...
|
67
86
|
end
|
68
87
|
|
69
88
|
A block must be present and it takes (optionally) the receiving object, the method name (as
|
70
|
-
a symbol with exception of a lambda matcher) and the method arguments.
|
89
|
+
a symbol with exception of a lambda matcher) and the method arguments. As of 0.2.0, the
|
90
|
+
block and lambda matcher is evaluated in the context of the object.
|
71
91
|
|
72
92
|
The matcher may be any of a symbol, string, regular expression or Proc/lambda.
|
73
93
|
|
74
94
|
==== String, symbols and regular expression matchers
|
75
95
|
|
76
|
-
define_ghost_method('my_method) {|
|
77
|
-
define_ghost_method(':my_method) {|
|
78
|
-
define_ghost_method(/^my_method$/) {|
|
96
|
+
define_ghost_method('my_method) {|sym, *args| ... }
|
97
|
+
define_ghost_method(':my_method) {|sym, *args| ... }
|
98
|
+
define_ghost_method(/^my_method$/) {|sym, *args| ... }
|
79
99
|
|
80
100
|
String and symbol matchers will only process the method if its name matches the string
|
81
101
|
or symbol exactly. Regular expressions provide greater flexibility and care must be
|
@@ -87,27 +107,28 @@ method block. This is not the case with lambda matchers.
|
|
87
107
|
|
88
108
|
==== Proc/lambda matchers
|
89
109
|
|
90
|
-
Lambda matchers provide an opportunity to greatly scrutinize the method call
|
91
|
-
parameters passed to the method block (object, method_name, *args). The method is
|
110
|
+
Lambda matchers provide an opportunity to greatly scrutinize the method call. The method is
|
92
111
|
invoked for any lambda not evaluating to nil or false.
|
93
112
|
|
94
|
-
proc = lambda{|
|
95
|
-
define_ghost_method(proc) {|
|
113
|
+
proc = lambda{|method_name, *args| ...matching code... }
|
114
|
+
define_ghost_method(proc) {|proc_result, *args| ... }
|
96
115
|
|
97
116
|
Proc and lambda matchers differ from the other matcher in that the second parameter passes
|
98
117
|
the result of the Proc/lambda matcher to the method block. This feature lets the matcher
|
99
118
|
pre-process the name of the method passed to the body.
|
100
119
|
|
101
|
-
proc = lambda{|
|
120
|
+
proc = lambda{|sym, *args| sym =~ /^not_(.+\?)$/ && self.class.method_defined?($1.to_sym) && $1.to_sym }
|
121
|
+
|
122
|
+
The lambda matcher is evaluated in the context of the object and can
|
123
|
+
call methods of the object as in the above example. The lambda matcher is also called
|
124
|
+
in the respond_to? method, so it is a good idea to avoid usind respond_to? in the lambda
|
125
|
+
matcher to avoid a stack overflow error. Instead call method_defined? on the class object.
|
102
126
|
|
103
127
|
The extra flexibility comes at greater responsibility. If you only want to pass the
|
104
128
|
method name symbol to the method block, don't forget to return it from the lambda.
|
105
129
|
|
106
|
-
proc = lambda{|
|
130
|
+
proc = lambda{|sym, *args| sym =~ /^not_.+\?$/ && sym }
|
107
131
|
|
108
|
-
Additionally, the 'return' keyword has different effects between Procs and lambdas.
|
109
|
-
I suggest not explicitly using the 'return' keyword in your method blocks. It's a Ruby thing.
|
110
|
-
define_ghost_method will remind you if a LocalJumpError is raised.
|
111
132
|
|
112
133
|
==== Critique
|
113
134
|
|
@@ -7,9 +7,10 @@ module MetaProgramming
|
|
7
7
|
def blank_slate(opts={})
|
8
8
|
opts[:except] = opts[:except] ? (opts[:except].is_a?(Array) ? opts[:except] : [opts[:except]]) : []
|
9
9
|
exceptions = opts[:except].map(&:to_s)
|
10
|
-
exceptions += ['method_missing', 'respond_to?'] unless opts[:all]
|
11
10
|
matchers = exceptions.map{|ex| "^#{ex.gsub(/\?/, '\?')}$" }
|
11
|
+
matchers += ['^method_missing', '^respond_to'] unless opts[:all]
|
12
12
|
matchers << '^__'
|
13
|
+
matchers << '^object_id$'
|
13
14
|
regexp = Regexp.new(matchers.join('|'))
|
14
15
|
instance_methods.each do |m|
|
15
16
|
undef_method m unless regexp.match(m.to_s)
|
@@ -74,32 +74,50 @@ module MetaProgramming
|
|
74
74
|
def define_ghost_method(matcher, &block)
|
75
75
|
raise "Must have a block" unless block_given?
|
76
76
|
raise ArgumentError, "Matcher argument must be either a 'string', :symbol, /regexp/ or proc" unless (matcher.nil? || [String, Symbol, Regexp, Proc].any?{|c| matcher.is_a?(c)})
|
77
|
-
ext = matcher.hash.abs.to_s
|
77
|
+
ext = "#{self.name.gsub(/^.+>::/,'')}_#{matcher.class.name.gsub(/^.+>::/,'')}#{matcher.hash.abs.to_s}"
|
78
|
+
_ghost_method_handler = "_ghost_method_handler_#{ext}".to_sym
|
79
|
+
_ghost_method_matcher = "_ghost_method_matcher_#{ext}".to_sym
|
80
|
+
define_method(_ghost_method_handler, block)
|
81
|
+
private _ghost_method_handler
|
82
|
+
if matcher.is_a?(Proc)
|
83
|
+
define_method(_ghost_method_matcher, matcher)
|
84
|
+
private _ghost_method_matcher
|
85
|
+
end
|
78
86
|
define_chained_method(:method_missing, ext.to_sym) do |symbol, *args|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
87
|
+
handled = case matcher
|
88
|
+
when Regexp then !(symbol.to_s =~ matcher).nil?
|
89
|
+
when String, Symbol then (symbol == matcher.to_sym)
|
90
|
+
when Proc
|
91
|
+
begin
|
92
|
+
__send__(_ghost_method_matcher, symbol)
|
93
|
+
rescue Exception => matcher_error
|
94
|
+
raise matcher_error, "#{matcher_error.message} in a ghost method matcher called for symbol :#{symbol}. Be sure to use self.class.method_defined? instead of respond_to? in a lambda matcher."
|
95
|
+
end
|
96
|
+
else nil
|
97
|
+
end
|
98
|
+
if handled
|
99
|
+
begin
|
100
|
+
__send__(_ghost_method_handler, (handled == true ? symbol : handled), *args)
|
101
|
+
rescue Exception => handler_error
|
102
|
+
raise handler_error, "#{handler_error.message} in a ghost method block called with symbol :#{symbol}."
|
89
103
|
end
|
90
|
-
|
91
|
-
|
92
|
-
raise LocalJumpError, "Remove the 'return' keyword in your method block."
|
104
|
+
else
|
105
|
+
__send__("method_missing_without_#{ext}".to_sym, symbol, *args)
|
93
106
|
end
|
94
107
|
end
|
95
108
|
#cripple respond_to? in deference of 1.8 -- the include_private no longer works
|
96
109
|
define_chained_method(:respond_to?, ext.to_sym) do |method_name| #1.9 only, include_private=nil|
|
97
110
|
responds = case matcher
|
98
|
-
when Regexp then method_name.to_s =~ matcher
|
111
|
+
when Regexp then !(method_name.to_s =~ matcher).nil?
|
99
112
|
when String, Symbol then method_name == matcher.to_sym
|
100
|
-
when Proc
|
113
|
+
when Proc
|
114
|
+
begin
|
115
|
+
__send__(_ghost_method_matcher, method_name)
|
116
|
+
rescue Exception => matcher_error
|
117
|
+
raise matcher_error, "#{matcher_error.message} in a ghost method matcher called in respond_to? for symbol :#{method_name}. Be sure to use self.class.method_defined? instead of respond_to? in a lambda matcher."
|
118
|
+
end
|
101
119
|
end
|
102
|
-
responds || __send__("respond_to_without_#{ext}?", method_name) #1.9 only, include_private)
|
120
|
+
responds || __send__("respond_to_without_#{ext}?".to_sym, method_name) #1.9 only, include_private)
|
103
121
|
end
|
104
122
|
end
|
105
123
|
end
|
data/spec/blank_slate_spec.rb
CHANGED
@@ -6,7 +6,8 @@ describe "blank_slate" do
|
|
6
6
|
all_methods = [:public_methods, :protected_methods, :private_methods].map do |method|
|
7
7
|
instance.send(method)
|
8
8
|
end.flatten.map(&:to_sym).uniq
|
9
|
-
|
9
|
+
matchers = (['^__', '^object_id$'] + (exceptions.is_a?(Array) ? exceptions : [exceptions])).join('|')
|
10
|
+
allowed_methods = all_methods.select{|method| method.to_s =~ Regexp.new(matchers)}.map(&:to_sym)
|
10
11
|
all_methods - allowed_methods
|
11
12
|
end
|
12
13
|
|
@@ -14,7 +15,7 @@ describe "blank_slate" do
|
|
14
15
|
class BlankSlate1
|
15
16
|
blank_slate
|
16
17
|
end
|
17
|
-
test_methods = collect_test_methods(Object.new, [
|
18
|
+
test_methods = collect_test_methods(Object.new, ['^method_missing', '^respond_to'])
|
18
19
|
|
19
20
|
bs = BlankSlate1.new
|
20
21
|
test_methods.each do |method|
|
@@ -39,7 +40,7 @@ describe "blank_slate" do
|
|
39
40
|
class BlankSlate3
|
40
41
|
blank_slate :except=>[:methods]
|
41
42
|
end
|
42
|
-
test_methods = collect_test_methods(Object.new, [
|
43
|
+
test_methods = collect_test_methods(Object.new, ['^methods$', '^method_missing', '^respond_to'])
|
43
44
|
|
44
45
|
bs=BlankSlate3.new
|
45
46
|
test_methods.each do |method|
|
@@ -51,3 +52,4 @@ describe "blank_slate" do
|
|
51
52
|
end
|
52
53
|
|
53
54
|
|
55
|
+
|
data/spec/ghost_methods_spec.rb
CHANGED
@@ -37,7 +37,7 @@ describe "MetaProgramming" do
|
|
37
37
|
it "should scream if matcher is not string, symbol or regular expression" do
|
38
38
|
lambda {
|
39
39
|
class GHA2
|
40
|
-
define_ghost_method(1) { puts 1}
|
40
|
+
define_ghost_method(1) {|sym| puts 1}
|
41
41
|
end
|
42
42
|
}.should raise_exception(ArgumentError)
|
43
43
|
end
|
@@ -50,28 +50,22 @@ describe "MetaProgramming" do
|
|
50
50
|
end
|
51
51
|
it "should define a ghost method using a symbol" do
|
52
52
|
class GHA1
|
53
|
-
define_ghost_method(:catch_this_one) { 'catch_this_one' }
|
53
|
+
define_ghost_method(:catch_this_one) {|sym| 'catch_this_one' }
|
54
54
|
end
|
55
55
|
lambda { GHA1.new.not_this_one }.should raise_exception(NoMethodError)
|
56
56
|
lambda { GHA1.new.catch_this_one.should == 'catch_this_one' }.should_not raise_exception
|
57
57
|
lambda { GHA1.new.catch_this_one_too }.should raise_exception(NoMethodError)
|
58
58
|
end
|
59
|
-
it "should warn about using 'return' keyword in method block" do
|
60
|
-
class GHA4
|
61
|
-
define_ghost_method(:catch_this_one) { return 'catch_this_one'}
|
62
|
-
end
|
63
|
-
lambda { GHA4.new.catch_this_one }.should raise_exception(LocalJumpError, /'return' keyword/)
|
64
|
-
end
|
65
59
|
it "should define a ghost method using a string" do
|
66
60
|
class GHA5
|
67
|
-
define_ghost_method('catch_another') { 'catch_another_one'}
|
61
|
+
define_ghost_method('catch_another') {|sym| 'catch_another_one'}
|
68
62
|
end
|
69
63
|
lambda { GHA5.new.catch_another.should == 'catch_another_one'}.should_not raise_exception
|
70
64
|
lambda { GHA5.new.catch_another_one }.should raise_exception(NoMethodError)
|
71
65
|
end
|
72
66
|
it "should define a ghost method using a regular expression" do
|
73
67
|
class GHA6
|
74
|
-
define_ghost_method(/catch/) do |
|
68
|
+
define_ghost_method(/catch/) do |symbol|
|
75
69
|
symbol
|
76
70
|
end
|
77
71
|
end
|
@@ -81,7 +75,7 @@ describe "MetaProgramming" do
|
|
81
75
|
end
|
82
76
|
it "should pass arguments to the block" do
|
83
77
|
class GHA7
|
84
|
-
define_ghost_method(/ghost/) do |
|
78
|
+
define_ghost_method(/ghost/) do |symbol, *args|
|
85
79
|
"#{symbol.to_s.gsub(/_/, ' ')} #{args.join(' ')}"
|
86
80
|
end
|
87
81
|
end
|
@@ -89,39 +83,39 @@ describe "MetaProgramming" do
|
|
89
83
|
end
|
90
84
|
it "should pass parameters" do
|
91
85
|
class GHA9
|
92
|
-
define_ghost_method('test_args') do |
|
86
|
+
define_ghost_method('test_args') do |sym, *args|
|
93
87
|
args.join(' ')
|
94
88
|
end
|
95
89
|
end
|
96
90
|
GHA9.new.test_args('hi', 'you').should == 'hi you'
|
97
91
|
end
|
98
92
|
#ghost methods that can call blocks are not supported
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
93
|
+
#it "should work for methods called with blocks in 1.9" do
|
94
|
+
# class GHA8
|
95
|
+
# define_ghost_method(:call_block) do |obj, sym, *args|
|
96
|
+
# yield(*args)
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
# GHA8.new.call_block(%w(hi you)) do |*args|
|
100
|
+
# args.join(' ')
|
101
|
+
# end.should == 'hi you'
|
102
|
+
#end
|
109
103
|
it "should define ghost methods for class Object" do
|
110
104
|
class Object
|
111
|
-
define_ghost_method(:alive!) do
|
105
|
+
define_ghost_method(:alive!) do |sym|
|
112
106
|
'ALIVE'
|
113
107
|
end
|
114
108
|
end
|
115
109
|
lambda { Object.new.alive!.should == 'ALIVE'}.should_not raise_exception
|
116
110
|
end
|
117
|
-
it "should accept lambda as an advanced matcher and lambda parameters should be
|
111
|
+
it "should accept lambda as an advanced matcher and lambda parameters should be matcher_result, *args" do
|
118
112
|
class TestObject1
|
119
113
|
def yes?; true; end
|
120
114
|
def no?; false; end
|
121
115
|
|
122
|
-
matcher = lambda {|
|
123
|
-
define_ghost_method(matcher) do |
|
124
|
-
|
116
|
+
matcher = lambda {|sym| sym.to_s =~ /^yn_(.*\?)$/ && self.class.method_defined?($1.to_sym) && $1.to_sym }
|
117
|
+
define_ghost_method(matcher) do |res, *args|
|
118
|
+
self.__send__(res, *args) ? 'yes' : 'no'
|
125
119
|
end
|
126
120
|
end
|
127
121
|
lambda { TestObject1.new.yes?.should == true}.should_not raise_exception
|
@@ -131,14 +125,23 @@ describe "MetaProgramming" do
|
|
131
125
|
lambda { TestObject1.new.yessir? }.should raise_exception(NoMethodError)
|
132
126
|
lambda { TestObject1.new.yn_yessir? }.should raise_exception(NoMethodError)
|
133
127
|
end
|
128
|
+
it "should create a private ghost_method_handler and ghost_method_matcher and not a public ones" do
|
129
|
+
class TestObject3
|
130
|
+
matcher = lambda {|sym| sym.to_s =~ /asdfjf/ && sym}
|
131
|
+
define_ghost_method(matcher) {|sym| 'hello' }
|
132
|
+
end
|
133
|
+
obj = TestObject3.new
|
134
|
+
obj.private_methods.map(&:to_s).detect{|method| method =~ /ghost_method_handler/}.should_not be_nil
|
135
|
+
obj.private_methods.map(&:to_s).detect{|method| method =~ /ghost_method_matcher/}.should_not be_nil
|
136
|
+
end
|
134
137
|
it "should accept Procs as an advanced matcher and the parameters should be obj, matcher_result, *args" do
|
135
138
|
class TestObject2
|
136
139
|
def yes?; true; end
|
137
140
|
def no?; false; end
|
138
141
|
|
139
|
-
matcher = Proc.new{|
|
140
|
-
define_ghost_method(matcher) do |
|
141
|
-
|
142
|
+
matcher = Proc.new{|sym| sym.to_s =~ /^yn_(.*\?)$/ && methods.map(&:to_sym).include?($1.to_sym) && $1.to_sym }
|
143
|
+
define_ghost_method(matcher) do |res, *args|
|
144
|
+
__send__(res, *args) ? 'yes' : 'no'
|
142
145
|
end
|
143
146
|
end
|
144
147
|
lambda { TestObject2.new.yes?.should == true}.should_not raise_exception
|
@@ -151,9 +154,9 @@ describe "MetaProgramming" do
|
|
151
154
|
it "should create an appropriate respond_to? for the string matchers" do
|
152
155
|
class StringMatcher
|
153
156
|
def hello; end
|
154
|
-
define_ghost_method('my_method') { 'yes_my_method'}
|
157
|
+
define_ghost_method('my_method') {|sym| 'yes_my_method'}
|
155
158
|
end
|
156
|
-
lambda { StringMatcher.new.my_method.should == 'yes_my_method'}.should_not raise_exception
|
159
|
+
lambda { StringMatcher.new.my_method.should == 'yes_my_method'}.should_not raise_exception
|
157
160
|
lambda { StringMatcher.new.respond_to?(:my_method2).should be_false}.should_not raise_exception
|
158
161
|
lambda { StringMatcher.new.respond_to?(:my_method).should be_true }.should_not raise_exception
|
159
162
|
StringMatcher.new.respond_to?(:hello).should be_true
|
@@ -161,7 +164,7 @@ describe "MetaProgramming" do
|
|
161
164
|
it "should create an appropriate respond_to? for the symbol matchers" do
|
162
165
|
class SymbolMatcher
|
163
166
|
def hello; end
|
164
|
-
define_ghost_method(:my_method2) { 'yep_my_method'}
|
167
|
+
define_ghost_method(:my_method2) {|sym| 'yep_my_method'}
|
165
168
|
end
|
166
169
|
lambda { SymbolMatcher.new.my_method2.should == 'yep_my_method'}.should_not raise_exception
|
167
170
|
lambda { SymbolMatcher.new.respond_to?(:my_method2).should be_true}.should_not raise_exception
|
@@ -171,14 +174,14 @@ describe "MetaProgramming" do
|
|
171
174
|
it "should create an appropriate respond_to? for the regexp matchers" do
|
172
175
|
class RegexpMatcher
|
173
176
|
def hello; end
|
174
|
-
define_ghost_method(/^my_method$/) { 'yo_my_method'}
|
177
|
+
define_ghost_method(/^my_method$/) {|sym| 'yo_my_method'}
|
175
178
|
end
|
176
179
|
lambda { RegexpMatcher.new.my_method.should == 'yo_my_method'}.should_not raise_exception
|
177
180
|
lambda { RegexpMatcher.new.respond_to?(:my_method).should be_true}.should_not raise_exception
|
178
181
|
RegexpMatcher.new.respond_to?(:hello).should be_true
|
179
182
|
class RegexpMatcher2
|
180
183
|
def hello; end
|
181
|
-
define_ghost_method(/^my_method\d$/) { 'yoo_my_method'}
|
184
|
+
define_ghost_method(/^my_method\d$/) {|sym| 'yoo_my_method'}
|
182
185
|
end
|
183
186
|
lambda { RegexpMatcher2.new.my_method1.should == 'yoo_my_method'}.should_not raise_exception
|
184
187
|
lambda { RegexpMatcher2.new.my_method }.should raise_exception(NoMethodError)
|
@@ -189,7 +192,7 @@ describe "MetaProgramming" do
|
|
189
192
|
it "should create an appropriate respond_to? for the lambda matchers" do
|
190
193
|
class LambdaMatcher
|
191
194
|
def hello; end
|
192
|
-
define_ghost_method(lambda{|
|
195
|
+
define_ghost_method(lambda{|sym| sym.to_s =~ /^my_method\d$/ }) {|sym| 'uhhuh_my_method'}
|
193
196
|
end
|
194
197
|
lambda { LambdaMatcher.new.my_method2.should == 'uhhuh_my_method'}.should_not raise_exception
|
195
198
|
lambda { LambdaMatcher.new.my_method}.should raise_exception(NoMethodError)
|
@@ -198,6 +201,54 @@ describe "MetaProgramming" do
|
|
198
201
|
lambda { LambdaMatcher.new.respond_to?(:my_methods).should be_false}.should_not raise_exception
|
199
202
|
LambdaMatcher.new.respond_to?(:hello).should be_true
|
200
203
|
end
|
204
|
+
|
205
|
+
if RUBY_VERSION >= '1.9'
|
206
|
+
it "should not raise a LocalJump error when 'return' keyword used in method block" do
|
207
|
+
class GHB1
|
208
|
+
define_ghost_method(:my_method) {|sym| return 'hello'}
|
209
|
+
end
|
210
|
+
lambda { GHB1.new.my_method.should == 'hello'}.should_not raise_exception
|
211
|
+
lambda { GHB1.new.my_method2}.should raise_exception(NoMethodError)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
it "should not raise a LocalJumpError when 'return' keyword used in lambda matcher" do
|
215
|
+
class GHB2
|
216
|
+
matcher = lambda{|sym| return true if sym == :my_method; return false }
|
217
|
+
define_ghost_method(matcher){|sym| 'hello'}
|
218
|
+
end
|
219
|
+
lambda { GHB2.new.my_method.should == 'hello'}.should_not raise_exception
|
220
|
+
lambda { GHB2.new.my_method2 }.should raise_exception(NoMethodError)
|
221
|
+
end
|
222
|
+
it "should indicate exceptions come from method block when originating in method block" do
|
223
|
+
class GHB3
|
224
|
+
define_ghost_method(:my_method){|sym| raise "my cool exception"}
|
225
|
+
end
|
226
|
+
lambda { GHB3.new.my_method}.should raise_exception(/ghost method block/)
|
227
|
+
lambda { GHB3.new.my_method}.should raise_exception(/my cool exception/)
|
228
|
+
end
|
229
|
+
it "should indicate exceptions come from lambda matcher when originating in lambda matcher" do
|
230
|
+
class GHB4
|
231
|
+
matcher = lambda{|sym| raise 'my cooler exception'}
|
232
|
+
define_ghost_method(matcher){|sym| 'hello'}
|
233
|
+
end
|
234
|
+
lambda { GHB4.new.my_method}.should raise_exception(/ghost method matcher/)
|
235
|
+
lambda { GHB4.new.my_method}.should raise_exception(/my cooler exception/)
|
236
|
+
end
|
237
|
+
it "should indicate exceptions come from respond_to? matcher when originating from there" do
|
238
|
+
class GHB6
|
239
|
+
matcher = lambda{|sym| raise 'my respond_to exception'}
|
240
|
+
define_ghost_method(matcher){|sym| 'hello'}
|
241
|
+
end
|
242
|
+
lambda { GHB6.new.respond_to?(:my_method)}.should raise_exception(/ghost method matcher/)
|
243
|
+
lambda { GHB6.new.respond_to?(:my_method)}.should raise_exception(/my respond_to exception/)
|
244
|
+
end
|
245
|
+
it "should inform about using method_defined? instead of respond_to? in matcher" do
|
246
|
+
class GHB5
|
247
|
+
matcher = lambda{|sym| sym.to_s =~ /^x_(.+)$/ && self.respond_to?($1.to_sym) && $1.to_sym }
|
248
|
+
define_ghost_method(matcher){|sym, *args| __send__(sym, *args) }
|
249
|
+
end
|
250
|
+
lambda { GHB5.new.x_methods }.should raise_exception(/method_defined\?/)
|
251
|
+
end
|
201
252
|
end
|
202
253
|
|
203
254
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
+
- 2
|
7
8
|
- 0
|
8
|
-
|
9
|
-
version: 0.0.8
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Jeff Patmon
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-02 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|