checklist 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/.gitignore +18 -0
- data/.travis.yml +9 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/NOTES.org +15 -0
- data/README.md +45 -0
- data/Rakefile +9 -0
- data/checklist.gemspec +26 -0
- data/examples/failing.rb +8 -0
- data/examples/simple.rb +8 -0
- data/lib/checklist.rb +104 -0
- data/lib/checklist/step.rb +20 -0
- data/lib/checklist/ui.rb +57 -0
- data/lib/checklist/version.rb +3 -0
- data/lib/checklist/wrapper.rb +9 -0
- data/spec/checklist/checklist_wrapper_method_spec.rb +24 -0
- data/spec/checklist/close_spec.rb +39 -0
- data/spec/checklist/initialize_spec.rb +59 -0
- data/spec/checklist/open_spec.rb +33 -0
- data/spec/checklist/run_spec.rb +42 -0
- data/spec/checklist/step_spec.rb +63 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/step/step_spec.rb +65 -0
- metadata +188 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Maciej Pasternacki
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/NOTES.org
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
* Features I'd like to see
|
3
|
+
** TODO Colorful console UI
|
4
|
+
- formatador gem
|
5
|
+
** TODO Separate "verify" lambda to call
|
6
|
+
** predefined steps & helper functions
|
7
|
+
*** TODO wait for user confirmation
|
8
|
+
*** TODO run a rake task
|
9
|
+
** TODO "Retry" exception
|
10
|
+
** TODO Checklist sections
|
11
|
+
** Export checklists
|
12
|
+
*** TODO PDF
|
13
|
+
*** TODO HTML
|
14
|
+
*** TODO Active HTML (w/ JS to go through the checklist)
|
15
|
+
*** TODO http://checklist.foreflight.com/
|
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# Checklist
|
2
|
+
|
3
|
+
Execute your tasks as checklist, get clear status of what has been
|
4
|
+
done and what has not on your terminal.
|
5
|
+
|
6
|
+

