tamarillo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.gitignore +19 -0
  2. data/.travis.yml +6 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +22 -0
  5. data/README.md +83 -0
  6. data/Rakefile +23 -0
  7. data/bin/tam +4 -0
  8. data/features/config.feature +44 -0
  9. data/features/step_definitions/tamarillo_steps.rb +50 -0
  10. data/features/support/env.rb +5 -0
  11. data/features/tamarillo.feature +47 -0
  12. data/lib/tamarillo/clock.rb +33 -0
  13. data/lib/tamarillo/command.rb +68 -0
  14. data/lib/tamarillo/config.rb +95 -0
  15. data/lib/tamarillo/controller.rb +113 -0
  16. data/lib/tamarillo/monitor.rb +27 -0
  17. data/lib/tamarillo/notification/bell.rb +15 -0
  18. data/lib/tamarillo/notification/growl.rb +13 -0
  19. data/lib/tamarillo/notification/none.rb +13 -0
  20. data/lib/tamarillo/notification/speech.rb +13 -0
  21. data/lib/tamarillo/notification/touch.rb +16 -0
  22. data/lib/tamarillo/notification.rb +36 -0
  23. data/lib/tamarillo/storage.rb +118 -0
  24. data/lib/tamarillo/tomato.rb +96 -0
  25. data/lib/tamarillo/tomato_file.rb +60 -0
  26. data/lib/tamarillo/version.rb +3 -0
  27. data/lib/tamarillo.rb +7 -0
  28. data/spec/lib/tamarillo/clock_spec.rb +39 -0
  29. data/spec/lib/tamarillo/command_spec.rb +17 -0
  30. data/spec/lib/tamarillo/config_spec.rb +133 -0
  31. data/spec/lib/tamarillo/controller_spec.rb +137 -0
  32. data/spec/lib/tamarillo/monitor_spec.rb +27 -0
  33. data/spec/lib/tamarillo/notification/bell_spec.rb +7 -0
  34. data/spec/lib/tamarillo/notification/growl_spec.rb +7 -0
  35. data/spec/lib/tamarillo/notification/speech_spec.rb +7 -0
  36. data/spec/lib/tamarillo/notification_spec.rb +22 -0
  37. data/spec/lib/tamarillo/storage_spec.rb +171 -0
  38. data/spec/lib/tamarillo/tomato_file_spec.rb +41 -0
  39. data/spec/lib/tamarillo/tomato_spec.rb +116 -0
  40. data/spec/support/invalid-config.yml +1 -0
  41. data/spec/support/sample-config.yml +3 -0
  42. data/tamarillo.gemspec +26 -0
  43. metadata +178 -0
