timert 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,38 @@
1
+ module Timert
2
+ class Timer
3
+ attr_reader :path, :today
4
+
5
+ def initialize(today)
6
+ @today = today
7
+ end
8
+
9
+ def start(time = nil)
10
+ if !on?
11
+ started = true
12
+ today.add_start(time || now)
13
+ end
14
+ {time: today.last_start, started: started}
15
+ end
16
+
17
+ def stop(time = nil)
18
+ if on?
19
+ stopped = true
20
+ today.add_stop(time || now)
21
+ end
22
+ {time: today.last_stop, stopped: stopped}
23
+ end
24
+
25
+ def add_task(task)
26
+ today.add_task(task)
27
+ end
28
+
29
+ private
30
+ def now
31
+ Time.now.to_i
32
+ end
33
+
34
+ def on?
35
+ today.is_interval_started?
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,148 @@
1
+ require 'timecop'
2
+ require_relative '../lib/timert/application'
3
+ require_relative '../lib/timert/database'
4
+ require_relative '../lib/timert/database_file'
5
+
6
+ describe Timert::Application do
7
+ let(:path) { './spec/data/timert' }
8
+ let(:database) { Timert::Database.new(Timert::DatabaseFile.new(path)) }
9
+ let(:app_with_no_args) { Timert::Application.new([], path) }
10
+
11
+ after(:each) do
12
+ File.delete(path)
13
+ end
14
+
15
+ context 'when initialized with empty arguments' do
16
+ it 'should be initialized without errors' do
17
+ expect { app_with_no_args }.to_not raise_error
18
+ end
19
+ end
20
+
21
+ context 'when initialized with start argument' do
22
+ before do
23
+ Timecop.freeze(Time.new(2013, 6, 12, 13, 56))
24
+ @app = Timert::Application.new(["start"], path)
25
+ end
26
+
27
+ after do
28
+ Timecop.return
29
+ end
30
+
31
+ it 'should perform start operation' do
32
+ expect(database.today.last_start).to eq(Time.now.to_i)
33
+ end
34
+
35
+ it 'should have a method that returns hash result' do
36
+ expect(@app.result["message"].instance_of?(String)).to eq(true)
37
+ end
38
+
39
+ context 'and then initialized with stop argument' do
40
+ before do
41
+ Timecop.freeze(Time.new(2013, 6, 12, 14, 34))
42
+ @app = Timert::Application.new(["stop"], path)
43
+ end
44
+
45
+ after do
46
+ Timecop.return
47
+ end
48
+
49
+ it 'should perform stop operation' do
50
+ expect(database.today.last_stop).to eq(Time.now.to_i)
51
+ end
52
+
53
+ it 'should have a method that returns hash result' do
54
+ expect(@app.result["message"].instance_of?(String)).to eq(true)
55
+ end
56
+ end
57
+
58
+ context 'and then initialized with stop and time argument' do
59
+ before do
60
+ Timecop.freeze(2013, 6, 12, 16, 00)
61
+ @app = Timert::Application.new(["stop", "15:59"], path)
62
+ end
63
+
64
+ after do
65
+ Timecop.return
66
+ end
67
+
68
+ it 'should stop with given time' do
69
+ expect(database.today.last_stop).to eq(Time.now.to_i - 60)
70
+ end
71
+ end
72
+
73
+ context 'and then initialized with stop and invalid time argument' do
74
+ it 'should not raise an error' do
75
+ Timecop.freeze(2013, 6, 12, 15, 00) do
76
+ Timert::Application.new(["stop", "9"], path)
77
+ expect { Timert::Application.new(["stop", "9"], path) }.to_not raise_error
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ context 'when initialized with start and time argument' do
84
+ before do
85
+ Timecop.freeze(2013, 3, 20, 16, 00)
86
+ @app = Timert::Application.new(["start", "15:59"], path)
87
+ end
88
+
89
+ after do
90
+ Timecop.return
91
+ end
92
+
93
+ it 'should start with given time' do
94
+ expect(database.today.last_start).to eq(Time.now.to_i - 60)
95
+ end
96
+ end
97
+
98
+ context 'when initialized with start and invalid time argument' do
99
+ before do
100
+ Timecop.freeze(Time.new(2013, 1, 10, 15)) do
101
+ Timert::Application.new(["start"], path)
102
+ end
103
+ Timecop.freeze(Time.new(2013, 1, 10, 16)) do
104
+ Timert::Application.new(["stop"], path)
105
+ end
106
+ end
107
+
108
+ it 'should not raise an error' do
109
+ Timecop.freeze(2013, 1, 10, 17, 00) do
110
+ expect { Timert::Application.new(["start", "9"], path) }.to_not raise_error
111
+ end
112
+ end
113
+ end
114
+
115
+ context 'when initialized with report argument' do
116
+ before do
117
+ @app = Timert::Application.new(["report"], path)
118
+ end
119
+
120
+ it 'should have a method that returns hash result' do
121
+ expect(@app.result["message"].include?("REPORT")).to eq(true)
122
+ end
123
+ end
124
+
125
+ context 'when initialized with argument other than start, stop or report' do
126
+ before do
127
+ @app = Timert::Application.new(["testing the new app"], path)
128
+ end
129
+
130
+ it 'should add task' do
131
+ expect(database.today.tasks).to eq(["testing the new app"])
132
+ end
133
+
134
+ context 'and then initialized with argument that fullfills the same requirements' do
135
+ before do
136
+ @app = Timert::Application.new(["writing emails"], path)
137
+ end
138
+
139
+ it 'should add another task' do
140
+ expect(database.today.tasks).to eq(["testing the new app", "writing emails"])
141
+ end
142
+ end
143
+
144
+ it 'should have a method that returns hash result' do
145
+ expect(@app.result["message"].instance_of?(String)).to eq(true)
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,47 @@
1
+ require 'timecop'
2
+ require_relative '../lib/timert/argument_parser'
3
+
4
+ describe Timert::ArgumentParser do
5
+ let(:ap_with_empty_array) { Timert::ArgumentParser.new([]) }
6
+ let(:ap_with_start) { Timert::ArgumentParser.new(['start']) }
7
+ let(:ap_with_start_at) { Timert::ArgumentParser.new(['start', '16:50']) }
8
+ let(:ap_with_stop_at) { Timert::ArgumentParser.new(['stop', '19']) }
9
+
10
+ it 'should be initialized with one array argument' do
11
+ expect { ap_with_empty_array }.to_not raise_error
12
+ end
13
+
14
+ it 'should have action method' do
15
+ expect(ap_with_start.action).to eq('start')
16
+ end
17
+
18
+ context 'when a time argument is given with the start action' do
19
+ it 'should have argument method that returns numeric representation of that time' do
20
+ Timecop.freeze(Time.new(2013, 4, 6, 16, 50)) do
21
+ expect(ap_with_start_at.argument).to eq(Time.now.to_i)
22
+ end
23
+ end
24
+ end
25
+
26
+ context 'when a time argument is given with the stop action' do
27
+ it 'should have argument method that returns numeric representation of that time' do
28
+ Timecop.freeze(Time.new(2013, 4, 6, 19)) do
29
+ expect(ap_with_stop_at.argument).to eq(Time.now.to_i)
30
+ end
31
+ end
32
+ end
33
+
34
+ it 'should have argument equal to nil if one-element array is passed' do
35
+ expect(ap_with_start.argument).to eq(nil)
36
+ end
37
+
38
+ it 'should have action equal to help and argument equal to nil if empty array is passed' do
39
+ expect(ap_with_empty_array.action).to eq("help")
40
+ expect(ap_with_empty_array.argument).to eq(nil)
41
+ end
42
+
43
+ it 'should have add_task action when no-api argument is passed' do
44
+ expect(Timert::ArgumentParser.new(['buy bananas']).action).to eq('add_task')
45
+ expect(Timert::ArgumentParser.new(['buy bananas']).argument).to eq('buy bananas')
46
+ end
47
+ end
@@ -0,0 +1,21 @@
1
+ require_relative '../lib/timert/database_file'
2
+
3
+ describe Timert::DatabaseFile do
4
+ let(:path) { './spec/data/timert' }
5
+
6
+ before(:each) do
7
+ @file = Timert::DatabaseFile.new(path)
8
+ end
9
+
10
+ after(:each) do
11
+ File.delete(path)
12
+ end
13
+
14
+ it "should load data that's been saved" do
15
+ data = {
16
+ "example" => "This is a sample data."
17
+ }
18
+ @file.save(data)
19
+ expect(@file.load).to eq(data)
20
+ end
21
+ end
@@ -0,0 +1,58 @@
1
+ require 'timecop'
2
+ require 'date'
3
+
4
+ require_relative '../lib/timert/database'
5
+ require_relative '../lib/timert/database_file'
6
+ require_relative '../lib/timert/day'
7
+
8
+ describe Timert::Database do
9
+ before(:each) do
10
+ @path = "./spec/data/test_data"
11
+ @db = Timert::Database.new(Timert::DatabaseFile.new(@path))
12
+ end
13
+
14
+ after(:each) do
15
+ File.delete(@path)
16
+ end
17
+
18
+ it 'should have a method for getting the current day' do
19
+ write_day = Timert::Day.new(tasks: ["emails"])
20
+ @db.save(write_day)
21
+ read_day = @db.day
22
+ expect(write_day.to_hash).to eq(read_day.to_hash)
23
+ end
24
+
25
+ it 'should have a method for getting one of the past days' do
26
+ write_day = Timecop.freeze(Time.new(2013, 8, 12, 12)) do
27
+ @db.save(Timert::Day.new(tasks: ["meeting"]))
28
+ @db.day
29
+ end
30
+ read_day = Timecop.freeze(Time.new(2013, 8, 14, 12)) do
31
+ @db.day(Date.today - 2)
32
+ end
33
+ expect(write_day).to eq(read_day)
34
+ end
35
+
36
+ it 'should save current day' do
37
+ day = Timert::Day.new(tasks: ["emails", "reading"])
38
+ @db.save(day)
39
+ expect(@db.day).to eq(day)
40
+ end
41
+
42
+ it 'should have a method for getting a range of days' do
43
+ first = Timert::Day.new(tasks: ["emails"], date: Date.new(2013, 9, 10))
44
+ second = Timert::Day.new(tasks: ["meetings"], date: Date.new(2013, 9, 11))
45
+
46
+ past = Timecop.freeze(first.date) do
47
+ @db.save(first)
48
+ Date.today - 5
49
+ end
50
+
51
+ present = Timecop.freeze(second.date) do
52
+ @db.save(second)
53
+ Date.today
54
+ end
55
+
56
+ expect(@db.days(Range.new(past, present))).to eq([first, second])
57
+ end
58
+ end
@@ -0,0 +1,19 @@
1
+ require 'timecop'
2
+ require_relative '../lib/timert/date_util'
3
+
4
+ describe Timert::DateUtil do
5
+ it 'should return formatted hour' do
6
+ time = Time.new(2000, 1, 1, 14, 20, 13)
7
+ expect(Timert::DateUtil.format_hour(time.to_i)).to eq(time.to_s.split[1])
8
+ end
9
+
10
+ it 'should return formatted date' do
11
+ expect(Timert::DateUtil.format_date(Date.new(2013, 3, 14))).to eq("2013-03-14")
12
+ end
13
+
14
+ it 'should parse time' do
15
+ Timecop.freeze(Time.new(2013, 2, 28)) do
16
+ expect(Timert::DateUtil.parse_time("11:14")).to eq(Time.new(2013, 2, 28, 11, 14).to_i)
17
+ end
18
+ end
19
+ end
data/spec/day_spec.rb ADDED
@@ -0,0 +1,220 @@
1
+ require 'date'
2
+ require 'timecop'
3
+
4
+ require_relative '../lib/timert/day'
5
+
6
+ describe Timert::Day do
7
+ let(:now) { Time.now.to_i}
8
+ let(:day) { Timert::Day.new }
9
+
10
+ it 'should have method intervals that returns array' do
11
+ expect(day.intervals.instance_of?(Array)).to eq(true)
12
+ end
13
+
14
+ context 'containing a completed, 100 seconds interval' do
15
+ before do
16
+ day.add_start(now)
17
+ day.add_stop(now + 100)
18
+ end
19
+
20
+ it 'should have a method intervals that returns an array with start and stop times' do
21
+ expect(day.intervals).to eq([{"start" => now, "stop" => now + 100}])
22
+ end
23
+
24
+ it 'should have a method that returns total elapsed time' do
25
+ expect(day.total_elapsed_time).to eq(100)
26
+ end
27
+
28
+ it 'should have a method that returns a hash representation of the object' do
29
+ expect(day.to_hash).to eq({
30
+ "intervals" => [{"start" => now, "stop" => now + 100}],
31
+ "tasks" => []})
32
+ end
33
+
34
+ it 'should have a method that says that no interval is started' do
35
+ expect(day.is_interval_started?).to eq(false)
36
+ end
37
+
38
+ it 'should have a method that returns the time of the last start' do
39
+ expect(day.last_start).to eq(now)
40
+ end
41
+
42
+ it 'should have a method that returns the time of the last stop' do
43
+ expect(day.last_stop).to eq(now + 100)
44
+ end
45
+
46
+ it "should not add a new interval when no interval is started" do
47
+ expect { day.add_stop(now + 200) }.to_not change(day, :intervals)
48
+ end
49
+ end
50
+
51
+ it "should not add a new interval when the last one hasn't been completed" do
52
+ day.add_start(now)
53
+ expect { day.add_start(now) }.not_to change(day, :intervals)
54
+ end
55
+
56
+ it 'should have a method that returns total elapsed time' do
57
+ day.add_start(Time.new(2013, 6, 10, 12, 34).to_i)
58
+ day.add_stop(Time.new(2013, 6, 10, 14, 51, 10).to_i)
59
+ day.add_start(Time.new(2013, 6, 10, 16, 10, 20).to_i)
60
+ day.add_stop(Time.new(2013, 6, 10, 17, 15, 41).to_i)
61
+ expect(day.total_elapsed_time).to eq(duration(3, 22, 31))
62
+ end
63
+
64
+ context "when calculating total elapsed time and when last interval isn't finished" do
65
+ let(:now) { Time.new(2013, 6, 10, 18, 10) }
66
+
67
+ context 'and the day is today' do
68
+ let(:start_of_work) { Time.new(2013, 6, 10, 12, 0) }
69
+
70
+ it "should assume the interval's end to now" do
71
+ Timecop.freeze(now) do
72
+ day.add_start(start_of_work.to_i)
73
+ # It is 6h and 10min between work started at 12:00 and now (18:10).
74
+ expect(day.total_elapsed_time).to eq(duration(6, 10, 0))
75
+ end
76
+ end
77
+ end
78
+
79
+ context "and the day is yesterday" do
80
+ let(:start_of_work) { Time.new(2013, 6, 9, 13, 30) }
81
+
82
+ it "should assume the interval's end to the last second of yesteday" do
83
+ day.add_start(start_of_work.to_i)
84
+ Timecop.freeze(now) do
85
+ # It is 10h and 30min between work started at 13:30 and end of the day.
86
+ expect(day.total_elapsed_time).to eq(duration(10, 30, 0))
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ it 'should have tasks method that returns an array' do
93
+ expect(day.tasks.instance_of?(Array)).to eq(true)
94
+ end
95
+
96
+ it 'should add tasks' do
97
+ day.add_task("watching youtube")
98
+ day.add_task("walking the dog")
99
+ expect(day.tasks).to eq(["watching youtube", "walking the dog"])
100
+ end
101
+
102
+ context 'after initialized with hash data' do
103
+ let(:valid_params) do
104
+ {
105
+ intervals: [{"start" => now, "stop" => now + 300}],
106
+ tasks: ["debugging", "emails"],
107
+ date: Time.now.to_date
108
+ }
109
+ end
110
+
111
+ let(:day) { Timert::Day.new(valid_params) }
112
+
113
+ it "should raise error if tasks aren't an array" do
114
+ expect { Timert::Day.new(valid_params.merge(tasks: "mails")) }.to raise_error
115
+ end
116
+
117
+ it "should raise error if intervals aren't an array" do
118
+ expect { Timert::Day.new(valid_params.merge(intervals: {"start" => 0})) }.to raise_error
119
+ end
120
+
121
+ it 'should have a method intervals that returns array with start and stop times' do
122
+ expect(day.intervals).to eq([{"start" => now, "stop" => now + 300}])
123
+ end
124
+
125
+ it 'should have a method tasks that returns an array of tasks' do
126
+ expect(day.tasks).to eq(["debugging", "emails"])
127
+ end
128
+
129
+ it "should have a method that returns the day's date" do
130
+ expect(day.date).to eq(Time.now.to_date)
131
+ end
132
+
133
+ it "should have to_hash method that returns day's state" do
134
+ hash = {
135
+ "intervals" => [{"start" => now, "stop" => now + 300}],
136
+ "tasks" => ["debugging", "emails"]
137
+ }
138
+ expect(day.to_hash).to eq(hash)
139
+ end
140
+ end
141
+
142
+ it "should return start time when it's added" do
143
+ expect(day.add_start(now)).to eq(now)
144
+ end
145
+
146
+ it "should return stop time when it's added" do
147
+ day.add_start(now - 100)
148
+ expect(day.add_stop(now)).to eq(now)
149
+ end
150
+
151
+ it 'should be equal to other day with the same data' do
152
+ first_day = Timert::Day.new(tasks: ["emails"])
153
+ second_day = Timert::Day.new(tasks: ["emails"])
154
+ expect(first_day).to eq(second_day)
155
+ end
156
+
157
+ it 'should raise an ArgumentError if passed stop time is lower than last start time' do
158
+ day.add_start(now)
159
+ expect { day.add_stop(now - 300) }.to raise_error(ArgumentError)
160
+ end
161
+
162
+ it 'should raise an ArgumentError if passed start time is lower than last start time' do
163
+ day.add_start(now)
164
+ day.add_stop(now + 600)
165
+ expect { day.add_start(now - 500) }.to raise_error(ArgumentError)
166
+ end
167
+
168
+ it 'should raise an ArgumentError if passed start time is equal to last start time' do
169
+ day.add_start(now)
170
+ day.add_stop(now + 600)
171
+ expect { day.add_start(now) }.to raise_error(ArgumentError)
172
+ end
173
+
174
+ it 'should raise an ArgumentError if passed start time is lower than last stop time' do
175
+ day.add_start(now)
176
+ day.add_stop(now + 1000)
177
+ expect { day.add_start(now + 500) }.to raise_error(ArgumentError)
178
+ end
179
+
180
+ it 'should not raise an ArgumentError if passed start time is equal to last stop time' do
181
+ day.add_start(now)
182
+ day.add_stop(now + 1000)
183
+ expect { day.add_start(now + 1000) }.not_to raise_error
184
+ end
185
+
186
+ context "when it's initialized with date" do
187
+ let(:day) { Timert::Day.new(date: Date.new(2013, 5, 12)) }
188
+ before do
189
+ Timecop.freeze(Time.new(2013, 5, 12, 12, 30))
190
+ end
191
+
192
+ after do
193
+ Timecop.return
194
+ end
195
+
196
+ it "should raise error if a start time with a different date is added" do
197
+ expect { day.add_start(Time.new(2013, 6, 14, 12).to_i) }.to raise_error
198
+ end
199
+
200
+ it "should raise error if a stop time with a different date is added" do
201
+ day.add_start(now)
202
+ expect { day.add_stop(Time.new(2013, 6, 14, 12).to_i) }.to raise_error
203
+ end
204
+ end
205
+
206
+ context "when a task is added more than once" do
207
+ before do
208
+ day.add_task("mails")
209
+ day.add_task("mails")
210
+ end
211
+
212
+ it "should ignore it" do
213
+ expect(day.to_hash).to eq({"tasks" => ["mails"], "intervals" => []})
214
+ end
215
+ end
216
+
217
+ def duration(hours, minutes, seconds)
218
+ hours * 60 * 60 + minutes * 60 + seconds
219
+ end
220
+ end