|
7
|
+
|
8
|
+
[](http://travis-ci.org/3ofcoins/checklist)
|
9
|
+
|
10
|
+
## More info
|
11
|
+
|
12
|
+
* [Code and home page at GitHub](https://github.com/3ofcoins/checklist)
|
13
|
+
* [Build status at Travis CI](http://travis-ci.org/3ofcoins/checklist)
|
14
|
+
* [API documentation at RubyDoc.info](http://rdoc.info/github/3ofcoins/checklist)
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
gem 'checklist'
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
$ bundle
|
25
|
+
|
26
|
+
Or install it yourself as:
|
27
|
+
|
28
|
+
$ gem install checklist
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
For now, see the examples/ directory and test cases. More docs will follow.
|
33
|
+
|
34
|
+
## Contributing
|
35
|
+
|
36
|
+
1. Fork it
|
37
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
38
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
39
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
40
|
+
5. Create new Pull Request
|
41
|
+
|
42
|
+
## References
|
43
|
+
|
44
|
+
1. Gawande, Atul (2009) [The Checklist Manifesto](http://gawande.com/the-checklist-manifesto) ISBN: 978-0805091784
|
45
|
+
2. Degani, A., & Wiener, E. L. (1993). [Cockpit checklists: Concepts, design, and use.](http://ti.arc.nasa.gov/m/profile/adegani/Cockpit%20Checklists.pdf) Human Factors, 35(2), 345-359. (more: [Asaf Degani - Procedure Design](http://ti.arc.nasa.gov/profile/adegani/procedure-design/))
|
data/Rakefile
ADDED
data/checklist.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/checklist/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Maciej Pasternacki"]
|
6
|
+
gem.email = ["maciej@pasternacki.net"]
|
7
|
+
gem.description = 'Multi-step checklist execution'
|
8
|
+
gem.summary = 'Define and execute a checklist'
|
9
|
+
gem.homepage = "https://github.com/3ofcoins/checklist"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "checklist"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Checklist::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency "formatador"
|
19
|
+
gem.add_dependency "locale"
|
20
|
+
gem.add_dependency "must_be"
|
21
|
+
|
22
|
+
gem.add_development_dependency 'bundler'
|
23
|
+
gem.add_development_dependency 'rake'
|
24
|
+
gem.add_development_dependency 'simplecov'
|
25
|
+
gem.add_development_dependency 'rspec'
|
26
|
+
end
|
data/examples/failing.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'checklist'
|
2
|
+
|
3
|
+
Checklist.checklist 'Example failing checklist' do |cl|
|
4
|
+
cl.step('One', 'DONE') { sleep(1) }
|
5
|
+
cl.step('Two', 'OK') { sleep(1) }
|
6
|
+
cl.step('Three', 'NO') { raise RuntimeError }
|
7
|
+
cl.step('Four', 'BAAH', 'A longer description here') { }
|
8
|
+
end
|
data/examples/simple.rb
ADDED
data/lib/checklist.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'must_be'
|
2
|
+
|
3
|
+
require "checklist/version"
|
4
|
+
require 'checklist/step'
|
5
|
+
require 'checklist/ui'
|
6
|
+
require 'checklist/wrapper'
|
7
|
+
|
8
|
+
class Checklist
|
9
|
+
attr_reader :steps, :current, :name, :ui
|
10
|
+
|
11
|
+
def initialize(name, options={})
|
12
|
+
@steps = []
|
13
|
+
@remaining_steps = nil
|
14
|
+
@completed_steps = nil
|
15
|
+
@current = nil
|
16
|
+
@name = name
|
17
|
+
@ui = options[:ui] || Checklist::UI.new
|
18
|
+
end
|
19
|
+
|
20
|
+
# appendd a Checklist::Step to the checklist
|
21
|
+
def <<(step)
|
22
|
+
raise RuntimeError, 'List is open' if open?
|
23
|
+
step.must_be_a(Step)
|
24
|
+
steps << step
|
25
|
+
end
|
26
|
+
|
27
|
+
# create new Checklist::Step and add it to the checklist
|
28
|
+
def step(challenge, response, description=nil, &code)
|
29
|
+
self << Checklist::step(challenge, response, description, &code)
|
30
|
+
end
|
31
|
+
|
32
|
+
# true if checklist is started
|
33
|
+
def open?
|
34
|
+
!!remaining_steps
|
35
|
+
end
|
36
|
+
|
37
|
+
# true if checklist is started and all steps are completed_steps
|
38
|
+
def completed?
|
39
|
+
remaining_steps && remaining_steps.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
# Number of defined steps
|
43
|
+
def length
|
44
|
+
steps.length
|
45
|
+
end
|
46
|
+
|
47
|
+
# Number of remaining steps or nil if list is not open
|
48
|
+
def remaining
|
49
|
+
remaining_steps and remaining_steps.length
|
50
|
+
end
|
51
|
+
|
52
|
+
# Number of completed steps or nil if list is not open
|
53
|
+
def completed
|
54
|
+
completed_steps and completed_steps.length
|
55
|
+
end
|
56
|
+
|
57
|
+
def open!
|
58
|
+
raise RuntimeError, "Checklist is already open" if open?
|
59
|
+
ui.header(self)
|
60
|
+
self.remaining_steps = steps.clone
|
61
|
+
self.completed_steps = []
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
# Execute one step of the checklist
|
66
|
+
def step!
|
67
|
+
raise RuntimeError, 'Checklist is not open' unless open?
|
68
|
+
raise RuntimeError, 'Checklist is completed' if completed?
|
69
|
+
self.current = remaining_steps.first
|
70
|
+
ui.start(current)
|
71
|
+
current.run!
|
72
|
+
ui.finish(current)
|
73
|
+
completed_steps << remaining_steps.shift
|
74
|
+
end
|
75
|
+
|
76
|
+
# Finish the checklist, print and return outstanding steps
|
77
|
+
def close!
|
78
|
+
raise RuntimeError, 'Checklist is not open' unless open?
|
79
|
+
if completed?
|
80
|
+
ui.complete(self)
|
81
|
+
else
|
82
|
+
ui.incomplete(self, remaining_steps)
|
83
|
+
end
|
84
|
+
rv = remaining_steps
|
85
|
+
self.current =
|
86
|
+
self.remaining_steps =
|
87
|
+
self.completed_steps =
|
88
|
+
nil
|
89
|
+
rv
|
90
|
+
end
|
91
|
+
|
92
|
+
# Run the whole thing
|
93
|
+
def run!
|
94
|
+
open!
|
95
|
+
step! until completed?
|
96
|
+
self
|
97
|
+
ensure
|
98
|
+
close!
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
attr_writer :current
|
103
|
+
attr_accessor :remaining_steps, :completed_steps
|
104
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Checklist
|
2
|
+
Step = Struct.new(:challenge, :response, :description, :code) do
|
3
|
+
# Run checklist's body code
|
4
|
+
def run!
|
5
|
+
self.code.call
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_hash
|
9
|
+
{ "Challenge" => self.challenge,
|
10
|
+
"Response" => self.response,
|
11
|
+
"Description" => self.description }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Create a new Step instance
|
16
|
+
def self.step(challenge, response, description=nil, &block)
|
17
|
+
raise ArgumentError, 'need a block' unless block_given?
|
18
|
+
Step.new(challenge, response, description, block)
|
19
|
+
end
|
20
|
+
end
|
data/lib/checklist/ui.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'formatador'
|
3
|
+
require 'locale'
|
4
|
+
|
5
|
+
class Checklist
|
6
|
+
class UI
|
7
|
+
UTF8_MARKS = {
|
8
|
+
:tick => '✓',
|
9
|
+
:cross => '✗' }
|
10
|
+
|
11
|
+
ASCII_MARKS = {
|
12
|
+
:tick => '[bold]+',
|
13
|
+
:cross => '[bold]X' }
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@fmtd = Formatador.new
|
17
|
+
@marks = Locale.charset == 'UTF-8' ? UTF8_MARKS : ASCII_MARKS
|
18
|
+
end
|
19
|
+
|
20
|
+
def say(msg='')
|
21
|
+
fmtd.display "#{msg}\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
def header(checklist)
|
25
|
+
fmtd.display "[bold][yellow]CHECKLIST[/][yellow]:[/] [bold]#{checklist.name}[/]\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
def start(step)
|
29
|
+
fmtd.display " [ ] [yellow]#{step.challenge}[/] ..."
|
30
|
+
end
|
31
|
+
|
32
|
+
def finish(step)
|
33
|
+
fmtd.redisplay " [[green]#{marks[:tick]}[/]] #{step.challenge} [green]#{step.response}[/]\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
def complete(checklist)
|
37
|
+
fmtd.display "[green]All #{checklist.length} steps [bold]completed[/]\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
def incomplete(checklist, remaining_steps)
|
41
|
+
fmtd.redisplay " [[red]#{marks[:cross]}[/]] #{remaining_steps.first.challenge} [red]FAILED[/]\n"
|
42
|
+
remaining_steps[1..remaining_steps.length].each do |step|
|
43
|
+
fmtd.display " [ ] #{step.challenge} [yellow]PENDING[/]\n"
|
44
|
+
end
|
45
|
+
fmtd.display "\n[indent]#{checklist.remaining} of #{checklist.length} steps [red]NOT COMPLETED[/]:\n"
|
46
|
+
|
47
|
+
fmtd.indent do
|
48
|
+
fmtd.display_table(
|
49
|
+
remaining_steps.map(&:to_hash),
|
50
|
+
%w(Challenge Response Description))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
attr_reader :fmtd, :marks
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Checklist, '.checklist' do
|
4
|
+
let(:ui) { double('UI').stub_checklist_ui }
|
5
|
+
|
6
|
+
it "yields a Checklist instance" do
|
7
|
+
Checklist.checklist 'Test', :ui => ui do |cl|
|
8
|
+
cl.should be_instance_of Checklist
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "runs a checklist defined within the block" do
|
13
|
+
tt = double("tracker")
|
14
|
+
tt.should_receive(:first).once.ordered
|
15
|
+
tt.should_receive(:second).once.ordered
|
16
|
+
tt.should_receive(:third).once.ordered
|
17
|
+
|
18
|
+
Checklist.checklist "A sample checklist", :ui => ui do |cl|
|
19
|
+
cl.step('first', 'done') { tt.first }
|
20
|
+
cl.step('second', 'ok') { tt.second }
|
21
|
+
cl.step('third', 'fine') { tt.third }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Checklist, '#close!' do
|
4
|
+
subject { example_checklist }
|
5
|
+
|
6
|
+
it "should report checklist completion" do
|
7
|
+
subject.ui.should_receive(:complete).with(subject)
|
8
|
+
|
9
|
+
subject.open!
|
10
|
+
subject.length.times { subject.step! }
|
11
|
+
subject.close!
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should report outstanding steps" do
|
15
|
+
subject.ui.should_receive(:incomplete).with(subject, subject.steps[2..3])
|
16
|
+
|
17
|
+
subject.open!
|
18
|
+
2.times { subject.step! }
|
19
|
+
subject.close!
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should return outstanding steps' do
|
23
|
+
subject.open!
|
24
|
+
subject.step!
|
25
|
+
|
26
|
+
rv = subject.close!
|
27
|
+
rv.should be_instance_of Array
|
28
|
+
rv.length.should == 3
|
29
|
+
rv.each_with_index do |s, i|
|
30
|
+
s.should be_instance_of Checklist::Step
|
31
|
+
s.challenge.should == EXAMPLE_STEPS[i+1][0]
|
32
|
+
s.response.should == EXAMPLE_STEPS[i+1][1]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should require checklist to be open' do
|
37
|
+
expect { subject.close! }.to raise_exception(RuntimeError)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Checklist do
|
4
|
+
let(:checklist) { Checklist.new('Test') }
|
5
|
+
|
6
|
+
it "has a name" do
|
7
|
+
checklist.name.should eq 'Test'
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'initially has an empty step list' do
|
11
|
+
checklist.steps.should eq []
|
12
|
+
checklist.length.should eq 0
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'initially is not open' do
|
16
|
+
checklist.open?.should be false
|
17
|
+
checklist.remaining.should be nil
|
18
|
+
checklist.completed.should be nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'initially is not completed' do
|
22
|
+
checklist.completed?.should be nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe Checklist, '#<<' do
|
27
|
+
let(:checklist) { Checklist.new('Test') }
|
28
|
+
|
29
|
+
it 'adds new step to a checklist' do
|
30
|
+
checklist.steps.should eq []
|
31
|
+
step = Checklist.step('foo', 'bar') { nil }
|
32
|
+
checklist << step
|
33
|
+
checklist.steps.length.should eq 1
|
34
|
+
checklist.steps.first.should eq step
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'requires argument to be a step' do
|
38
|
+
lambda { checklist << 23 }.should raise_error MustBe::Note
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe Checklist, '#step' do
|
43
|
+
let(:checklist) { Checklist.new('Test') }
|
44
|
+
|
45
|
+
it 'adds new step to a checklist' do
|
46
|
+
checklist.steps.should eq []
|
47
|
+
checklist.step('foo', 'bar') { 23 }
|
48
|
+
checklist.steps.length.should eq 1
|
49
|
+
checklist.steps.first.challenge.should eq 'foo'
|
50
|
+
checklist.steps.first.response.should eq 'bar'
|
51
|
+
checklist.steps.first.description.should be nil
|
52
|
+
checklist.steps.first.code.call.should eq 23
|
53
|
+
|
54
|
+
checklist.step('one', 'one done') { nil }
|
55
|
+
checklist.step('two', 'check two') { nil }
|
56
|
+
checklist.step('three', 'three it is') { nil }
|
57
|
+
checklist.length.should == 4
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Checklist, '#open!' do
|
4
|
+
subject { example_checklist }
|
5
|
+
|
6
|
+
it 'opens the checklist' do
|
7
|
+
subject.open?.should be false
|
8
|
+
subject.open!
|
9
|
+
subject.open?.should be true
|
10
|
+
subject.completed.should == 0
|
11
|
+
subject.remaining.should == subject.length
|
12
|
+
end
|
13
|
+
|
14
|
+
it "reports the checklist header" do
|
15
|
+
subject.ui.should_receive(:header).with(subject)
|
16
|
+
subject.open!
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'cannot be called twice' do
|
20
|
+
subject.open!
|
21
|
+
expect { subject.open! }.to raise_exception(RuntimeError)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should prevent adding new steps' do
|
25
|
+
subject.open!
|
26
|
+
expect { subject.step('one', 'one done') { nil } }.
|
27
|
+
to raise_exception(RuntimeError)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns checklist itself' do
|
31
|
+
subject.open!.should == subject
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Checklist, '#run!' do
|
4
|
+
subject { example_checklist }
|
5
|
+
|
6
|
+
it "should call all the methods and execute all the steps" do
|
7
|
+
def should_receive_and_call(method)
|
8
|
+
original_method = subject.method(method)
|
9
|
+
subject.should_receive(method) { original_method.call }
|
10
|
+
end
|
11
|
+
|
12
|
+
should_receive_and_call(:run!).once
|
13
|
+
should_receive_and_call(:open!).once
|
14
|
+
should_receive_and_call(:step!).exactly(subject.length).times
|
15
|
+
should_receive_and_call(:close!).once
|
16
|
+
|
17
|
+
subject.body.should_receive(:step).with(0).once
|
18
|
+
subject.body.should_receive(:step).with(1).once
|
19
|
+
subject.body.should_receive(:step).with(2).once
|
20
|
+
subject.body.should_receive(:step).with(3).once
|
21
|
+
|
22
|
+
subject.run!
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should return the checklist itself' do
|
26
|
+
subject.run!.should == subject
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should raise and report incomplete steps if a step bombs' do
|
30
|
+
subject.step('five', 'bomb') do
|
31
|
+
raise Exception, 'as planned'
|
32
|
+
body.step(5)
|
33
|
+
end
|
34
|
+
subject.step('six', 'done', 'a description') { body.step(6) }
|
35
|
+
|
36
|
+
subject.body.should_not_receive(:step).with(5)
|
37
|
+
subject.body.should_not_receive(:step).with(6)
|
38
|
+
subject.ui.should_receive(:incomplete).with(subject, subject.steps[4..5])
|
39
|
+
|
40
|
+
expect { subject.run! }.to raise_exception(Exception)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Checklist, '#step!' do
|
4
|
+
subject { example_checklist() }
|
5
|
+
|
6
|
+
it 'should execute one next step and push it from remaining to completed' do
|
7
|
+
subject.open!
|
8
|
+
|
9
|
+
subject.remaining.should == 4
|
10
|
+
subject.completed.should == 0
|
11
|
+
|
12
|
+
subject.body.should_receive(:step).with(0).once
|
13
|
+
subject.step!
|
14
|
+
|
15
|
+
subject.remaining.should == 3
|
16
|
+
subject.completed.should == 1
|
17
|
+
|
18
|
+
subject.body.should_receive(:step).with(1).once
|
19
|
+
subject.step!
|
20
|
+
|
21
|
+
subject.remaining.should == 2
|
22
|
+
subject.completed.should == 2
|
23
|
+
|
24
|
+
subject.body.should_receive(:step).with(2).once
|
25
|
+
subject.step!
|
26
|
+
|
27
|
+
subject.remaining.should == 1
|
28
|
+
subject.completed.should == 3
|
29
|
+
|
30
|
+
subject.body.should_receive(:step).with(3).once
|
31
|
+
subject.step!
|
32
|
+
|
33
|
+
subject.remaining.should == 0
|
34
|
+
subject.completed.should == 4
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should complete the checklist, eventually' do
|
38
|
+
subject.open!
|
39
|
+
|
40
|
+
subject.length.times do
|
41
|
+
subject.completed?.should be false
|
42
|
+
subject.step!
|
43
|
+
end
|
44
|
+
|
45
|
+
subject.completed?.should be true
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should not be allowed when list is completed' do
|
49
|
+
subject.open!
|
50
|
+
|
51
|
+
subject.length.times { subject.step! }
|
52
|
+
expect { subject.step! }.to raise_exception(RuntimeError)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should report task start and finish' do
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should be disallowed when list is not open' do
|
60
|
+
# Look, Ma, no open!
|
61
|
+
expect { subject.step! }.to raise_exception(RuntimeError)
|
62
|
+
end
|
63
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
if ENV['COVERAGE']
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start
|
4
|
+
SimpleCov.add_filter('spec')
|
5
|
+
SimpleCov.add_filter('lib/checklist/ui.rb') # not covered by tests
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'rspec/expectations'
|
9
|
+
require 'rspec/mocks'
|
10
|
+
require 'checklist'
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.before(:each) do
|
14
|
+
Checklist.stub(:say)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
EXAMPLE_STEPS = [
|
19
|
+
[ 'one', 'one done' ],
|
20
|
+
[ 'two', 'check two' ],
|
21
|
+
[ 'three', 'three it is' ],
|
22
|
+
[ 'four', 'here you are', 'A surprise description' ]]
|
23
|
+
|
24
|
+
class RSpec::Mocks::Mock
|
25
|
+
def stub_checklist_ui
|
26
|
+
[
|
27
|
+
:say, :header, :start, :finish, :complete, :incomplete, :describe
|
28
|
+
].each { |mm| self.stub(mm) }
|
29
|
+
self
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def example_checklist
|
34
|
+
body = RSpec::Mocks::Mock.new('body')
|
35
|
+
body.stub(:step)
|
36
|
+
|
37
|
+
cl = Checklist.new('Test',
|
38
|
+
:ui => RSpec::Mocks::Mock.new('UI').stub_checklist_ui)
|
39
|
+
|
40
|
+
class << cl ; attr_accessor :body ; end
|
41
|
+
cl.body = body
|
42
|
+
|
43
|
+
EXAMPLE_STEPS.each_with_index do |step, ii|
|
44
|
+
challenge, response, description = step
|
45
|
+
cl.step(challenge, response, description) { body.step(ii) }
|
46
|
+
end
|
47
|
+
|
48
|
+
cl
|
49
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Checklist::Step do
|
4
|
+
it "is a struct with challenge, response, description, and code fields" do
|
5
|
+
step = Checklist::Step.new(
|
6
|
+
'the challenge',
|
7
|
+
'the response',
|
8
|
+
'the description',
|
9
|
+
'some code')
|
10
|
+
step.challenge.should eq('the challenge')
|
11
|
+
step.response.should eq('the response')
|
12
|
+
step.description.should eq('the description')
|
13
|
+
step.code.should eq('some code')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe Checklist::Step, "#run!" do
|
18
|
+
let(:code_body) do
|
19
|
+
code_body = double('code body')
|
20
|
+
code_body.should_receive(:poke!)
|
21
|
+
code_body
|
22
|
+
end
|
23
|
+
let(:step) do
|
24
|
+
Checklist::Step.new(
|
25
|
+
'the challenge',
|
26
|
+
'the response',
|
27
|
+
'the description',
|
28
|
+
lambda { code_body.poke! } )
|
29
|
+
end
|
30
|
+
|
31
|
+
it "runs the proc from code field" do
|
32
|
+
step.run!
|
33
|
+
end
|
34
|
+
|
35
|
+
it "does not catch exceptions raised by code" do
|
36
|
+
step.code = lambda { code_body.poke! ; raise RuntimeError }
|
37
|
+
lambda { step.run! }.should raise_error(RuntimeError)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe Checklist, '::step' do
|
42
|
+
it "Creates a runnable Checklist::Step instance" do
|
43
|
+
called = false
|
44
|
+
step = Checklist.step('the challenge', 'the response', 'a description') do
|
45
|
+
called = true
|
46
|
+
end
|
47
|
+
step.challenge.should eq 'the challenge'
|
48
|
+
step.response.should eq 'the response'
|
49
|
+
step.description.should eq 'a description'
|
50
|
+
|
51
|
+
step.run!
|
52
|
+
called.should be_true
|
53
|
+
end
|
54
|
+
|
55
|
+
it "Defaults description to nil" do
|
56
|
+
step = Checklist.step('the challenge', 'the response') { nil }
|
57
|
+
step.challenge.should eq 'the challenge'
|
58
|
+
step.response.should eq 'the response'
|
59
|
+
step.description.should be nil
|
60
|
+
end
|
61
|
+
|
62
|
+
it "Requires block" do
|
63
|
+
lambda { Checklist.step('foo', 'bar') }.should raise_error(ArgumentError)
|
64
|
+
end
|
65
|
+
end
|
metadata
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: checklist
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Maciej Pasternacki
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: formatador
|
16
|
+
requirement: !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: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: locale
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: must_be
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bundler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rake
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: simplecov
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rspec
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: Multi-step checklist execution
|
127
|
+
email:
|
128
|
+
- maciej@pasternacki.net
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- .gitignore
|
134
|
+
- .travis.yml
|
135
|
+
- Gemfile
|
136
|
+
- LICENSE
|
137
|
+
- NOTES.org
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- checklist.gemspec
|
141
|
+
- examples/failing.rb
|
142
|
+
- examples/simple.rb
|
143
|
+
- lib/checklist.rb
|
144
|
+
- lib/checklist/step.rb
|
145
|
+
- lib/checklist/ui.rb
|
146
|
+
- lib/checklist/version.rb
|
147
|
+
- lib/checklist/wrapper.rb
|
148
|
+
- spec/checklist/checklist_wrapper_method_spec.rb
|
149
|
+
- spec/checklist/close_spec.rb
|
150
|
+
- spec/checklist/initialize_spec.rb
|
151
|
+
- spec/checklist/open_spec.rb
|
152
|
+
- spec/checklist/run_spec.rb
|
153
|
+
- spec/checklist/step_spec.rb
|
154
|
+
- spec/spec_helper.rb
|
155
|
+
- spec/step/step_spec.rb
|
156
|
+
homepage: https://github.com/3ofcoins/checklist
|
157
|
+
licenses: []
|
158
|
+
post_install_message:
|
159
|
+
rdoc_options: []
|
160
|
+
require_paths:
|
161
|
+
- lib
|
162
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
163
|
+
none: false
|
164
|
+
requirements:
|
165
|
+
- - ! '>='
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0'
|
168
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
requirements: []
|
175
|
+
rubyforge_project:
|
176
|
+
rubygems_version: 1.8.24
|
177
|
+
signing_key:
|
178
|
+
specification_version: 3
|
179
|
+
summary: Define and execute a checklist
|
180
|
+
test_files:
|
181
|
+
- spec/checklist/checklist_wrapper_method_spec.rb
|
182
|
+
- spec/checklist/close_spec.rb
|
183
|
+
- spec/checklist/initialize_spec.rb
|
184
|
+
- spec/checklist/open_spec.rb
|
185
|
+
- spec/checklist/run_spec.rb
|
186
|
+
- spec/checklist/step_spec.rb
|
187
|
+
- spec/spec_helper.rb
|
188
|
+
- spec/step/step_spec.rb
|