speck 1
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.
- checksums.yaml +15 -0
- data/LICENSE.text +16 -0
- data/README.markdown +82 -0
- data/lib/speck.rb +143 -0
- data/lib/speck/battery.rb +28 -0
- data/lib/speck/check.rb +43 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NTcxNDI0MDBkZDlkYWQ4OGJjOGI2N2RhYzQwM2Y5NDEyNmQzN2QyNw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OTM5MTY0MDJjZDZiNDhlNmI3OWZlMmQ0M2FkNTBmMTgwODFkYThjYw==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NzVhMWIzMjk3NDU4ZTg5MGRlOWRkMzI1ODhiZDVlMzI1YzA0Y2ZmZmU1MTkz
|
10
|
+
MjVmMjBmMWRjNjBlOTkxZDMwNzI0MWFmOGQ4MDIwNzVhYTlhYTMyOTI4OTQ0
|
11
|
+
OTNiNjhlZDI0ZGYzOTU2NGE1YmUwYzRjMGJmMGMxNjI3NjU3Y2E=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MzIyODQ4MGZlYzRlNTNkMWU4MmRiYWJhZDkzYmE5NGZmN2U3ODVjNjI0Y2U0
|
14
|
+
N2Q1ZjJhNmZhMDU5OWYxMzZkMmVkYmYzZGUzMThjMDFkYzc4NmY5YTIxZGMw
|
15
|
+
ZTdhMjc5YmQ4YTU4NGJlZWQ2NjBmM2IzZjNlNWE2OWY2MjA0NGE=
|
data/LICENSE.text
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Copyright (C) 2013 elliottcable
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
4
|
+
associated documentation files (the "Software"), to deal in the Software without restriction,
|
5
|
+
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
6
|
+
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
7
|
+
furnished to do so, subject to the following conditions:
|
8
|
+
|
9
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial
|
10
|
+
portions of the Software.
|
11
|
+
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
13
|
+
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
14
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
15
|
+
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
16
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
Speck
|
2
|
+
=====
|
3
|
+
This is a super-light library that attempts to enable a “literate” form of code-testing. This
|
4
|
+
library is a part of a suite of tools intended to be used together; alongside its
|
5
|
+
[brethren](#suite), it allows for “literate” specifications that look something like this:
|
6
|
+
|
7
|
+
Speck.new Dog do
|
8
|
+
some_breed = Dog::Shetland
|
9
|
+
|
10
|
+
Dog.new.check { it.breed == Dog::Labrador }
|
11
|
+
Dog.new(a_breed).check { it.breed == a_breed }
|
12
|
+
|
13
|
+
Speck.new Dog.class_method :bark do
|
14
|
+
a_dog = Dog.new
|
15
|
+
a_dog.bark.check { |rv| rv == 'arf' }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Ideas, feature requests, and bugs can be reported on the [GitHub][] issue tracker:
|
20
|
+
|
21
|
+
<http://github.com/elliottcable/Speck/issues>
|
22
|
+
|
23
|
+
[GitHub]: http://github.com/
|
24
|
+
|
25
|
+
Philosophy
|
26
|
+
----------
|
27
|
+
If you'll pay attention to the above example, you'll notice we do things a little differently around
|
28
|
+
here. The conventions that Speck is designed to enable include:
|
29
|
+
|
30
|
+
- first and foremost, we want the specification to be *source-code*, that is, we want it to read
|
31
|
+
like any Ruby code anywhere else. Our ‘checks’ (called assertions in other environments) simply
|
32
|
+
boil down to a `target` and a block to execute *against* that target. This is intended to be very
|
33
|
+
idiomatic Ruby-style code.
|
34
|
+
- secondary to the former, we also like it when the checks themselves read a bit like an English
|
35
|
+
sentence. For example, Slack ([see below](#suite)) uses the [it][] library to make the blocks look
|
36
|
+
a little more readable in the usual case.
|
37
|
+
- this is especially powerful, because Spark extracts the source-code responsible for each check,
|
38
|
+
and hilights it based on the execution of that check. Thus, specification code itself is self-
|
39
|
+
documenting output for your test suite.
|
40
|
+
- related to that, we tend to abstract the specifics of each check into variables, named in such a
|
41
|
+
way as to express the generic form of that specific element. If we're testing that a dog takes a
|
42
|
+
breed, but not *which* breed it takes, we place a relevant breed into `some_breed`. Similarly, we
|
43
|
+
might put an arbitrary numeric argument into a variable named `some_number` instead of using the
|
44
|
+
developer standards of `42` or `1337`. If, however, we were testing specific functionality related
|
45
|
+
to handling of the magic number `2`, as opposed to `1` or `3`, then we'd write the `2` directly
|
46
|
+
into the check-line.
|
47
|
+
- our specification sections directly include *what* they're testing, instead of a description
|
48
|
+
thereof in a string. Whether this is an class, an instance method, or something else entirely.
|
49
|
+
|
50
|
+
Generally speaking, we try to write *code* that self-describes what it's testing, instead of writing
|
51
|
+
lots of english sentences in a DSL to redundantly describe the code being tested. (Lookin' at you,
|
52
|
+
RSpec 'n friends.)
|
53
|
+
|
54
|
+
[it]: <http://github.com/elliottcable/it> "A sneakly library to expose iterated elements beautifully"
|
55
|
+
|
56
|
+
Suite
|
57
|
+
-----
|
58
|
+
I've intentionally designed Speck to be tiny and modular. Speck itself, here, is three extremely
|
59
|
+
brief files. To do more interesting things, and make specks easier to write, I've provided several
|
60
|
+
“sister libraries” that expand upon what Speck itself does:
|
61
|
+
|
62
|
+
- **[Slack][]**: Provides convenience methods for creating specks directly from various objects and
|
63
|
+
values, and various other comparators and tools to make *writing* specks easier.
|
64
|
+
- **[Spark][]**: Provides a [Rake][] task and other tools that beautifully *runs* your specks.
|
65
|
+
- **[Smock][]**: Provides a “mocking and spying” toolkit, for testing intricate code that you can't
|
66
|
+
easily decouple enough to test in minutae.
|
67
|
+
|
68
|
+
In no way are any of these *required* to use Speck, nor do any of them inter-depend upon eachother.
|
69
|
+
Mix-and-match as you desire. Or, y'know, write your own.
|
70
|
+
|
71
|
+
[Slack]: <http://github.com/elliottcable/slack>
|
72
|
+
[Spark]: <http://github.com/elliottcable/spark>
|
73
|
+
[Smock]: <http://github.com/elliottcable/smock>
|
74
|
+
|
75
|
+
[Rake]: <http://rake.rubyforge.org> "A Ruby DSL for `make`-like project tasks"
|
76
|
+
|
77
|
+
License
|
78
|
+
-------
|
79
|
+
This project is licensed very openly for your use, under a variation of the ‘MIT license.’
|
80
|
+
Details are available in [LICENSE][].
|
81
|
+
|
82
|
+
[LICENSE]: <./blob/master/LICENSE.text>
|
data/lib/speck.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# All library files are required at the bottom, because in this unique case we
|
2
|
+
# need `Speck` defined before we can use it to `Speck` anything.
|
3
|
+
|
4
|
+
class Speck
|
5
|
+
VERSION = 1
|
6
|
+
|
7
|
+
class <<self
|
8
|
+
|
9
|
+
##
|
10
|
+
# All specks not bound to an environment
|
11
|
+
attr_accessor :unbound
|
12
|
+
def unbound; @unbound ||= Array.new; end
|
13
|
+
|
14
|
+
# The current `Speck` execution stack
|
15
|
+
#
|
16
|
+
# @see #current
|
17
|
+
attr_accessor :stack
|
18
|
+
def stack; @stack ||= Array.new; end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Returns the top `Speck` on the execution stack (the one currently in the
|
22
|
+
# process of executing)
|
23
|
+
#
|
24
|
+
# When your `Speck`s are being run, there is a `stack` of `Speck` objects,
|
25
|
+
# consisting of the current nesting list of `Speck`s being run.
|
26
|
+
def current
|
27
|
+
stack.last
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Retreives the `Speck`s defiend for a given object, or, if none are
|
32
|
+
# defined, creates a new (empty) `Speck` for it.
|
33
|
+
#
|
34
|
+
# It’s worth noting that `Module#instance_method` returns a new
|
35
|
+
# `UnboundMethod` object every time you call it, even for the same method…
|
36
|
+
# so you can’t retreive Specks assigned to `UnboundMethods` via
|
37
|
+
# `Module#instance_method` with this method.
|
38
|
+
def for object
|
39
|
+
specks = Speck::on object
|
40
|
+
specks << Speck.new(object) if specks.empty?
|
41
|
+
return specks
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Functions like `Speck::for`, without creating a new `Speck` if none are
|
46
|
+
# defined.
|
47
|
+
#
|
48
|
+
# @see `Speck::for`
|
49
|
+
def on object
|
50
|
+
object.instance_variable_get(NinjaVar) ||
|
51
|
+
object.instance_variable_set(NinjaVar, Array.new)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# This instance variable will be set on target objects to point to the
|
58
|
+
# specks for that object
|
59
|
+
NinjaVar = :@_specks_
|
60
|
+
|
61
|
+
##
|
62
|
+
# The block to be executed
|
63
|
+
attr_accessor :block
|
64
|
+
|
65
|
+
##
|
66
|
+
# `Speck`s which consider this `Speck` to be their environment
|
67
|
+
attr_accessor :children
|
68
|
+
def children; @children ||= Array.new; end
|
69
|
+
|
70
|
+
##
|
71
|
+
# The `environment` of a `Speck` is another `Speck`, describing some sort of
|
72
|
+
# parent. The environment of a `Speck` describing an `UnboundMethod`, for
|
73
|
+
# instance, would most likely be a `Speck` describing a `Module` or `Class`
|
74
|
+
# on which that method is defined
|
75
|
+
attr_accessor :environment
|
76
|
+
def environment= object
|
77
|
+
(@environment ? @environment.children : Speck.unbound).delete self
|
78
|
+
|
79
|
+
speck = object.is_a?(Speck) || object.nil? ?
|
80
|
+
object : Speck::for(object).first
|
81
|
+
@environment = speck
|
82
|
+
|
83
|
+
(@environment ? @environment.children : Speck.unbound) << self
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# The checks involved in the current `Speck`
|
88
|
+
attr_accessor :checks
|
89
|
+
def checks; @checks ||= Array.new; end
|
90
|
+
|
91
|
+
##
|
92
|
+
# The `target` of a speck is usually the object which it is intended to
|
93
|
+
# describe (and test) the functionality of (Usually, this will be an
|
94
|
+
# instance of `Class`, `Module`, `Method` for “class” methods, or
|
95
|
+
# `UnboundMethod` for instance methods)
|
96
|
+
attr_accessor :target
|
97
|
+
def target= object
|
98
|
+
Speck::on(@target).delete self if @target and Speck::on(@target).include? self
|
99
|
+
|
100
|
+
@target = object
|
101
|
+
|
102
|
+
Speck::on(@target) << self if @target
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Creates a new `Speck`.
|
107
|
+
def initialize *environment, &block
|
108
|
+
self.target = environment.pop
|
109
|
+
|
110
|
+
environment = environment.inject do |prev, curr|
|
111
|
+
raise Exception::EnvironmentConflict if Speck::for(curr).first.environment and Speck::for(curr).first.environment != Speck::for(prev).first
|
112
|
+
Speck::for(curr).first.environment = Speck::for(prev).first
|
113
|
+
curr
|
114
|
+
end
|
115
|
+
|
116
|
+
self.environment = environment ? Speck::for(environment).first : Speck.current
|
117
|
+
@block = block || lambda {}
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# Executes the `Speck`.
|
122
|
+
def execute
|
123
|
+
Speck.stack << self
|
124
|
+
@block.call
|
125
|
+
Speck.stack.pop
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
##
|
130
|
+
# A root class, and container class, for `Speck` exceptions.
|
131
|
+
class Exception < StandardError
|
132
|
+
# Raised when a `Check` fails
|
133
|
+
CheckFailed = Class.new self
|
134
|
+
|
135
|
+
# Raised when you attempt to stack an environment contrary to the existing
|
136
|
+
# environment
|
137
|
+
EnvironmentConflict = Class.new self
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
require 'speck/battery'
|
143
|
+
require 'speck/check'
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Speck
|
2
|
+
##
|
3
|
+
# A `Battery` of `Speck`s is a set of specks which should be executed
|
4
|
+
# together, and which interdepend upon other `Speck`s in the battery.
|
5
|
+
# `Batteries` are recursive structures; that is, a `Battery` may have sub–
|
6
|
+
# `Batteries` for relevant objects.
|
7
|
+
class Battery
|
8
|
+
|
9
|
+
attr_reader :specks
|
10
|
+
attr_reader :targets
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@specks = Array.new
|
14
|
+
@targets = Hash.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def << speck
|
18
|
+
@specks << speck
|
19
|
+
@targets[speck.target] ||= Battery.new
|
20
|
+
return self
|
21
|
+
end
|
22
|
+
|
23
|
+
def [] object
|
24
|
+
return @targets[object]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/lib/speck/check.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
class Speck
|
2
|
+
##
|
3
|
+
# Represents a queued thing to be checked of some sort, within a `Speck`.
|
4
|
+
class Check
|
5
|
+
##
|
6
|
+
# The block to be executed, determining the success or failure of this
|
7
|
+
# particular `Check`. If it accepts an argument, the result of the target
|
8
|
+
# block will be passed as that argument.
|
9
|
+
attr_accessor :expectation
|
10
|
+
|
11
|
+
##
|
12
|
+
# The `target` of a `Check` is a block that returns an object to be passed
|
13
|
+
# to the `expectation`. It represents the object that the `Check` is
|
14
|
+
# intended to, well, check.
|
15
|
+
attr_accessor :target
|
16
|
+
|
17
|
+
##
|
18
|
+
# The status of the `Check`. `nil` indicates the `Check` hasn’t been
|
19
|
+
# executed, and `true` or `false` indicate the success of the latest
|
20
|
+
# execution
|
21
|
+
attr_accessor :status
|
22
|
+
def passed?
|
23
|
+
status == :passed
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(target = nil, &expectation)
|
27
|
+
@target = target.respond_to?(:call) ? target : ->{target}
|
28
|
+
@expectation = expectation
|
29
|
+
Speck.current.checks << self if Speck.current
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Executes this `Check`, raising an error if the expectation returns nil
|
34
|
+
# or false.
|
35
|
+
def execute
|
36
|
+
call = @expectation.arity == 0 ? ->{@target.call; @expectation.call} : ->{@expectation[@target.call]}
|
37
|
+
@status = call.call ? :passed : :failed
|
38
|
+
raise Exception::CheckFailed unless passed?
|
39
|
+
return self
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: speck
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- elliottcable [http://ell.io/tt]
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-02-27 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/speck/battery.rb
|
20
|
+
- lib/speck/check.rb
|
21
|
+
- lib/speck.rb
|
22
|
+
- README.markdown
|
23
|
+
- LICENSE.text
|
24
|
+
homepage: http://github.com/elliottcable/speck
|
25
|
+
licenses:
|
26
|
+
- MIT
|
27
|
+
metadata: {}
|
28
|
+
post_install_message:
|
29
|
+
rdoc_options: []
|
30
|
+
require_paths:
|
31
|
+
- lib
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ! '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ! '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
requirements: []
|
43
|
+
rubyforge_project:
|
44
|
+
rubygems_version: 2.0.0
|
45
|
+
signing_key:
|
46
|
+
specification_version: 4
|
47
|
+
summary: ! 'An (extremely light) source-code literate code-specification and -testing
|
48
|
+
system. (see: spark, slack)'
|
49
|
+
test_files: []
|
50
|
+
has_rdoc:
|