must_be 1.0.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/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ pkg/
2
+ coverage/
data/README.md ADDED
@@ -0,0 +1,348 @@
1
+ # must_be Runtime Assertions
2
+
3
+ Ruby doesn't have a static type system. Tests and specs are well and good. But at the end of the day, Cthulhu reigns and sanity needs checking. must_be provides runtime assertions in all kinds of delicious Ruby flavors.
4
+
5
+ You can configure must_be to notify you of trouble in any way imaginable. Just set `MustBe.notifier` to any proc or other call worthy object. By default must_be raises an error, but logging and debugging notifiers come included. Furthermore, you can disable must_be at any time if its performance penalty proves unsatisfactory.
6
+
7
+ # Examples
8
+
9
+ After installing:
10
+
11
+ > sudo gem install must_be
12
+
13
+ Begin with customary oblations:
14
+
15
+ require 'rubygems'
16
+ require 'must_be'
17
+
18
+ Now `Object` has a number of `must`* and `must_not`* methods. Here are several examples of each. When an example notifies, the message corresponding to its `MustBe::Note` error is listed on the following comment line (`#=>` or `#~>` for a regexp match).
19
+
20
+ ## Basic
21
+
22
+ For your everyday, average sanity:
23
+
24
+ 87.must_be_a(Float, Integer)
25
+ 87.must_be_a(Symbol, String)
26
+ #=> 87.must_be_a(Symbol, String), but is a Fixnum
27
+
28
+ 87.must_not_be_a(Symbol, String, "message")
29
+ 87.must_not_be_a(Float, Integer, "message")
30
+ #=> 87.must_not_be_a(Float, Integer, "message"), but is a Fixnum
31
+
32
+ `must_be_in` uses `include?` to check. Multiple arguments are grouped together as an array:
33
+
34
+ 2.must_be_in(1, 2, 3)
35
+ 2.must_be_in([1, 2, 3])
36
+ 2.must_be_in(1 => 3, 2 => 4)
37
+ 2.must_be_in(1..3)
38
+ 2.must_be_in
39
+ #=> 2.must_be_in
40
+
41
+ 2.must_not_be_in
42
+ 2.must_not_be_in(4, 5)
43
+ 2.must_not_be_in(1 => 3, 2 => 4)
44
+ #=> 2.must_not_be_in({1=>3, 2=>4})
45
+
46
+ `must_be_boolean` ensures the value is `true` or `false`:
47
+
48
+ true.must_be_boolean
49
+ false.must_be_boolean
50
+ nil.must_be_boolean
51
+ #=> nil.must_be_boolean
52
+
53
+ Sometimes close is good enough:
54
+
55
+ 2.must_be_close(2.0)
56
+ 2.must_be_close(2.01)
57
+ 2.must_be_close(2.1)
58
+ #=> 2.must_be_close(2.1, 0.1), difference is 0.1
59
+
60
+ 2.must_be_close(2.1, 6)
61
+ 2.must_be_close(9.0, 6)
62
+ #=> 2.must_be_close(9.0, 6), difference is 7.0
63
+
64
+ `must_be` uses case (`===`) equality:
65
+
66
+ 34.must_be(Integer, :all)
67
+ :all.must_be(Integer, :all)
68
+ :none.must_be(Integer, :all)
69
+ #=> :none.must_be(Integer, :all), but matches Symbol
70
+
71
+ 5.must_be(1..5)
72
+ 5.must_be(1...5)
73
+ #=> 5.must_be(1...5), but matches Fixnum
74
+
75
+ 5.must_not_be(1...5)
76
+ 3.must_not_be(1...5)
77
+ #=> 3.must_not_be(1...5), but matches Fixnum
78
+
79
+ true.must_be
80
+ nil.must_be
81
+ #=> nil.must_be, but matches NilClass
82
+ false.must_be
83
+ #=> false.must_be, but matches FalseClass
84
+
85
+ nil.must_not_be
86
+ :hello.must_not_be
87
+ #=> :hello.must_not_be, but matches Symbol
88
+
89
+ :yep.must_be(lambda {|v| v == :yep})
90
+ :nope.must_be(lambda {|v| v == :yep})
91
+ #~> :nope.must_be\(#<Proc:0x[^.]+?>\), but matches Symbol
92
+
93
+ :yep.must_not_be(lambda {|v| v == :nope})
94
+ :nope.must_not_be(lambda {|v| v == :nope})
95
+ #~> :nope.must_not_be\(#<Proc:0x[^.]+?>\), but matches Symbol
96
+
97
+ ## Proxy
98
+
99
+ `must` either takes a block, or it returns a proxy for checking predicates:
100
+
101
+ 7.must {|x| x > 3}
102
+ 7.must {|x| x < 3}
103
+ #=> 7.must {}
104
+
105
+ 7.must_not {|x| x < 3}
106
+ 7.must_not {|x| x > 3}
107
+ #=> 7.must_not {}
108
+
109
+ 7.must == 7
110
+ 7.must > 3
111
+ 7.must.even?
112
+ #=> 7.must.even?
113
+
114
+ 7.must_not == 8
115
+ 7.must_not < 3
116
+ 7.must_not.odd?
117
+ #=> 7.must_not.odd?
118
+
119
+ ## Module#attr_typed
120
+
121
+ `attr_typed` is like `attr_accessor` but with type checking:
122
+
123
+ class Typed
124
+ attr_typed :v, Symbol
125
+ attr_typed :n, Fixnum, Bignum
126
+ attr_typed :o, &:odd?
127
+ end
128
+
129
+ t = Typed.new
130
+ t.v = :hello
131
+ t.v = "world"
132
+ #=> attribute `v' must be a Symbol, but value "world" is a String
133
+
134
+ t.n = 411
135
+ t.n = 4.1
136
+ #=> attribute `n' must be a Fixnum or Bignum, but value 4.1 is a Float
137
+
138
+ t.o = 7
139
+ t.o = 8
140
+ #=> attribute `o' cannot be 8
141
+
142
+ ## Containers
143
+
144
+ It's good to be sure that an array or hash contains the right stuff:
145
+
146
+ ["okay", :ready, "go"].must_only_contain(Symbol, String)
147
+ ["okay", :ready, 4].must_only_contain(Symbol, String)
148
+ #=> must_only_contain: 4.must_be(Symbol, String), but matches Fixnum in container ["okay", :ready, 4]
149
+
150
+ ["okay", :ready, "go"].must_not_contain(Numeric)
151
+ ["okay", :ready, 4].must_not_contain(Numeric)
152
+ #=> must_not_contain: 4.must_not_be(Numeric), but matches Fixnum in container ["okay", :ready, 4]
153
+
154
+ [].must_only_contain(:yes, :no)
155
+ [:yep].must_only_contain(:yes, :no)
156
+ #=> must_only_contain: :yep.must_be(:yes, :no), but matches Symbol in container [:yep]
157
+
158
+ [].must_not_contain(:yes, :no)
159
+ [:yes, :no].must_not_contain(:yes, :no)
160
+ #=> must_not_contain: :yes.must_not_be(:yes, :no), but matches Symbol in container [:yes, :no]
161
+
162
+ [0, [], ""].must_only_contain
163
+ [nil].must_only_contain
164
+ #=> must_only_contain: nil.must_be, but matches NilClass in container [nil]
165
+
166
+ [nil, false].must_not_contain
167
+ [0].must_not_contain
168
+ #=> must_not_contain: 0.must_not_be, but matches Fixnum in container [0]
169
+
170
+ {:welcome => :home}.must_only_contain(Symbol => Symbol)
171
+ {:symbol => :s, :fixnum => 5}.must_only_contain(Symbol => [Symbol, Fixnum])
172
+ {5 => :s, 6 => 5, :t => 5, :s => :s}.must_only_contain([Symbol, Fixnum] => [Symbol, Fixnum])
173
+ {6 => 5}.must_only_contain(Symbol => Fixnum, Fixnum => Symbol)
174
+ #=> must_only_contain: pair {6=>5} does not match [{Symbol=>Fixnum, Fixnum=>Symbol}] in container {6=>5}
175
+
176
+ {:welcome => nil}.must_not_contain(nil => Object)
177
+ {nil => :welcome}.must_not_contain(nil => Object)
178
+ #=> must_not_contain: pair {nil=>:welcome} matches [{nil=>Object}] in container {nil=>:welcome}
179
+
180
+ {:welcome => :home}.must_only_contain
181
+ {:welcome => nil}.must_only_contain
182
+ #=> must_only_contain: pair {:welcome=>nil} does not match [] in container {:welcome=>nil}
183
+
184
+ {nil => false, false => nil}.must_not_contain
185
+ {nil => 0}.must_not_contain
186
+ #=> must_not_contain: pair {nil=>0} matches [] in container {nil=>0}
187
+
188
+ It's better to be sure that a array or hash always contains the right stuff:
189
+
190
+ numbers = [].must_only_ever_contain(Numeric)
191
+ numbers << 3
192
+ numbers << :float
193
+ #=> must_only_ever_contain: Array#<<(:float): :float.must_be(Numeric), but matches Symbol in container [3, :float]
194
+
195
+ financials = [1, 4, 9].must_never_ever_contain(Float)
196
+ financials.map!{|x| Math.sqrt(x)}
197
+ #=> must_never_ever_contain: Array#map! {}: 3.0.must_not_be(Float), but matches Float in container [1.0, 2.0, 3.0]
198
+
199
+ ## MustBe::MustOnlyEverContain.register
200
+
201
+ It's best to be sure that your custom container contains the right stuff:
202
+
203
+ class Box
204
+ attr_accessor :contents
205
+
206
+ def self.[](contents = nil)
207
+ new(contents)
208
+ end
209
+
210
+ def initialize(contents = nil)
211
+ @contents = nil
212
+ end
213
+
214
+ def each
215
+ yield(contents) unless contents.nil?
216
+ end
217
+
218
+ def empty!
219
+ self.contents = nil
220
+ end
221
+
222
+ def inspect
223
+ "Box[#{contents.inspect}]"
224
+ end
225
+ end
226
+
227
+ MustOnlyEverContain.register(Box) do
228
+ def contents=(contents)
229
+ must_check_member(contents)
230
+ super
231
+ end
232
+
233
+ must_check_contents_after :empty!
234
+ end
235
+
236
+ box = Box[:hello].must_only_ever_contain(Symbol)
237
+ box.contents = :world
238
+ box.contents = 987
239
+ #=> must_only_ever_contain: Box#contents=(987): 987.must_be(Symbol), but matches Fixnum in container Box[987]
240
+
241
+ box = Box[2].must_never_ever_contain(nil)
242
+ box.contents = 64
243
+ box.empty!
244
+ #=> must_never_ever_contain: Box#empty!: nil.must_not_be(nil), but matches NilClass in container Box[nil]
245
+
246
+ ## Core
247
+
248
+ Define new must-assertions by using `must_notify`:
249
+
250
+ must_notify("message")
251
+ #=> message
252
+
253
+ must_notify(:receiver, :method, [:args], nil, ", additional message")
254
+ #=> :receiver.method(:args), additional message
255
+
256
+ note = MustBe::Note.new("message")
257
+ must_notify(note)
258
+ #=> message
259
+
260
+ Use `must_check` to temporarily override `MustBe.notify` usually as part of a bigger, better must-assertion.
261
+
262
+ note = must_check {}
263
+ note.must == nil
264
+
265
+ note = must_check { :it.must_not_be }
266
+ must_notify(note)
267
+ #=> :it.must_not_be, but matches Symbol
268
+
269
+ def add_more_if_notifies
270
+ must_check(lambda do
271
+ yield
272
+ end) do |note|
273
+ MustBe::Note.new(note.message+" more")
274
+ end
275
+ end
276
+
277
+ result = add_more_if_notifies { 5 }
278
+ result.must == 5
279
+
280
+ add_more_if_notifies { must_notify("learn") }
281
+ #=> learn more
282
+
283
+ ## Nonstandard Control Flow
284
+
285
+ Last, but not least, we have must-assertions for raising and throwing.
286
+
287
+ def rescuing
288
+ yield
289
+ rescue
290
+ raise if $!.is_a? MustBe::Note
291
+ end
292
+
293
+ rescuing { must_raise(/match/) { raise ArgumentError, "should match" } }
294
+ must_raise { "But it doesn't!" }
295
+ #=> main.must_raise {}, but nothing was raised
296
+
297
+ must_not_raise { "I'm fine."}
298
+ rescuing { must_not_raise { raise } }
299
+ #=> main.must_not_raise {}, but raised RuntimeError
300
+
301
+ catch(:ball) { must_throw { throw :ball } }
302
+ catch(:ball) { must_throw(:party) { throw :ball } }
303
+ #=> main.must_throw(:party) {}, but threw :ball
304
+
305
+ catch(:ball) { must_not_throw(:ball, :fiercely) { throw :ball, :gently } }
306
+ catch(:ball) { must_not_throw { throw :ball } }
307
+ #=> main.must_not_throw {}, but threw :ball
308
+
309
+ # Configuration
310
+
311
+ Set `MustBe.notifier` as needed. It takes a `MustBe::Note` as an argument and raises an exception unless its return value is false or nil:
312
+
313
+ MustBe.notifier = lambda do |note|
314
+ puts note
315
+ false
316
+ end
317
+
318
+ You can freely `MustBe.disable` and `MustBe.enable` as needed:
319
+
320
+ MustBe.disable
321
+
322
+ 3.must == 4
323
+ # no message
324
+
325
+ MustBe.enable
326
+
327
+ 3.must == 4
328
+ #=> 3.must.==(4)
329
+
330
+ Before requiring must_be, you can use `ENV['MUST_BE__NOTIFIER']` to set the notifier:
331
+
332
+ # Default: just raises the note.
333
+ ENV['MUST_BE__NOTIFIER'] = :raise
334
+
335
+ # Puts the note together with its backtrace.
336
+ ENV['MUST_BE__NOTIFIER'] = :log
337
+
338
+ # Invokes ruby-debug.
339
+ ENV['MUST_BE__NOTIFIER'] = :debug
340
+
341
+ # Calls MustBe.disable.
342
+ ENV['MUST_BE__NOTIFIER'] = :disable
343
+
344
+ By default `MustBe` is mixed into `Object`. If you want to mix `MustBe` selectively, set:
345
+
346
+ ENV['MUST_BE__DO_NOT_AUTOMATICALLY_INCLUDE_IN_OBJECT'] = "anything"
347
+
348
+ # Enjoy must_be.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'spec/rake/spectask'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gemspec|
6
+ gemspec.name = "must_be"
7
+ gemspec.summary = "must_be Runtime Assertions"
8
+ gemspec.description = "must_be provides runtime assertions which can easily be disabled in production environments. Likewise, the notifier can be customized to raise errors, log failure, enter the debugger, or anything else."
9
+ gemspec.email = "wtaysom@gmail.com"
10
+ gemspec.homepage = "http://github.com/wtaysom/must_be"
11
+ gemspec.authors = ["William Taysom"]
12
+ end
13
+ Jeweler::GemcutterTasks.new
14
+ rescue LoadError
15
+ puts "Jeweler not available. Install it with: gem install jeweler"
16
+ end
17
+
18
+ desc "Run the spec suite against rcov"
19
+ Spec::Rake::SpecTask.new(:rcov_helper) do |t|
20
+ t.rcov = true
21
+ t.rcov_opts = ['--exclude', '/Library/Ruby/Gems/']
22
+ end
23
+
24
+ desc "Run the spec suite against rcov and open coverage results"
25
+ task :rcov => :rcov_helper do
26
+ `open coverage/index.html`
27
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,262 @@
1
+ ## Basic
2
+
3
+ 87.must_be_a(Float, Integer)
4
+ 87.must_be_a(Symbol, String)
5
+ #=> 87.must_be_a(Symbol, String), but is a Fixnum
6
+
7
+ 87.must_not_be_a(Symbol, String, "message")
8
+ 87.must_not_be_a(Float, Integer, "message")
9
+ #=> 87.must_not_be_a(Float, Integer, "message"), but is a Fixnum
10
+
11
+ 2.must_be_in(1, 2, 3)
12
+ 2.must_be_in([1, 2, 3])
13
+ 2.must_be_in(1 => 3, 2 => 4)
14
+ 2.must_be_in(1..3)
15
+ 2.must_be_in
16
+ #=> 2.must_be_in
17
+
18
+ 2.must_not_be_in
19
+ 2.must_not_be_in(4, 5)
20
+ 2.must_not_be_in(1 => 3, 2 => 4)
21
+ #=> 2.must_not_be_in({1=>3, 2=>4})
22
+
23
+ true.must_be_boolean
24
+ false.must_be_boolean
25
+ nil.must_be_boolean
26
+ #=> nil.must_be_boolean
27
+
28
+ 2.must_be_close(2.0)
29
+ 2.must_be_close(2.01)
30
+ 2.must_be_close(2.1)
31
+ #=> 2.must_be_close(2.1, 0.1), difference is 0.1
32
+
33
+ 2.must_be_close(2.1, 6)
34
+ 2.must_be_close(9.0, 6)
35
+ #=> 2.must_be_close(9.0, 6), difference is 7.0
36
+
37
+ 34.must_be(Integer, :all)
38
+ :all.must_be(Integer, :all)
39
+ :none.must_be(Integer, :all)
40
+ #=> :none.must_be(Integer, :all), but matches Symbol
41
+
42
+ 5.must_be(1..5)
43
+ 5.must_be(1...5)
44
+ #=> 5.must_be(1...5), but matches Fixnum
45
+
46
+ 5.must_not_be(1...5)
47
+ 3.must_not_be(1...5)
48
+ #=> 3.must_not_be(1...5), but matches Fixnum
49
+
50
+ true.must_be
51
+ nil.must_be
52
+ #=> nil.must_be, but matches NilClass
53
+ false.must_be
54
+ #=> false.must_be, but matches FalseClass
55
+
56
+ nil.must_not_be
57
+ :hello.must_not_be
58
+ #=> :hello.must_not_be, but matches Symbol
59
+
60
+ :yep.must_be(lambda {|v| v == :yep})
61
+ :nope.must_be(lambda {|v| v == :yep})
62
+ #~> :nope.must_be\(#<Proc:0x[^.]+?>\), but matches Symbol
63
+
64
+ :yep.must_not_be(lambda {|v| v == :nope})
65
+ :nope.must_not_be(lambda {|v| v == :nope})
66
+ #~> :nope.must_not_be\(#<Proc:0x[^.]+?>\), but matches Symbol
67
+
68
+ ## Proxy
69
+
70
+ 7.must {|x| x > 3}
71
+ 7.must {|x| x < 3}
72
+ #=> 7.must {}
73
+
74
+ 7.must_not {|x| x < 3}
75
+ 7.must_not {|x| x > 3}
76
+ #=> 7.must_not {}
77
+
78
+ 7.must == 7
79
+ 7.must > 3
80
+ 7.must.even?
81
+ #=> 7.must.even?
82
+
83
+ 7.must_not == 8
84
+ 7.must_not < 3
85
+ 7.must_not.odd?
86
+ #=> 7.must_not.odd?
87
+
88
+ ## `Module#attr_typed`
89
+
90
+ class Typed
91
+ attr_typed :v, Symbol
92
+ attr_typed :n, Fixnum, Bignum
93
+ attr_typed :o, &:odd?
94
+ end
95
+
96
+ t = Typed.new
97
+ t.v = :hello
98
+ t.v = "world"
99
+ #=> attribute `v' must be a Symbol, but value "world" is a String
100
+
101
+ t.n = 411
102
+ t.n = 4.1
103
+ #=> attribute `n' must be a Fixnum or Bignum, but value 4.1 is a Float
104
+
105
+ t.o = 7
106
+ t.o = 8
107
+ #=> attribute `o' cannot be 8
108
+
109
+ ## Containers
110
+
111
+ ["okay", :ready, "go"].must_only_contain(Symbol, String)
112
+ ["okay", :ready, 4].must_only_contain(Symbol, String)
113
+ #=> must_only_contain: 4.must_be(Symbol, String), but matches Fixnum in container ["okay", :ready, 4]
114
+
115
+ ["okay", :ready, "go"].must_not_contain(Numeric)
116
+ ["okay", :ready, 4].must_not_contain(Numeric)
117
+ #=> must_not_contain: 4.must_not_be(Numeric), but matches Fixnum in container ["okay", :ready, 4]
118
+
119
+ [].must_only_contain(:yes, :no)
120
+ [:yep].must_only_contain(:yes, :no)
121
+ #=> must_only_contain: :yep.must_be(:yes, :no), but matches Symbol in container [:yep]
122
+
123
+ [].must_not_contain(:yes, :no)
124
+ [:yes, :no].must_not_contain(:yes, :no)
125
+ #=> must_not_contain: :yes.must_not_be(:yes, :no), but matches Symbol in container [:yes, :no]
126
+
127
+ [0, [], ""].must_only_contain
128
+ [nil].must_only_contain
129
+ #=> must_only_contain: nil.must_be, but matches NilClass in container [nil]
130
+
131
+ [nil, false].must_not_contain
132
+ [0].must_not_contain
133
+ #=> must_not_contain: 0.must_not_be, but matches Fixnum in container [0]
134
+
135
+ {:welcome => :home}.must_only_contain(Symbol => Symbol)
136
+ {:symbol => :s, :fixnum => 5}.must_only_contain(Symbol => [Symbol, Fixnum])
137
+ {5 => :s, 6 => 5, :t => 5, :s => :s}.must_only_contain([Symbol, Fixnum] => [Symbol, Fixnum])
138
+ {6 => 5}.must_only_contain(Symbol => Fixnum, Fixnum => Symbol)
139
+ #=> must_only_contain: pair {6=>5} does not match [{Symbol=>Fixnum, Fixnum=>Symbol}] in container {6=>5}
140
+
141
+ {:welcome => nil}.must_not_contain(nil => Object)
142
+ {nil => :welcome}.must_not_contain(nil => Object)
143
+ #=> must_not_contain: pair {nil=>:welcome} matches [{nil=>Object}] in container {nil=>:welcome}
144
+
145
+ {:welcome => :home}.must_only_contain
146
+ {:welcome => nil}.must_only_contain
147
+ #=> must_only_contain: pair {:welcome=>nil} does not match [] in container {:welcome=>nil}
148
+
149
+ {nil => false, false => nil}.must_not_contain
150
+ {nil => 0}.must_not_contain
151
+ #=> must_not_contain: pair {nil=>0} matches [] in container {nil=>0}
152
+
153
+ numbers = [].must_only_ever_contain(Numeric)
154
+ numbers << 3
155
+ numbers << :float
156
+ #=> must_only_ever_contain: Array#<<(:float): :float.must_be(Numeric), but matches Symbol in container [3, :float]
157
+
158
+ financials = [1, 4, 9].must_never_ever_contain(Float)
159
+ financials.map!{|x| Math.sqrt(x)}
160
+ #=> must_never_ever_contain: Array#map! {}: 3.0.must_not_be(Float), but matches Float in container [1.0, 2.0, 3.0]
161
+
162
+ ## MustBe::MustOnlyEverContain.register
163
+
164
+ class Box
165
+ attr_accessor :contents
166
+
167
+ def self.[](contents = nil)
168
+ new(contents)
169
+ end
170
+
171
+ def initialize(contents = nil)
172
+ @contents = nil
173
+ end
174
+
175
+ def each
176
+ yield(contents) unless contents.nil?
177
+ end
178
+
179
+ def empty!
180
+ self.contents = nil
181
+ end
182
+
183
+ def inspect
184
+ "Box[#{contents.inspect}]"
185
+ end
186
+ end
187
+
188
+ MustOnlyEverContain.register(Box) do
189
+ def contents=(contents)
190
+ must_check_member(contents)
191
+ super
192
+ end
193
+
194
+ must_check_contents_after :empty!
195
+ end
196
+
197
+ box = Box[:hello].must_only_ever_contain(Symbol)
198
+ box.contents = :world
199
+ box.contents = 987
200
+ #=> must_only_ever_contain: Box#contents=(987): 987.must_be(Symbol), but matches Fixnum in container Box[987]
201
+
202
+ box = Box[2].must_never_ever_contain(nil)
203
+ box.contents = 64
204
+ box.empty!
205
+ #=> must_never_ever_contain: Box#empty!: nil.must_not_be(nil), but matches NilClass in container Box[nil]
206
+
207
+ ## Core
208
+
209
+ must_notify("message")
210
+ #=> message
211
+
212
+ must_notify(:receiver, :method, [:args], nil, ", additional message")
213
+ #=> :receiver.method(:args), additional message
214
+
215
+ note = MustBe::Note.new("message")
216
+ must_notify(note)
217
+ #=> message
218
+
219
+ note = must_check {}
220
+ note.must == nil
221
+
222
+ note = must_check { :it.must_not_be }
223
+ must_notify(note)
224
+ #=> :it.must_not_be, but matches Symbol
225
+
226
+ def add_more_if_notifies
227
+ must_check(lambda do
228
+ yield
229
+ end) do |note|
230
+ MustBe::Note.new(note.message+" more")
231
+ end
232
+ end
233
+
234
+ result = add_more_if_notifies { 5 }
235
+ result.must == 5
236
+
237
+ add_more_if_notifies { must_notify("learn") }
238
+ #=> learn more
239
+
240
+ ## Nonstandard Control Flow
241
+
242
+ def rescuing
243
+ yield
244
+ rescue
245
+ raise if $!.is_a? MustBe::Note
246
+ end
247
+
248
+ rescuing { must_raise(/match/) { raise ArgumentError, "should match" } }
249
+ must_raise { "But it doesn't!" }
250
+ #=> main.must_raise {}, but nothing was raised
251
+
252
+ must_not_raise { "I'm fine."}
253
+ rescuing { must_not_raise { raise } }
254
+ #=> main.must_not_raise {}, but raised RuntimeError
255
+
256
+ catch(:ball) { must_throw { throw :ball } }
257
+ catch(:ball) { must_throw(:party) { throw :ball } }
258
+ #=> main.must_throw(:party) {}, but threw :ball
259
+
260
+ catch(:ball) { must_not_throw(:ball, :fiercely) { throw :ball, :gently } }
261
+ catch(:ball) { must_not_throw { throw :ball } }
262
+ #=> main.must_not_throw {}, but threw :ball
@@ -0,0 +1,47 @@
1
+ require 'lib/must_be'
2
+
3
+ $expecting_note = false
4
+
5
+ class UnexpectedNoteError < StandardError; end
6
+ class MismatchedNoteError < StandardError; end
7
+
8
+ MustBe.notifier = lambda do |note|
9
+ unless $expecting_note
10
+ $note = note
11
+ raise UnexpectedNoteError
12
+ end
13
+ true
14
+ end
15
+
16
+ def log(message, details)
17
+ puts "expected: #{message.is_a?(Regexp) ? message.inspect : message}"
18
+ puts details
19
+ end
20
+
21
+ def check_note(message)
22
+ $expecting_note = true
23
+ yield
24
+ log(message, "but did not notify")
25
+ rescue Note => note
26
+ unless message === note.message
27
+ log(message, " found: #{note.message}")
28
+ raise MismatchedNoteError
29
+ end
30
+ ensure
31
+ $expecting_note = false
32
+ end
33
+
34
+ example_text = IO.read(File.dirname(__FILE__)+"/examples.rb")
35
+ example_text.gsub!(/^(.*)\n#=> (.*)$/, "check_note(%{\\2}) {\n\\1}")
36
+ example_text.gsub!(/^(.*)\n#~> (.*)$/, "check_note(%r{\\2}) {\n\\1}")
37
+
38
+ begin
39
+ eval(example_text)
40
+ puts "Examples are okay."
41
+ rescue UnexpectedNoteError => ex
42
+ eval_frame = ex.backtrace.last
43
+ eval_frame =~ /:(\d+)$/
44
+ eval_line = $1.to_i
45
+ puts "example.rb:#{eval_line}: unexpected note: #=> #{$note}"
46
+ rescue MismatchedNoteError
47
+ end