@@ -0,0 +1,27 @@
1
+ require_relative '../../../lib/tamarillo/monitor'
2
+
3
+ describe Tamarillo::Monitor do
4
+ let(:tomato) do
5
+ tomato = double('tomato')
6
+ tomato.stub(:completed?).and_return(false, true)
7
+ tomato
8
+ end
9
+
10
+ let(:notifier) do
11
+ notifier = double('notifier')
12
+ notifier.should_receive(:call)
13
+ notifier
14
+ end
15
+
16
+ # XXX This test relies on system time passing,
17
+ # so it's unfortunately slow.
18
+ subject { Tamarillo::Monitor.new(tomato, notifier) }
19
+
20
+ # as a result it is horrible.
21
+ it "watches a tomato for completion" do
22
+ pending "not sure how to test when forking"
23
+ subject.start
24
+ sleep 0.1
25
+ end
26
+ end
27
+
@@ -0,0 +1,7 @@
1
+ require_relative '../../../../lib/tamarillo/notification/bell'
2
+
3
+ describe Tamarillo::Notification::Bell do
4
+ subject { Tamarillo::Notification::Bell.new }
5
+ it { should respond_to(:call) }
6
+ # specify { subject.call }
7
+ end
@@ -0,0 +1,7 @@
1
+ require_relative '../../../../lib/tamarillo/notification/growl'
2
+
3
+ describe Tamarillo::Notification::Growl do
4
+ subject { Tamarillo::Notification::Growl.new }
5
+ it { should respond_to(:call) }
6
+ # specify { subject.call }
7
+ end
@@ -0,0 +1,7 @@
1
+ require_relative '../../../../lib/tamarillo/notification/speech'
2
+
3
+ describe Tamarillo::Notification::Speech do
4
+ subject { Tamarillo::Notification::Speech.new }
5
+ it { should respond_to(:call) }
6
+ # specify { subject.call }
7
+ end
@@ -0,0 +1,22 @@
1
+ require_relative '../../../lib/tamarillo/notification'
2
+
3
+ include Tamarillo
4
+
5
+ describe Tamarillo::Notification do
6
+ subject { Tamarillo::Notification }
7
+
8
+ describe ".valid!" do
9
+ specify { subject.valid!(:bell).should == Notification::BELL }
10
+ specify { subject.valid!('speech').should == Notification::SPEECH }
11
+ specify { subject.valid!('Growl').should == Notification::GROWL }
12
+ specify { subject.valid!('bogus').should == nil }
13
+ end
14
+
15
+ describe ".for" do
16
+ specify { subject.for(Notification::BELL).should be_a(Notification::Bell) }
17
+ specify { subject.for(Notification::GROWL).should be_a(Notification::Growl) }
18
+ specify { subject.for(Notification::SPEECH).should be_a(Notification::Speech) }
19
+ specify { subject.for(Notification::NONE).should be_a(Notification::None) }
20
+ specify { subject.for('bogus').should be_a(Notification::None) }
21
+ end
22
+ end
@@ -0,0 +1,171 @@
1
+ require_relative '../../../lib/tamarillo/storage'
2
+ require 'fakefs/spec_helpers'
3
+ require 'active_support/core_ext/numeric/time'
4
+
5
+ include Tamarillo
6
+
7
+ # Storage layout looks like this
8
+ # |,tamarillo
9
+ # |-config
10
+ # |-basket.sqlite
11
+ # |-basket
12
+ # |-2012
13
+ # |-0401
14
+ # |-060101
15
+
16
+ describe Storage do
17
+ include FakeFS::SpecHelpers
18
+
19
+ let(:time) { Time.local(2011,1,1,6,0,0) }
20
+ let(:date) { Date.new(2011,1,1) }
21
+ let(:storage_path) { Pathname.new( File.expand_path('tmp/tamarillo')) }
22
+ let(:config_path) { storage_path.join('config.yml') }
23
+ let(:tomato_path) { storage_path.join('2011/0101/20110101060000') }
24
+ let(:sample_tomato) { <<EOS }
25
+ #{time.iso8601}
26
+ Some task I'm working on
27
+ completed
28
+ EOS
29
+
30
+ def create_tomato_file(time, options = {})
31
+ folder_path = storage_path.join(time.strftime('%Y/%m%d'))
32
+ tomato_path = folder_path.join(time.strftime('%Y%m%d%H%M%S'))
33
+ state = options.fetch(:state) { 'completed' }
34
+
35
+ FileUtils.mkdir_p(folder_path)
36
+ File.open(tomato_path, 'w') { |f| f << <<EOS }
37
+ #{time.iso8601}
38
+ Some task I'm working on
39
+ #{state}
40
+ EOS
41
+
42
+ tomato_path
43
+ end
44
+
45
+
46
+ subject { Storage.new(storage_path) }
47
+
48
+ it "creates the storage directory if it is missing" do
49
+ expect { Storage.new(storage_path) }.
50
+ to change { File.directory?(storage_path) }
51
+ end
52
+
53
+ it "has a path to the storage directory" do
54
+ subject.path.should == storage_path
55
+ end
56
+
57
+ it "has a default configuration" do
58
+ subject.config.should be_a(Tamarillo::Config)
59
+ end
60
+
61
+ it "can accept a specific configuration" do
62
+ config = stub(:duration => 10)
63
+ storage = Storage.new(storage_path, config)
64
+ storage.config.duration.should == 10
65
+ end
66
+
67
+ describe "writing config to the filesystem" do
68
+ it "writes files to the right place" do
69
+ FakeFS::FileSystem.clear
70
+ FakeFS do
71
+ expect { subject.write_config }.
72
+ to change { File.exist?(config_path) }
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "reading config from the filesystem" do
78
+ end
79
+
80
+ describe "reading monitor from the filesystem" do
81
+ end
82
+
83
+ describe "writing tomatoes to the filesystem" do
84
+ it "writes files to the right place" do
85
+ FakeFS::FileSystem.clear
86
+ FakeFS do
87
+ tomato = stub(:started_at => time, :state => :active)
88
+ expect { subject.write_tomato(tomato) }.
89
+ to change { File.exist?(tomato_path) }
90
+ end
91
+ end
92
+
93
+ it "returns a path to the tomato" do
94
+ tomato = stub(:started_at => time, :state => :active)
95
+ path = subject.write_tomato(tomato)
96
+ path.should == subject.path.join(tomato_path)
97
+ end
98
+
99
+ describe "tomato file" do
100
+ before do
101
+ tomato = stub(:started_at => time, :date => date, :state => :active)
102
+ Storage.new(storage_path).write_tomato(tomato)
103
+ end
104
+
105
+ subject { FakeFS { File.readlines(tomato_path.to_s) } }
106
+
107
+ specify { subject[0].should == time.iso8601}
108
+ specify { subject[1].should == "Some task I'm working on" }
109
+ specify { subject[2].should == 'active' }
110
+ end
111
+ end
112
+
113
+ describe "reading tomatoes from the filesystem" do
114
+ before do
115
+ create_tomato_file(time)
116
+ end
117
+
118
+ it "can read tomatoes from the filesystem" do
119
+ tomato = subject.read_tomato(tomato_path)
120
+ tomato.should be
121
+ end
122
+
123
+ it "returns nil of not found" do
124
+ tomato = subject.read_tomato('NOTHING')
125
+ tomato.should be_nil
126
+ end
127
+
128
+ describe "the tomato" do
129
+ let(:config) { stub(:duration_in_seconds => 10.minutes) }
130
+ let(:storage) { Storage.new(storage_path, config) }
131
+ subject { storage.read_tomato(tomato_path) }
132
+
133
+ its(:started_at) { should == time }
134
+ its(:date) { should == date }
135
+ its(:duration) { should == 10.minutes }
136
+ it { should be_completed }
137
+ end
138
+
139
+ it "handles interrupted tomatoes" do
140
+ create_tomato_file(time, :state => 'interrupted')
141
+ tomato = subject.read_tomato(tomato_path)
142
+ tomato.should be_interrupted
143
+ end
144
+ end
145
+
146
+ describe "finding most recent tomato" do
147
+ subject { Storage.new(storage_path).latest }
148
+
149
+ context "when there are many tomatoes" do
150
+ before do
151
+ create_tomato_file(Date.today.to_time + 6.hours)
152
+ create_tomato_file(Date.today.to_time + 6.hours + 15.minutes)
153
+ end
154
+
155
+ its(:started_at) { should == Date.today.to_time + 6.hours + 15.minutes }
156
+ end
157
+
158
+ context "when there are no tomatoes for the day" do
159
+ before do
160
+ create_tomato_file(Date.today.to_time - 1.days)
161
+ end
162
+
163
+ it { should be_nil }
164
+ end
165
+ end
166
+
167
+ # it "can count how many tomatoes are stored"
168
+ # it "can find tomatoes by week"
169
+ # it "can find tomatoes by month"
170
+ # something can group them by day and maybe task
171
+ end
@@ -0,0 +1,41 @@
1
+ require_relative '../../../lib/tamarillo/tomato_file'
2
+ require_relative '../../../lib/tamarillo/tomato'
3
+ require_relative '../../../lib/tamarillo/clock'
4
+
5
+ describe Tamarillo::TomatoFile do
6
+ # FIXME Timezones will change when running test, argh
7
+ let(:time) { Time.local(2011,1,1, 6,0,0) }
8
+ let(:today) { Date.new(2011,1,1) }
9
+ let(:clock) { Tamarillo::Clock.new(time) }
10
+ let(:tomato) { Tamarillo::Tomato.new(25 * 60, clock) }
11
+
12
+ subject { Tamarillo::TomatoFile.new(tomato) }
13
+
14
+ its(:name) { should == '20110101060000' }
15
+ its(:path) { should == '2011/0101/20110101060000' }
16
+ its(:content) { should == <<-EOS.chomp }
17
+ #{time.iso8601}
18
+ Some task I'm working on
19
+ completed
20
+ EOS
21
+
22
+ it "can write an interrupted tomato" do
23
+ tomato.interrupt!
24
+ subject.content.should include('interrupted')
25
+ end
26
+
27
+ describe ".path" do
28
+ subject { Tamarillo::TomatoFile }
29
+
30
+ specify do
31
+ path = subject.path(Time.new(1999,12,31,11,59,59))
32
+ path.should == '1999/1231/19991231115959'
33
+ end
34
+
35
+ specify do
36
+ path = subject.path(Time.new(2012,04,10,6,0,23))
37
+ path.should == '2012/0410/20120410060023'
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,116 @@
1
+ require 'active_support/core_ext/numeric/time'
2
+ require_relative '../../../lib/tamarillo/tomato'
3
+
4
+ include Tamarillo
5
+
6
+ describe Tomato do
7
+ subject { Tomato.new(25.minutes, clock) }
8
+
9
+ describe "basic attibutes" do
10
+ let(:now) { Time.new(2012,1,1,6,0,0) }
11
+ let(:today) { Date.new(2012,1,1) }
12
+ let(:clock) { stub(:start_time => now, :start_date => today, :elapsed => 60) }
13
+
14
+ its(:duration) { should == 25.minutes }
15
+ its(:started_at) { should == now }
16
+ its(:date) { should == today }
17
+ its(:elapsed) { should == clock.elapsed }
18
+
19
+ it "update the duration" do
20
+ subject.duration = 5.minutes
21
+ subject.duration.should == 5.minutes
22
+ end
23
+ end
24
+
25
+ describe "comparison" do
26
+ it "is the same if the start-times match" do
27
+ clock = stub(:start_time => Time.new)
28
+ tomato_a = Tomato.new(25.minutes, clock)
29
+ tomato_b = Tomato.new(25.minutes, clock)
30
+
31
+ tomato_a.should eql(tomato_b)
32
+ end
33
+
34
+ # FIXME This ensures two tomatoes from the same
35
+ # time cannot co-exist, but it seems confusing
36
+ it "is the same if the duration differs" do
37
+ clock = stub(:start_time => Time.new)
38
+ tomato_a = Tomato.new(10.minutes, clock)
39
+ tomato_b = Tomato.new(20.minutes, clock)
40
+
41
+ tomato_a.should eql(tomato_b)
42
+ end
43
+ end
44
+
45
+ describe "remaining time" do
46
+ let(:clock) { stub(:elapsed => elapsed) }
47
+ let(:elapsed) { 0 }
48
+
49
+ context "when no time has elapsed" do
50
+ let(:elapsed) { 0 }
51
+ its(:remaining) { should == 25.minutes }
52
+ end
53
+
54
+ context "if some time has elapsed" do
55
+ let(:elapsed) { 10.minutes }
56
+ its(:remaining) { should == 15.minutes }
57
+ end
58
+
59
+ context "when all time has elapsed" do
60
+ let(:elapsed) { 25.minutes }
61
+ its(:remaining) { should == 0 }
62
+ end
63
+
64
+ context "when excessive time has elapsed" do
65
+ let(:elapsed) { 30.minutes }
66
+ its(:remaining) { should == 0 }
67
+ end
68
+
69
+ context "when it has been interrupted" do
70
+ before do
71
+ subject.interrupt!
72
+ end
73
+
74
+ its(:remaining) { should == 0 }
75
+ end
76
+ end
77
+
78
+ describe "states" do
79
+ let(:clock) { stub(:elapsed => elapsed) }
80
+ let(:elapsed) { 0 }
81
+
82
+ context "when just started" do
83
+ let(:elapsed) { 0 }
84
+ it { should be_active }
85
+ it { should_not be_completed }
86
+ it { should_not be_interrupted }
87
+ end
88
+
89
+ context "when time remains" do
90
+ let(:elapsed) { 15.minutes }
91
+ it { should be_active }
92
+ it { should_not be_completed }
93
+ it { should_not be_interrupted }
94
+ end
95
+
96
+ context "when time has elapsed" do
97
+ let(:elapsed) { 25.minutes }
98
+ it { should_not be_active }
99
+ it { should be_completed }
100
+ it { should_not be_interrupted }
101
+ end
102
+
103
+ context "when it has been interrupted" do
104
+ before do
105
+ subject.interrupt!
106
+ end
107
+
108
+ it { should be_interrupted }
109
+ it { should_not be_completed }
110
+ end
111
+
112
+ end
113
+
114
+ # it "can be distracted" ??
115
+
116
+ end
@@ -0,0 +1 @@
1
+ ---
@@ -0,0 +1,3 @@
1
+ ---
2
+ duration: 30
3
+ notifier: growl
data/tamarillo.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/tamarillo/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "tamarillo"
6
+ gem.version = Tamarillo::VERSION
7
+ gem.authors = ["Tim Uruski"]
8
+ gem.email = ["timuruski@gmail.com"]
9
+ gem.description = %q{Manage pomodoros from the command-line.}
10
+ gem.summary = %q{A command-line application for managing the pomodoro technique.}
11
+ gem.homepage = "https://github.com/timuruski/tamarillo"
12
+
13
+ gem.files = `git ls-files`.split($\)
14
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
15
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.require_paths = ["lib"]
17
+
18
+ gem.add_development_dependency 'aruba', '~> 0.4.11'
19
+ gem.add_development_dependency 'active_support', '~> 3.0.0'
20
+ gem.add_development_dependency 'fakefs', '~> 0.4.0'
21
+ gem.add_development_dependency 'rspec', '~> 2.9.0'
22
+ gem.add_development_dependency 'timecop', '~> 0.3.5'
23
+
24
+ gem.add_runtime_dependency 'methadone', '~> 1.0.0.rc5'
25
+
26
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tamarillo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tim Uruski
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: aruba
16
+ requirement: &70235404161660 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.4.11
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70235404161660
25
+ - !ruby/object:Gem::Dependency
26
+ name: active_support
27
+ requirement: &70235404160820 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70235404160820
36
+ - !ruby/object:Gem::Dependency
37
+ name: fakefs
38
+ requirement: &70235404160260 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 0.4.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70235404160260
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &70235404159400 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.9.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70235404159400
58
+ - !ruby/object:Gem::Dependency
59
+ name: timecop
60
+ requirement: &70235404158380 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 0.3.5
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70235404158380
69
+ - !ruby/object:Gem::Dependency
70
+ name: methadone
71
+ requirement: &70235404157700 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 1.0.0.rc5
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *70235404157700
80
+ description: Manage pomodoros from the command-line.
81
+ email:
82
+ - timuruski@gmail.com
83
+ executables:
84
+ - tam
85
+ extensions: []
86
+ extra_rdoc_files: []
87
+ files:
88
+ - .gitignore
89
+ - .travis.yml
90
+ - Gemfile
91
+ - LICENSE
92
+ - README.md
93
+ - Rakefile
94
+ - bin/tam
95
+ - features/config.feature
96
+ - features/step_definitions/tamarillo_steps.rb
97
+ - features/support/env.rb
98
+ - features/tamarillo.feature
99
+ - lib/tamarillo.rb
100
+ - lib/tamarillo/clock.rb
101
+ - lib/tamarillo/command.rb
102
+ - lib/tamarillo/config.rb
103
+ - lib/tamarillo/controller.rb
104
+ - lib/tamarillo/monitor.rb
105
+ - lib/tamarillo/notification.rb
106
+ - lib/tamarillo/notification/bell.rb
107
+ - lib/tamarillo/notification/growl.rb
108
+ - lib/tamarillo/notification/none.rb
109
+ - lib/tamarillo/notification/speech.rb
110
+ - lib/tamarillo/notification/touch.rb
111
+ - lib/tamarillo/storage.rb
112
+ - lib/tamarillo/tomato.rb
113
+ - lib/tamarillo/tomato_file.rb
114
+ - lib/tamarillo/version.rb
115
+ - spec/lib/tamarillo/clock_spec.rb
116
+ - spec/lib/tamarillo/command_spec.rb
117
+ - spec/lib/tamarillo/config_spec.rb
118
+ - spec/lib/tamarillo/controller_spec.rb
119
+ - spec/lib/tamarillo/monitor_spec.rb
120
+ - spec/lib/tamarillo/notification/bell_spec.rb
121
+ - spec/lib/tamarillo/notification/growl_spec.rb
122
+ - spec/lib/tamarillo/notification/speech_spec.rb
123
+ - spec/lib/tamarillo/notification_spec.rb
124
+ - spec/lib/tamarillo/storage_spec.rb
125
+ - spec/lib/tamarillo/tomato_file_spec.rb
126
+ - spec/lib/tamarillo/tomato_spec.rb
127
+ - spec/support/invalid-config.yml
128
+ - spec/support/sample-config.yml
129
+ - tamarillo.gemspec
130
+ homepage: https://github.com/timuruski/tamarillo
131
+ licenses: []
132
+ post_install_message:
133
+ rdoc_options: []
134
+ require_paths:
135
+ - lib
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ segments:
143
+ - 0
144
+ hash: 734855712674183534
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ none: false
147
+ requirements:
148
+ - - ! '>='
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ segments:
152
+ - 0
153
+ hash: 734855712674183534
154
+ requirements: []
155
+ rubyforge_project:
156
+ rubygems_version: 1.8.11
157
+ signing_key:
158
+ specification_version: 3
159
+ summary: A command-line application for managing the pomodoro technique.
160
+ test_files:
161
+ - features/config.feature
162
+ - features/step_definitions/tamarillo_steps.rb
163
+ - features/support/env.rb
164
+ - features/tamarillo.feature
165
+ - spec/lib/tamarillo/clock_spec.rb
166
+ - spec/lib/tamarillo/command_spec.rb
167
+ - spec/lib/tamarillo/config_spec.rb
168
+ - spec/lib/tamarillo/controller_spec.rb
169
+ - spec/lib/tamarillo/monitor_spec.rb
170
+ - spec/lib/tamarillo/notification/bell_spec.rb
171
+ - spec/lib/tamarillo/notification/growl_spec.rb
172
+ - spec/lib/tamarillo/notification/speech_spec.rb
173
+ - spec/lib/tamarillo/notification_spec.rb
174
+ - spec/lib/tamarillo/storage_spec.rb
175
+ - spec/lib/tamarillo/tomato_file_spec.rb
176
+ - spec/lib/tamarillo/tomato_spec.rb
177
+ - spec/support/invalid-config.yml
178
+ - spec/support/sample-config.yml