meta_programming 0.0.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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 |object, method_name_symbol, *args|
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) {|obj, sym, *args| ... }
77
- define_ghost_method(':my_method) {|obj, sym, *args| ... }
78
- define_ghost_method(/^my_method$/) {|obj, sym, *args| ... }
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 based on the
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{|object, method_name, *args| ...matching code... }
95
- define_ghost_method(proc) {|obj, proc_result, *args| ... }
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{|obj, sym, *args| sym =~ /^not_(.+\?)$/ && $1.to_sym }
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{|obj, sym, *args| sym =~ /^not_.+\?$/ && sym }
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
- begin
80
- handled = nil
81
- result = case matcher
82
- when Regexp
83
- yield(self, symbol, *args) if (handled = (symbol.to_s =~ matcher))
84
- when String, Symbol
85
- yield(self, symbol, *args) if (handled = (symbol == matcher.to_sym))
86
- when Proc
87
- handled = matcher.call(self, symbol)
88
- yield(self, handled == true ? symbol : handled, *args) if handled
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
- handled ? result : __send__("method_missing_without_#{ext}".to_sym, symbol, *args)
91
- rescue LocalJumpError
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 then matcher.call(self, method_name)
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
@@ -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
- allowed_methods = all_methods.select{|method| method.to_s =~ /^__/} + (exceptions.is_a?(Array) ? exceptions : [exceptions]).map(&:to_sym)
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, [:method_missing, :respond_to?])
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, [:methods, :method_missing, :respond_to?])
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
+
@@ -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 |object, symbol|
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 |object, symbol, *args|
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 |obj, sym, *args|
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
- # it "should work for methods called with blocks in 1.9" do
100
- # class GHA8
101
- # define_ghost_method(:call_block) do |obj, sym, *args|
102
- # yield(*args)
103
- # end
104
- # end
105
- # GHA8.new.call_block(%w(hi you)) do |*args|
106
- # args.join(' ')
107
- # end.should == 'hi you'
108
- # end
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 object, matcher_result, *args" do
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 {|obj, sym, *args| sym.to_s =~ /^yn_(.*\?)$/ && obj.methods.map(&:to_sym).include?($1.to_sym) && $1.to_sym }
123
- define_ghost_method(matcher) do |obj, res, *args|
124
- obj.__send__(res, *args) ? 'yes' : 'no'
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{|obj, sym, *args| sym.to_s =~ /^yn_(.*\?)$/ && obj.methods.map(&:to_sym).include?($1.to_sym) && $1.to_sym }
140
- define_ghost_method(matcher) do |obj, res, *args|
141
- obj.__send__(res, *args) ? 'yes' : 'no'
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{|obj, sym| sym.to_s =~ /^my_method\d$/ }) { 'uhhuh_my_method'}
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
- - 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-01 00:00:00 -07:00
17
+ date: 2010-05-02 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies: []
20
20