timert 1.0.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.
@@ -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