bang 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING.md +36 -0
- data/HISTORY.md +10 -0
- data/README.md +63 -0
- data/lib/bang.rb +350 -0
- data/lib/bang/minitest.rb +2 -0
- data/lib/bang/testunit.rb +2 -0
- data/spec/applique/brass.rb +2 -0
- metadata +90 -0
data/COPYING.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# COPYRIGHT
|
2
|
+
|
3
|
+
## NOTICES
|
4
|
+
|
5
|
+
### Assay
|
6
|
+
|
7
|
+
| Project | Bang |
|
8
|
+
|-----------|-----------------------------------|
|
9
|
+
| Copyright | (c) 2012 Rubyworks |
|
10
|
+
| License | (r) BSD-2-Clause |
|
11
|
+
| Website | http://rubyworks.github.com/bang |
|
12
|
+
|
13
|
+
## LICENSES
|
14
|
+
|
15
|
+
### BSD-2-Clause License
|
16
|
+
|
17
|
+
Redistribution and use in source and binary forms, with or without
|
18
|
+
modification, are permitted provided that the following conditions are met:
|
19
|
+
|
20
|
+
1. Redistributions of source code must retain the above copyright notice,
|
21
|
+
this list of conditions and the following disclaimer.
|
22
|
+
|
23
|
+
2. Redistributions in binary form must reproduce the above copyright
|
24
|
+
notice, this list of conditions and the following disclaimer in the
|
25
|
+
documentation and/or other materials provided with the distribution.
|
26
|
+
|
27
|
+
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
28
|
+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
29
|
+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
30
|
+
COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
31
|
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
32
|
+
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
33
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
34
|
+
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
35
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
36
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/HISTORY.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# Bang! Bang!
|
2
|
+
|
3
|
+
[Website](http://rubyworks.github.com/bang) /
|
4
|
+
[Report Issue](http://github.com/rubyworks/bang/issues) /
|
5
|
+
[Mailing List](http://groups.google.com/groups/rubyworks-mailinglist) /
|
6
|
+
[Source Code](http://github.com/rubyworks/bang) /
|
7
|
+
IRC #rubyworks
|
8
|
+
|
9
|
+
[![Build Status](https://secure.travis-ci.org/rubyworks/bang.png)](http://travis-ci.org/rubyworks/bang)
|
10
|
+
|
11
|
+
|
12
|
+
## Description
|
13
|
+
|
14
|
+
Bang! Bang! is an assertions framework with a very clever design that translates
|
15
|
+
any bang call, e.g. `#foo!` into an assertion based on the corresponding query
|
16
|
+
call, `#foo?` (if it exists). In practice the framework is similar to MiniTest's
|
17
|
+
spec expectation methods, e.g. `#must_equal`, but the dynamic nature of Bang!
|
18
|
+
Bang! makes it much more flexible, as it is not limited to a finite set of
|
19
|
+
assertion methods.
|
20
|
+
|
21
|
+
It's also pretty interesting idea that bang methods are asseriton methods.
|
22
|
+
In usual Ruby code, bang methods ususually aren't particularly necessary and
|
23
|
+
could just as well be handled by non-bang methods, e.g. `#update` vs `#merge!`.
|
24
|
+
|
25
|
+
|
26
|
+
## Instruction
|
27
|
+
|
28
|
+
Usage is pretty straight forward.
|
29
|
+
|
30
|
+
require 'bang'
|
31
|
+
|
32
|
+
"This string".equals!("That string") #=> raises Bang::Assertion
|
33
|
+
|
34
|
+
To use Bang! Bang! most effectively with common test frameworks, you may need
|
35
|
+
to load an adapter to ensure the framework recognizes the assertions as
|
36
|
+
such rather than as ordinary errors.
|
37
|
+
|
38
|
+
For MiniTest use:
|
39
|
+
|
40
|
+
require 'bang/minitest'`
|
41
|
+
|
42
|
+
For TestUnit use:
|
43
|
+
|
44
|
+
require 'bang/testunit'
|
45
|
+
|
46
|
+
An RSpec adapter is in the works.
|
47
|
+
|
48
|
+
Cucumber does not require an adapter as it does not differntiate errors
|
49
|
+
from assertions.
|
50
|
+
|
51
|
+
Note, these adapters simply require the `brass/adapters/minitest` and
|
52
|
+
`brass/adapters/testunit` respecitvely along with `bang`. So that's another
|
53
|
+
way to do it too.
|
54
|
+
|
55
|
+
|
56
|
+
## Copyrights
|
57
|
+
|
58
|
+
Bang Bang is Copyright (c) 2012 Rubyworks
|
59
|
+
|
60
|
+
You can redistribute it in accordance to the *BSD-2-Clause* license.
|
61
|
+
|
62
|
+
Please see the included COPYING.md file for license details.
|
63
|
+
|
data/lib/bang.rb
ADDED
@@ -0,0 +1,350 @@
|
|
1
|
+
module Bang
|
2
|
+
|
3
|
+
if RUBY_VERSION < '1.9'
|
4
|
+
require 'bang/version'
|
5
|
+
else
|
6
|
+
require_relative 'bang/version'
|
7
|
+
end
|
8
|
+
|
9
|
+
# Bang's assertion class. Follows standard set by Brass project,
|
10
|
+
# defining `#assertion?` method which return `true`.
|
11
|
+
#
|
12
|
+
class Assertion < ::Exception
|
13
|
+
|
14
|
+
#
|
15
|
+
# Price together an Assetion error give the message used to
|
16
|
+
# cause the assertion failure.
|
17
|
+
#
|
18
|
+
# @return [Assertion] Assertion instance.
|
19
|
+
#
|
20
|
+
def self.piece(s, a, b, t)
|
21
|
+
e = new(message(s, *a, &b))
|
22
|
+
e.set_backtrace(t)
|
23
|
+
e
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Put together an error message representive of the assertion made.
|
28
|
+
#
|
29
|
+
# @todo Imporve this to better handle operators.
|
30
|
+
#
|
31
|
+
# @return [String] Failed assertion message.
|
32
|
+
#
|
33
|
+
def self.message(s, *a, &b)
|
34
|
+
"#{s}(%s)" % a.map{ |e| e.inspect }.join(',')
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Bang::Assertion is alwasy an assertion.
|
39
|
+
#
|
40
|
+
# @return [true] Always true.
|
41
|
+
#
|
42
|
+
def assertion?
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Mixin of Object class, will take any undefined bang method, e.g. `foo!`
|
48
|
+
# and if there is a corresponding query method, e.g. `foo?`, then it will
|
49
|
+
# utilize the query method to make an assertion.
|
50
|
+
#
|
51
|
+
module MethodMissing
|
52
|
+
|
53
|
+
#
|
54
|
+
# If missing method is a bang method, see if there is a corresponding query
|
55
|
+
# method and use that to make an assertion. Will also recognize the same
|
56
|
+
# prefixed by `not_`, e.g. `not_equal_to?`.
|
57
|
+
#
|
58
|
+
def method_missing(s, *a, &b)
|
59
|
+
return super(s, *a, &b) unless s.to_s.end_with?('!')
|
60
|
+
|
61
|
+
neg = false
|
62
|
+
name = s.to_s.chomp('!')
|
63
|
+
|
64
|
+
if name.start_with?('not_')
|
65
|
+
neg = true
|
66
|
+
name = name[4..-1]
|
67
|
+
end
|
68
|
+
|
69
|
+
meth = method("#{name}?") rescue nil
|
70
|
+
|
71
|
+
return super(s, *a, &b) unless meth
|
72
|
+
|
73
|
+
result = meth.call(*a, &b)
|
74
|
+
|
75
|
+
if !(neg ^ result)
|
76
|
+
raise Bang::Assertion.piece(s, a, b, caller)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
# Mixin for Object class that adds some very useful query methods.
|
83
|
+
#
|
84
|
+
module ObjectMixin
|
85
|
+
|
86
|
+
#
|
87
|
+
# Is `self` identical with `other`? In other words, do two variables
|
88
|
+
# reference the one and the same object.
|
89
|
+
#
|
90
|
+
# @return [true,false] Whether `self` is identical to `other`.
|
91
|
+
#
|
92
|
+
def identical?(other)
|
93
|
+
other.object_id == object_id
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# Query method for `#==`. We have to use the `_to` suffix becuase Ruby
|
98
|
+
# already defines the prepositionless term as a synonym for `#identical?`.
|
99
|
+
# (Hopefully that will change one day.)
|
100
|
+
#
|
101
|
+
# @return [true,false] Whether `self` is equal to `other`.
|
102
|
+
#
|
103
|
+
def equal_to?
|
104
|
+
other == self
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# Until we can use `#equal?`, lets make the best of it and offer
|
109
|
+
# the plural form as well.
|
110
|
+
#
|
111
|
+
alias :equals? :equal_to?
|
112
|
+
|
113
|
+
#
|
114
|
+
# Test whether `self` is like `other`. Like is broad equality
|
115
|
+
# measure testing `identical?`, `eql?`, `==` and `===`.
|
116
|
+
#
|
117
|
+
# @todo Should `like?` this include `=~` also?
|
118
|
+
#
|
119
|
+
# @return [true,false] Whether `self` is like `other`.
|
120
|
+
#
|
121
|
+
def like?(other)
|
122
|
+
other.identical?(self) ||
|
123
|
+
other.eql?(self) ||
|
124
|
+
other.==(self) ||
|
125
|
+
other.===(self)
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Test whether `other` is a case of `self` via `#===` method.
|
130
|
+
#
|
131
|
+
# @return [true,false] Whether `other` is a case of `self`.
|
132
|
+
#
|
133
|
+
def case?(other)
|
134
|
+
other === self
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# Test whether `self` matches `other` via `#=~` method.
|
139
|
+
#
|
140
|
+
# @return [true,false] Whether `self` matches `other`.
|
141
|
+
#
|
142
|
+
def match?(other)
|
143
|
+
other =~ self
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
# Test whether `self` is the `true` instance.
|
148
|
+
#
|
149
|
+
# @return [true,false] Whether `self` is `true`.
|
150
|
+
#
|
151
|
+
def true?
|
152
|
+
TrueClass === self
|
153
|
+
end
|
154
|
+
|
155
|
+
#
|
156
|
+
# Test whether `self` is the `false` instance.
|
157
|
+
#
|
158
|
+
# @return [true,false] Whether `self` is `false`.
|
159
|
+
#
|
160
|
+
def false?
|
161
|
+
FalseClass === self
|
162
|
+
end
|
163
|
+
|
164
|
+
#
|
165
|
+
# Yield the given block and return `true` if the `self` is throw,
|
166
|
+
# otherwise `false`.
|
167
|
+
#
|
168
|
+
# @return [true,false] Whether `self` was thrown.
|
169
|
+
#
|
170
|
+
def thrown?(&block)
|
171
|
+
pass = true
|
172
|
+
catch(self) do
|
173
|
+
begin
|
174
|
+
yield
|
175
|
+
rescue ArgumentError => err # 1.9 exception
|
176
|
+
#msg += ", not #{err.message.split(/ /).last}"
|
177
|
+
rescue NameError => err # 1.8 exception
|
178
|
+
#msg += ", not #{err.name.inspect}"
|
179
|
+
end
|
180
|
+
pass = false
|
181
|
+
end
|
182
|
+
pass
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# Yield block and return true if it runs without exception and does not
|
187
|
+
# return `nil` or `false`.
|
188
|
+
#
|
189
|
+
# @return [true,false] True if block succeeds, otherwise false.
|
190
|
+
#
|
191
|
+
def satisfy?(&block)
|
192
|
+
begin
|
193
|
+
block.call(self)
|
194
|
+
rescue
|
195
|
+
false
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
# Mixin for Numeric class that adds `#within?` and `#close?`.
|
202
|
+
#
|
203
|
+
module NumericMixin
|
204
|
+
|
205
|
+
#
|
206
|
+
# Is this value within a given absolute `delta` of another?
|
207
|
+
#
|
208
|
+
# @return [true,false] True if within absolute delta, otherwise false.
|
209
|
+
#
|
210
|
+
def within?(other, delta)
|
211
|
+
a, b, d = self.to_f, other.to_f, delta.to_f
|
212
|
+
|
213
|
+
(b - d) <= a && (b + d) >= a
|
214
|
+
end
|
215
|
+
|
216
|
+
#
|
217
|
+
# Is this value within a given relative `epsilon` of another?
|
218
|
+
#
|
219
|
+
# @return [true,false] True if within relative epsilon, otherwise false.
|
220
|
+
#
|
221
|
+
def close?(other, epsilon)
|
222
|
+
a, b, e = self.to_f, other.to_f, epsilon.to_f
|
223
|
+
|
224
|
+
d = b * e
|
225
|
+
|
226
|
+
(b - d) <= a && (b + d) >= a
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
# Mixin for Proc class that adds `#raises?`, `#rescues?` and `#throws?`.
|
232
|
+
#
|
233
|
+
module ProcMixin
|
234
|
+
|
235
|
+
#
|
236
|
+
# Execute this procedure and return `true` if the specific given `exception`
|
237
|
+
# is raised, otherwise `false`.
|
238
|
+
#
|
239
|
+
# @return [true,false] Whether exception was raised.
|
240
|
+
#
|
241
|
+
def raises?(exception)
|
242
|
+
begin
|
243
|
+
call
|
244
|
+
false
|
245
|
+
rescue exception => err
|
246
|
+
exception == err.class
|
247
|
+
rescue Exception => err
|
248
|
+
false
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
#
|
253
|
+
# Execute this procedure and return `true` if the given `exception`,
|
254
|
+
# or subclass there-of is raised, otherwise `false`.
|
255
|
+
#
|
256
|
+
# @return [true,false] Whether exception was rescued.
|
257
|
+
#
|
258
|
+
def rescues?(exception)
|
259
|
+
begin
|
260
|
+
call
|
261
|
+
false
|
262
|
+
rescue exception => err
|
263
|
+
true
|
264
|
+
rescue Exception => err
|
265
|
+
false
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
#
|
270
|
+
# Execute this procedure and return `true` if the given `object`,
|
271
|
+
# is thrown, otherwise `false`.
|
272
|
+
#
|
273
|
+
# @return [true,false] Whether object was thrown.
|
274
|
+
#
|
275
|
+
def throws?(object)
|
276
|
+
pass = true
|
277
|
+
catch(object) do
|
278
|
+
begin
|
279
|
+
call
|
280
|
+
rescue ArgumentError => err # 1.9 exception
|
281
|
+
#msg += ", not #{err.message.split(/ /).last}"
|
282
|
+
rescue NameError => err # 1.8 exception
|
283
|
+
#msg += ", not #{err.name.inspect}"
|
284
|
+
end
|
285
|
+
pass = false
|
286
|
+
end
|
287
|
+
pass
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
# Class-level extension for Exception class that adds `#raised?` and `#rescued?`.
|
293
|
+
#
|
294
|
+
module ExceptionExtension
|
295
|
+
|
296
|
+
#
|
297
|
+
# Yield a given block and return `true` if this exception specifically
|
298
|
+
# is raised, otherwise `false`.
|
299
|
+
#
|
300
|
+
# @return [true,false] Whether exception is raised.
|
301
|
+
#
|
302
|
+
def raised? #:yield:
|
303
|
+
begin
|
304
|
+
yield
|
305
|
+
false
|
306
|
+
rescue self => err
|
307
|
+
self == err.class
|
308
|
+
rescue Exception
|
309
|
+
false
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
#
|
314
|
+
# Yield a given block and return `true` if this exception, or a sub-class
|
315
|
+
# there-of is raised, otherwise `false`.
|
316
|
+
#
|
317
|
+
# @return [true,false] Whether exception is rescued.
|
318
|
+
#
|
319
|
+
def rescued? #:yield:
|
320
|
+
begin
|
321
|
+
yield
|
322
|
+
false
|
323
|
+
rescue self
|
324
|
+
true
|
325
|
+
rescue Exception
|
326
|
+
false
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
|
334
|
+
class Object
|
335
|
+
include Bang::MethodMissing
|
336
|
+
include Bang::ObjectMixin
|
337
|
+
end
|
338
|
+
|
339
|
+
class Proc
|
340
|
+
include Bang::ProcMixin
|
341
|
+
end
|
342
|
+
|
343
|
+
class Numeric
|
344
|
+
include Bang::NumericMixin
|
345
|
+
end
|
346
|
+
|
347
|
+
class Exception
|
348
|
+
extend Bang::ExceptionExtension
|
349
|
+
end
|
350
|
+
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bang
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Thomas Sawyer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-09 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: brass
|
16
|
+
requirement: &23786500 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *23786500
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: detroit
|
27
|
+
requirement: &23785960 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *23785960
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: qed
|
38
|
+
requirement: &23785460 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *23785460
|
47
|
+
description: Bang! Bang! provides a dynamic assertions framework utlizing bang methods
|
48
|
+
and built to BRASS standards.
|
49
|
+
email:
|
50
|
+
- transfire@gmail.com
|
51
|
+
executables: []
|
52
|
+
extensions: []
|
53
|
+
extra_rdoc_files:
|
54
|
+
- COPYING.md
|
55
|
+
- HISTORY.md
|
56
|
+
- README.md
|
57
|
+
files:
|
58
|
+
- lib/bang/minitest.rb
|
59
|
+
- lib/bang/testunit.rb
|
60
|
+
- lib/bang.rb
|
61
|
+
- spec/applique/brass.rb
|
62
|
+
- COPYING.md
|
63
|
+
- HISTORY.md
|
64
|
+
- README.md
|
65
|
+
homepage: http://rubyworks.github.com/bang
|
66
|
+
licenses:
|
67
|
+
- BSD-2-Clause
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.8.11
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Bang methods for assertions!
|
90
|
+
test_files: []
|