for_each_row 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/LICENSE +21 -0
- data/Rakefile +22 -0
- data/Readme.markdown +102 -0
- data/VERSION.yml +4 -0
- data/examples/detonator/lib/detonator.rb +20 -0
- data/examples/detonator/spec/fer_detonator_spec.rb +31 -0
- data/examples/detonator/spec/spec_helper.rb +11 -0
- data/examples/detonator/spec/wordy_detonator_spec.rb +30 -0
- data/for_each_row.gemspec +28 -0
- data/lib/for_each_row.rb +18 -0
- data/spec/for_each_row_spec.rb +42 -0
- metadata +70 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Portions copyright (c) 2009 Drop.io, Inc.
|
2
|
+
Portions copyright (c) 2009 Peter Jaros
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
6
|
+
t.warning = true
|
7
|
+
end
|
8
|
+
rescue LoadError
|
9
|
+
STDERR.puts "RSpec not found. Install it to run specs."
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'jeweler'
|
14
|
+
Jeweler::Tasks.new do |s|
|
15
|
+
s.name = "for_each_row"
|
16
|
+
s.summary = s.description = "A method for specifying Fit-esque tables in Ruby"
|
17
|
+
s.authors = ["Peter Jaros"]
|
18
|
+
s.email = "peter.a.jaros@gmail.com"
|
19
|
+
end
|
20
|
+
rescue LoadError
|
21
|
+
STDERR.puts "Jeweler not available. To release, install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
22
|
+
end
|
data/Readme.markdown
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
`for_each_row`: Tables for your specs
|
2
|
+
=====================================
|
3
|
+
|
4
|
+
This gem provides a single method, `#for_each_row`, which lets you run a block with the values from a table. It's ideal for specs or tests (though in theory it could be used for anything). It looks like this:
|
5
|
+
|
6
|
+
for_each_row <<-TABLE do |number1, number2, sum|
|
7
|
+
|1, 2, 3 |
|
8
|
+
|5, 7, 12 |
|
9
|
+
TABLE
|
10
|
+
(number2 + number2).should == sum
|
11
|
+
end
|
12
|
+
|
13
|
+
Getting it
|
14
|
+
----------
|
15
|
+
|
16
|
+
First, install the gem
|
17
|
+
|
18
|
+
sudo gem install Peeja-for_each_row -s http://gems.github.com"
|
19
|
+
|
20
|
+
Then include the module in your spec or test environment. For RSpec, this looks like:
|
21
|
+
|
22
|
+
Spec::Runner.configure do |config|
|
23
|
+
config.include ForEachRow
|
24
|
+
end
|
25
|
+
|
26
|
+
I'm not sure what the official way to do this in Test::Unit is, since I don't use it, but you'll figure it out. You're a smart one, you.
|
27
|
+
|
28
|
+
|
29
|
+
Example
|
30
|
+
-------
|
31
|
+
|
32
|
+
A good spec is not written to be read by Ruby. A good spec is written to be read by people. Specs are not merely tests; they are a tool for communicating with other developers. A good test exercises the full behavior of an object. A good spec also explains the behavior.
|
33
|
+
|
34
|
+
Sometimes, though, a spec has to get wordy to be complete. For instance, let's say we're spec'ing a bomb detonator. The detonator has two keys which need to be turned and a safety which needs to be turned off for the big red button to function. The spec looks something like this (full spec in `examples/detonator`):
|
35
|
+
|
36
|
+
describe Detonator do
|
37
|
+
before(:each) do
|
38
|
+
@detonator = Detonator.new
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should not work if the first key hasn't been turned" do
|
42
|
+
@detonator.turn_key(2)
|
43
|
+
lambda { @detonator.push_big_red_button }.should_not raise_error
|
44
|
+
end
|
45
|
+
|
46
|
+
# ...
|
47
|
+
|
48
|
+
it "should work if both keys have been turned and the safety is turned off" do
|
49
|
+
@detonator.turn_key(1)
|
50
|
+
@detonator.turn_key(2)
|
51
|
+
@detonator.turn_off_safety
|
52
|
+
lambda { @detonator.push_big_red_button }.should raise_error(Explosion)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
We're doing the same thing over and over again. It would be nice to be able to express all of this in a single table, where each row describes a case. That's what `for_each_row` is for. Here's the same thing, using `for_each_row`:
|
57
|
+
|
58
|
+
describe Detonator do
|
59
|
+
|
60
|
+
# Note that this table checks more cases than wordy_detonator_spec.rb.
|
61
|
+
|
62
|
+
it "should only work if both keys have been turned and the safety is turned off" do
|
63
|
+
for_each_row <<-TABLE do |turned_key1, turned_key2, safety_on, explosion|
|
64
|
+
|false, false, true, false |
|
65
|
+
|true, false, true, false |
|
66
|
+
|false, true, true, false |
|
67
|
+
|true, true, true, false |
|
68
|
+
|false, false, false, false |
|
69
|
+
|true, false, false, false |
|
70
|
+
|false, true, false, false |
|
71
|
+
|true, true, false, true |
|
72
|
+
TABLE
|
73
|
+
detonator = Detonator.new
|
74
|
+
|
75
|
+
detonator.turn_key(1) if turned_key1
|
76
|
+
detonator.turn_key(2) if turned_key2
|
77
|
+
detonator.turn_off_safety unless safety_on
|
78
|
+
|
79
|
+
if explosion
|
80
|
+
lambda { detonator.push_big_red_button }.should raise_error(Explosion)
|
81
|
+
else
|
82
|
+
lambda { detonator.push_big_red_button }.should_not raise_error(Explosion)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
"So this is always more readable than writing out the cases?"
|
90
|
+
-------------------------------------------------------------
|
91
|
+
|
92
|
+
Almost certainly not. This isn't math, this is writing. Every spec and example is a little different, and you'll have to decide what reads better. Whatever is clearest in any particular case is the way to go. This is just another tool in your belt. To be honest, having just written the the thing, I don't know how useful it is yet. But it seems like it's worth trying out.
|
93
|
+
|
94
|
+
|
95
|
+
Credits
|
96
|
+
-------
|
97
|
+
|
98
|
+
Written by Peter Jaros
|
99
|
+
Portions © 2009 Peter Jaros
|
100
|
+
Portions © 2009 drop.io, Inc.
|
101
|
+
|
102
|
+
Released under the MIT License. See `LICENSE` for details.
|
data/VERSION.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
class Explosion < Exception; end
|
2
|
+
|
3
|
+
class Detonator
|
4
|
+
def initialize
|
5
|
+
@keys = {}
|
6
|
+
@safety_on = true
|
7
|
+
end
|
8
|
+
|
9
|
+
def turn_key(key_num)
|
10
|
+
@keys[key_num] = true
|
11
|
+
end
|
12
|
+
|
13
|
+
def turn_off_safety
|
14
|
+
@safety_on = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def push_big_red_button
|
18
|
+
raise Explosion if @keys[1] and @keys[2] and not @safety_on
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
describe Detonator do
|
4
|
+
|
5
|
+
# Note that this table checks more cases than wordy_detonator_spec.rb.
|
6
|
+
|
7
|
+
it "should only work if both keys have been turned and the safety is turned off" do
|
8
|
+
for_each_row <<-TABLE do |turned_key1, turned_key2, safety_on, explosion|
|
9
|
+
|false, false, true, false |
|
10
|
+
|true, false, true, false |
|
11
|
+
|false, true, true, false |
|
12
|
+
|true, true, true, false |
|
13
|
+
|false, false, false, false |
|
14
|
+
|true, false, false, false |
|
15
|
+
|false, true, false, false |
|
16
|
+
|true, true, false, true |
|
17
|
+
TABLE
|
18
|
+
detonator = Detonator.new
|
19
|
+
|
20
|
+
detonator.turn_key(1) if turned_key1
|
21
|
+
detonator.turn_key(2) if turned_key2
|
22
|
+
detonator.turn_off_safety unless safety_on
|
23
|
+
|
24
|
+
if explosion
|
25
|
+
lambda { detonator.push_big_red_button }.should raise_error(Explosion)
|
26
|
+
else
|
27
|
+
lambda { detonator.push_big_red_button }.should_not raise_error(Explosion)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
describe Detonator do
|
4
|
+
before(:each) do
|
5
|
+
@detonator = Detonator.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should not work if the first key hasn't been turned" do
|
9
|
+
@detonator.turn_key(2)
|
10
|
+
lambda { @detonator.push_big_red_button }.should_not raise_error
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should not work if the second key hasn't been turned" do
|
14
|
+
@detonator.turn_key(1)
|
15
|
+
lambda { @detonator.push_big_red_button }.should_not raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should not work if both keys have been turned but the safety is still on" do
|
19
|
+
@detonator.turn_key(1)
|
20
|
+
@detonator.turn_key(2)
|
21
|
+
lambda { @detonator.push_big_red_button }.should_not raise_error
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should work if both keys have been turned and the safety is turned off" do
|
25
|
+
@detonator.turn_key(1)
|
26
|
+
@detonator.turn_key(2)
|
27
|
+
@detonator.turn_off_safety
|
28
|
+
lambda { @detonator.push_big_red_button }.should raise_error(Explosion)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{for_each_row}
|
5
|
+
s.version = "0.0.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Peter Jaros"]
|
9
|
+
s.date = %q{2009-02-03}
|
10
|
+
s.description = %q{A method for specifying Fit-esque tables in Ruby}
|
11
|
+
s.email = %q{peter.a.jaros@gmail.com}
|
12
|
+
s.files = ["Readme.markdown", "VERSION.yml", "lib/for_each_row.rb", "spec/for_each_row_spec.rb", "spec/spec.opts"]
|
13
|
+
s.has_rdoc = true
|
14
|
+
s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
s.rubygems_version = %q{1.3.1}
|
17
|
+
s.summary = %q{A method for specifying Fit-esque tables in Ruby}
|
18
|
+
|
19
|
+
if s.respond_to? :specification_version then
|
20
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
21
|
+
s.specification_version = 2
|
22
|
+
|
23
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
24
|
+
else
|
25
|
+
end
|
26
|
+
else
|
27
|
+
end
|
28
|
+
end
|
data/lib/for_each_row.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module ForEachRow
|
2
|
+
# Example:
|
3
|
+
# for_each_row <<-TABLE do |number1, number2, sum|
|
4
|
+
# |1, 2, 3 |
|
5
|
+
# |5, 7, 12 |
|
6
|
+
# TABLE
|
7
|
+
# (number2 + number2).should == sum
|
8
|
+
# end
|
9
|
+
|
10
|
+
def for_each_row(table, &block)
|
11
|
+
table.split("\n").each do |row|
|
12
|
+
if row.strip =~ /^\|(.*)\|$/
|
13
|
+
cell_values = $1.split(",").map { |cell| eval(cell, block) }
|
14
|
+
block.call *cell_values
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
$: << File.dirname(__FILE__) + "/../lib"
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'spec'
|
5
|
+
require 'for_each_row'
|
6
|
+
|
7
|
+
describe ForEachRow do
|
8
|
+
include ForEachRow
|
9
|
+
|
10
|
+
it "should call the block once with each row" do
|
11
|
+
should_receive(:called_with).once.with("foo", "bar", "foobar")
|
12
|
+
should_receive(:called_with).once.with("baz", "bax", "bazbax")
|
13
|
+
|
14
|
+
for_each_row <<-TABLE do |string1, string2, full_string|
|
15
|
+
|"foo", "bar", "foobar" |
|
16
|
+
|"baz", "bax", "bazbax" |
|
17
|
+
TABLE
|
18
|
+
called_with(string1, string2, full_string)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "should interpret" do
|
23
|
+
def should_interpret(from, to, some_local=nil)
|
24
|
+
should_receive(:called_with) { |value| value.should eql(to) }
|
25
|
+
|
26
|
+
for_each_row <<-TABLE do |value |
|
27
|
+
|#{from}|
|
28
|
+
TABLE
|
29
|
+
called_with(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
specify "strings" do should_interpret(%|"foo"|, "foo") end
|
34
|
+
specify "integers" do should_interpret(%|5|, 5) end
|
35
|
+
specify "floats" do should_interpret(%|5.0|, 5.0) end
|
36
|
+
specify "symbols" do should_interpret(%|:bar|, :bar) end
|
37
|
+
|
38
|
+
specify "local varables" do should_interpret(%|some_local|, :bar, :bar) end
|
39
|
+
specify "self" do should_interpret(%|self|, self) end
|
40
|
+
specify "methods of self" do should_interpret(%|object_id|, self.object_id) end
|
41
|
+
end
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: for_each_row
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Peter Jaros
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-22 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: A method for specifying Fit-esque tables in Ruby
|
17
|
+
email: peter.a.jaros@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
files:
|
25
|
+
- .gitignore
|
26
|
+
- LICENSE
|
27
|
+
- Rakefile
|
28
|
+
- Readme.markdown
|
29
|
+
- VERSION.yml
|
30
|
+
- examples/detonator/lib/detonator.rb
|
31
|
+
- examples/detonator/spec/fer_detonator_spec.rb
|
32
|
+
- examples/detonator/spec/spec_helper.rb
|
33
|
+
- examples/detonator/spec/wordy_detonator_spec.rb
|
34
|
+
- for_each_row.gemspec
|
35
|
+
- lib/for_each_row.rb
|
36
|
+
- spec/for_each_row_spec.rb
|
37
|
+
has_rdoc: true
|
38
|
+
homepage:
|
39
|
+
licenses: []
|
40
|
+
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- --charset=UTF-8
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
version:
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
requirements: []
|
59
|
+
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.3.5
|
62
|
+
signing_key:
|
63
|
+
specification_version: 3
|
64
|
+
summary: A method for specifying Fit-esque tables in Ruby
|
65
|
+
test_files:
|
66
|
+
- spec/for_each_row_spec.rb
|
67
|
+
- examples/detonator/lib/detonator.rb
|
68
|
+
- examples/detonator/spec/fer_detonator_spec.rb
|
69
|
+
- examples/detonator/spec/spec_helper.rb
|
70
|
+
- examples/detonator/spec/wordy_detonator_spec.rb
|