bang 0.1.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/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
|
+
[](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: []
|