tempo-cli 0.1.3 → 0.1.4

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/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tempo-cli (0.1.2)
4
+ tempo-cli (0.1.3)
5
5
  chronic (~> 0.10.2)
6
6
  gli (= 2.6.1)
7
7
 
@@ -36,6 +36,7 @@ Feature: Start Command starts a new time record
36
36
  When I run `tempo start --end "1 hour from now"`
37
37
  Then the stdout should contain "time record started"
38
38
  And the output should match /\d{2}:\d{2} - \d{2}:\d{2}/
39
+
39
40
  @pending
40
41
  Scenario: Adding a time record with tags
41
42
  Given an existing project file
@@ -45,7 +46,7 @@ Feature: Start Command starts a new time record
45
46
  When I run `tempo start --at "1-1-2014 7:00"`
46
47
  And I run `tempo end --at "1-1-2014 10:00"`
47
48
  And I run `tempo start --at "1-1-2014 8:00"`
48
- Then the stderr should contain "error: Time conflict with existing record"
49
+ Then the stderr should contain "error: time <08:00> conflicts with existing record: 07:00 - 10:00"
49
50
 
50
51
  Scenario: Adding a time record and closing out the last one
51
52
  Given an existing project file
@@ -60,21 +61,15 @@ Feature: Start Command starts a new time record
60
61
  And I run `tempo start --at "1-3-2014 10:00"`
61
62
  Then the stdout should contain "time record started"
62
63
  And the time record 20140101 should contain ":end_time: 2014-01-01 23:59" at line 5
63
- @pending
64
- Scenario: Adding an evening time record should compensate for local time
65
- # need to mock entering time in the evening, 21:26:46 -0400
66
- # make sure --at "5:00" is recorded for the local day, not GMC
67
64
 
68
- # Adding a time record at the beginning of the day seems to close out at the last time record
69
- # check on and fix this bug.
70
- @pending
65
+
71
66
  Scenario: Adding an earlier time record should immediately close out
72
67
  Given an existing project file
73
- When I run `tempo start --at "1-1-2014 9:00"`
68
+ When I run `tempo start --at "1-1-2014 10:00"`
74
69
  And I run `tempo start --at "1-1-2014 17:00"`
75
70
  And I run `tempo start --at "1-1-2014 6:00"`
76
71
  Then the stdout should contain "time record started"
77
- And the time record 20140101 should contain ":end_time: 2014-01-01 9:00" at line 5
72
+ And the time record 20140101 should contain ":end_time: 2014-01-01 10:00" at line 5
78
73
 
79
74
  Scenario: Adding an earlier day time record should immediately close out
80
75
  Given an existing project file
@@ -83,5 +78,9 @@ Feature: Start Command starts a new time record
83
78
  Then the stdout should contain "time record started"
84
79
  And the time record 20140101 should contain ":end_time: 2014-01-01 23:59" at line 5
85
80
 
81
+ @pending
82
+ Scenario: Adding an evening time record should compensate for local time
83
+ # need to mock entering time in the evening, 21:26:46 -0400
84
+ # make sure --at "5:00" is recorded for the local day, not GMC
86
85
 
87
86
 
@@ -0,0 +1,39 @@
1
+ module Tempo
2
+
3
+ # Reporter raises and error when view records of unknown format
4
+ class InvalidViewRecordError < Exception
5
+ end
6
+
7
+ # This error is raised when an existing time period conflicts with a time or time period
8
+ # All parameters are optional, and will be build into the error string if supplied
9
+ #
10
+ # Expected parameters are Time objects, but nil or strings will be handled as well
11
+ #
12
+ # examples:
13
+ #
14
+ # () => "time conflicts with existing record"
15
+ # (<8:00>, <10:00>) => "time conflicts with existing record: 8:00 - 10:00"
16
+ # (<8:00>, <10:00>, <9:00>) => "time <9:00> conflicts with existing record: 8:00 - 10:00"
17
+ # (<8:00>, <10:00>, <9:00>, <9:30>) => "time <9:00 - 9:30> conflicts with existing record: 8:00 - 10:00"
18
+ # (<8:00>, :running) => "time conflicts with existing record: 8:00 - running"
19
+ #
20
+ class TimeConflictError < ArgumentError
21
+
22
+ def initialize( start_time=nil, end_time=nil, target_start_time=nil, target_end_time=nil )
23
+
24
+ @end_time = (end_time.kind_of? Time) ? end_time.strftime('%H:%M') : end_time.to_s
25
+ @end_time = " - #{@end_time}" if !@end_time.empty?
26
+ @existing = (start_time.kind_of? Time) ? ": #{start_time.strftime('%H:%M')}#{@end_time}" : start_time.to_s
27
+
28
+ @target_end_time = (target_end_time.kind_of? Time) ? "#{target_end_time.strftime('%H:%M')}" : target_end_time.to_s
29
+ @target_end_time = " - #{@target_end_time}" if !@target_end_time.empty?
30
+ @target = (target_start_time.kind_of? Time) ? "<#{target_start_time.strftime('%H:%M')}#{@target_end_time}> " : ""
31
+
32
+ @message = "time #{@target}conflicts with existing record#{@existing}"
33
+ end
34
+
35
+ def to_s
36
+ @message
37
+ end
38
+ end
39
+ end
@@ -11,6 +11,7 @@ module Tempo
11
11
  # for example Jan 1, 2013 would be :"130101"
12
12
  # id counter is managed through the private methods
13
13
  # increase_id_counter and next_id below
14
+ #
14
15
  def id_counter time
15
16
  dsym = date_symbol time
16
17
  @id_counter = {} unless @id_counter.kind_of? Hash
@@ -26,6 +27,7 @@ module Tempo
26
27
  # all instances are saved in the index inherited from base.
27
28
  # Additionally, the days index organizes all instances into
28
29
  # arrays by day. This is used for saving to file.
30
+ #
29
31
  def days_index
30
32
  @days_index = {} unless @days_index.kind_of? Hash
31
33
  @days_index
@@ -56,6 +58,7 @@ module Tempo
56
58
  end
57
59
 
58
60
  # load all the records for a single day
61
+ #
59
62
  def load_day_record time
60
63
  dsym = date_symbol time
61
64
  if not days_index.has_key? dsym
@@ -65,6 +68,7 @@ module Tempo
65
68
  end
66
69
 
67
70
  # load the records for each day from time 1 to time 2
71
+ #
68
72
  def load_days_records time_1, time_2
69
73
 
70
74
  return if time_1.nil? || time_2.nil?
@@ -76,6 +80,7 @@ module Tempo
76
80
  end
77
81
 
78
82
  # load the records for the most recently recorded day
83
+ #
79
84
  def load_last_day
80
85
  reg = /(\d+)\.yaml/
81
86
  if records.last
@@ -1,17 +1,27 @@
1
+ # Time Record is an extension of the Log Model,
2
+ # it adds and end time, and verifies that no records overlap
3
+ # in their time periods. Additionally, one, and only on record
4
+ # can be running at any given time, and it can only be the most
5
+ # recent record.
6
+
1
7
  module Tempo
2
8
  module Model
9
+
3
10
  class TimeRecord < Tempo::Model::Log
4
11
  attr_accessor :project, :description
5
12
  attr_reader :start_time, :end_time, :tags
6
13
 
7
14
  class << self
8
15
 
16
+ # Only one record can be running at any given time. This record
17
+ # is the class current, and has and end time of :running
18
+ #
9
19
  def current
10
20
  return @current if @current && @current.end_time == :running
11
21
  @current = nil
12
22
  end
13
23
 
14
- def current=( instance )
24
+ def current=instance
15
25
  if instance.class == self
16
26
  @current = instance
17
27
  else
@@ -20,7 +30,7 @@ module Tempo
20
30
  end
21
31
  end
22
32
 
23
- def initialize(options={})
33
+ def initialize options={}
24
34
 
25
35
  # declare these first for model organization when sent to YAML
26
36
  @project_title = nil
@@ -28,10 +38,11 @@ module Tempo
28
38
  @start_time = nil
29
39
 
30
40
  # verify both start time and end time before sending to super
41
+ # super handles start_time, not end time
31
42
  options[:start_time] ||= Time.now
32
- verify_start_time options[:start_time]
33
43
  @end_time = options.fetch :end_time, :running
34
- verify_end_time options[:start_time], @end_time
44
+ verify_times options[:start_time], @end_time
45
+
35
46
  super options
36
47
 
37
48
  project = options.fetch :project, Tempo::Model::Project.current
@@ -40,67 +51,92 @@ module Tempo
40
51
  @tags = []
41
52
  tag options.fetch(:tags, [])
42
53
 
43
- # close out the running time record
44
- if running?
45
- if not self.class.current
46
- self.class.current = self
47
- else
48
-
49
- current = self.class.current
50
-
51
- # more recent entries exist, need to close out immediately
52
- if current.start_time > @start_time
53
- if current.start_time.day > @start_time.day
54
- out = self.class.end_of_day @start_time
55
- @end_time = out
56
- # TODO add a new record onto the next day
57
- else
58
- @end_time = current.start_time
59
- end
60
-
61
- # close out the last current record
62
- else
63
- self.class.close_current @start_time
64
- self.class.current = self
65
- end
66
- end
54
+ leave_only_one_running
55
+ end
67
56
 
68
- # close out any earlier running timerecords
57
+ def start_time= time
58
+ raise ArgumentError if !time.kind_of? Time
59
+ if @end_time != :running
60
+ @start_time = time if verify_times time, @end_time
69
61
  else
70
- if self.class.current
71
- if self.class.current.start_time < @start_time
72
- self.class.close_current @start_time
73
- end
74
- end
62
+ @start_time = time if verify_start_time time
75
63
  end
76
64
  end
77
65
 
78
- def start_time= time
79
- @start_time = time if verify_start_time time
66
+ # end time cannot be set to :running, only to a
67
+ # valid Time object
68
+ #
69
+ def end_time= time
70
+ raise ArgumentError if !time.kind_of? Time
71
+ @end_time = time if verify_times self.start_time, time
80
72
  end
81
73
 
82
- def end_time= time
83
- @end_time = time if verify_end_time self.start_time, time
74
+ # method for updating both times at once, necessary if it would
75
+ # cause a conflict to do them individually
76
+ #
77
+ def update_times start_time, end_time
78
+ raise ArgumentError if !start_time.kind_of? Time
79
+ raise ArgumentError if !end_time.kind_of? Time
80
+ verify_times start_time, end_time
81
+ @start_time = start_time
82
+ @end_time = end_time
83
+ leave_only_one_running
84
84
  end
85
85
 
86
+ # Public method to access verify start time,
87
+ # determine if an error will be raised
88
+ #
86
89
  def valid_start_time? time
90
+ return false if !time.kind_of? Time
87
91
  begin
88
- verify_start_time time
92
+ if @end_time != :running
93
+ verify_times time, @end_time
94
+ else
95
+ verify_start_time time
96
+ end
89
97
  rescue ArgumentError => e
90
98
  return false
91
99
  end
92
100
  true
93
101
  end
94
102
 
103
+ # Public method to access verify end time,
104
+ # determine if an error will be raised
105
+ #
95
106
  def valid_end_time? time
107
+ return false if !time.kind_of? Time
96
108
  begin
97
- verify_end_time self.start_time, time
109
+ verify_times self.start_time, time
98
110
  rescue ArgumentError => e
99
111
  return false
100
112
  end
101
113
  true
102
114
  end
103
115
 
116
+ # Returns the next record in time from the current record
117
+ # Remember, only records loaded from files will be available
118
+ # to compare against, so it is important to use the following
119
+ # methods defined in Log first to assure accuracy:
120
+ # * load_day_record
121
+ # * load_days_records
122
+ # * load_last_day
123
+ #
124
+ # uses start_time if end time is :running
125
+ #
126
+ def next_record
127
+ next_one = nil
128
+ end_time = ( @end_time.kind_of? Time ) ? @end_time : @start_time
129
+ self.class.index.each do |record|
130
+ next if record == self
131
+ if next_one == nil && record.start_time >= end_time
132
+ next_one = record
133
+ elsif record.start_time >= end_time && record.start_time < next_one.start_time
134
+ next_one = record
135
+ end
136
+ end
137
+ next_one
138
+ end
139
+
104
140
  def project_title
105
141
  Project.find_by_id( @project ).title if @project
106
142
  end
@@ -124,7 +160,7 @@ module Tempo
124
160
  record
125
161
  end
126
162
 
127
- def tag( tags )
163
+ def tag tags
128
164
  return unless tags and tags.kind_of? Array
129
165
  tags.each do |tag|
130
166
  tag.split.each {|t| @tags << t if ! @tags.include? t }
@@ -132,7 +168,7 @@ module Tempo
132
168
  @tags.sort!
133
169
  end
134
170
 
135
- def untag( tags )
171
+ def untag tags
136
172
  return unless tags and tags.kind_of? Array
137
173
  tags.each do |tag|
138
174
  tag.split.each {|t| @tags.delete t }
@@ -157,6 +193,64 @@ module Tempo
157
193
  end
158
194
  end
159
195
 
196
+ # If the current project end_time is :running, we
197
+ # need to update the class running record to the most
198
+ # recent record that is running. We also need to close out
199
+ # all other records at the start time of the next record,
200
+ # or the end of the day if it is the last record on that day.
201
+ #
202
+ # If the current project has an end_time, then we
203
+ # close any previous running record.
204
+ #
205
+ def leave_only_one_running
206
+
207
+ if running?
208
+
209
+ nxt_rcrd = next_record
210
+
211
+ # Nothing running, no newer records, make this one current
212
+ if self.class.current.nil? && nxt_rcrd.nil?
213
+ self.class.current = self
214
+
215
+ # This is the newest record, close out the running record
216
+ elsif self.class.current && nxt_rcrd.nil?
217
+ self.class.close_current @start_time
218
+ self.class.current = self
219
+
220
+ # newer records exits, close out on the next record start
221
+ # date, or end of day if next record is on another day.
222
+ #
223
+ # ? Do we care about the current record ?
224
+ else
225
+ #current = self.class.current
226
+
227
+ # # more recent running entries exist, close this record
228
+ # if current.start_time > @start_time
229
+ # if current.start_time.day > @start_time.day
230
+ # out = self.class.end_of_day @start_time
231
+ # @end_time = out
232
+ # else
233
+ # @end_time = current.start_time
234
+ # end
235
+
236
+
237
+ if nxt_rcrd.start_time.day == @start_time.day
238
+ @end_time = nxt_rcrd.start_time
239
+ else
240
+ @end_time = self.class.end_of_day @start_time
241
+ end
242
+ end
243
+
244
+ # Not running, but we still need to close out any earlier running timerecords
245
+ else
246
+ if self.class.current
247
+ if self.class.current.start_time < @start_time
248
+ self.class.close_current @start_time
249
+ end
250
+ end
251
+ end
252
+ end
253
+
160
254
  # check a time against all loaded instances, verify that it doesn't
161
255
  # fall in the middle of any closed time records
162
256
  #
@@ -172,13 +266,21 @@ module Tempo
172
266
  next if record.end_time == :running
173
267
  next if record == self
174
268
  if time < record.end_time
175
- raise ArgumentError, "Time conflict with existing record" if time_in_record? time, record
269
+ raise Tempo::TimeConflictError.new( record.start_time, record.end_time, time ) if time_in_record? time, record
176
270
  end
177
271
  end
178
272
  true
179
273
  end
180
274
 
181
- def verify_end_time start_time, end_time
275
+ # We never have an end time without a start time
276
+ # so this is also the equivalent of a verify_end_time method
277
+ # This method returns true for any valid start time, and an
278
+ # end time of :running. This condition, (currently only possible from init)
279
+ # requires a second check to close out all but the most recent time entry.
280
+ #
281
+ def verify_times start_time, end_time
282
+
283
+ verify_start_time start_time
182
284
 
183
285
  # TODO: a better check for :running conditions
184
286
  return true if end_time == :running
@@ -195,15 +297,16 @@ module Tempo
195
297
 
196
298
  self.class.days_index[dsym].each do |record|
197
299
  next if record == self
198
- raise ArgumentError, "Time conflict with existing record:" if time_span_intersects_record? start_time, end_time, record
300
+ raise Tempo::TimeConflictError.new( record.start_time, record.end_time, start_time, end_time ) if time_span_intersects_record? start_time, end_time, record
199
301
  end
200
302
  true
201
303
  end
202
304
 
203
305
  # this is used for both start time and end times,
204
- # so it will return true if the time is :running
205
- # or if it is exactly the record start or end time
206
- # these conditions need to be checked separately
306
+ # so it will return false if the time is :running
307
+ #
308
+ # It will return true if it is exactly the record start or end time
309
+ #
207
310
  def time_in_record? time, record
208
311
  return false if record.end_time == :running
209
312
  time >= record.start_time && time <= record.end_time
@@ -214,6 +317,7 @@ module Tempo
214
317
  # It does not invalidate a time span earlier than the record with a :running end time,
215
318
  # this condition must be accounted for separately.
216
319
  # It assumes a valid start and end time.
320
+ #
217
321
  def time_span_intersects_record? start_time, end_time, record
218
322
  if record.end_time == :running
219
323
  return true if start_time <= record.start_time && end_time > record.start_time
@@ -227,6 +331,7 @@ module Tempo
227
331
  end
228
332
 
229
333
  # returns the last minute of the day
334
+ #
230
335
  def self.end_of_day time
231
336
  Time.new(time.year, time.month, time.day, 23, 59)
232
337
  end
data/lib/tempo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Tempo
2
- VERSION = '0.1.3'
2
+ VERSION = '0.1.4'
3
3
  end
@@ -19,9 +19,6 @@
19
19
  module Tempo
20
20
  module Views
21
21
 
22
- class InvalidViewRecordError < Exception
23
- end
24
-
25
22
  module ViewRecords
26
23
 
27
24
  # The most simple view records, with a message string
data/lib/tempo.rb CHANGED
@@ -4,6 +4,7 @@
4
4
  require 'time_utilities.rb'
5
5
 
6
6
  require 'tempo/version.rb'
7
+ require 'tempo/exceptions.rb'
7
8
 
8
9
  require 'tempo/models/base.rb'
9
10
  require 'tempo/models/composite.rb'
data/tempo-cli.gemspec CHANGED
@@ -9,8 +9,6 @@ spec = Gem::Specification.new do |s|
9
9
  s.platform = Gem::Platform::RUBY
10
10
  s.summary = 'A command line time tracker for recording by day and by project'
11
11
  s.description = 'tempo-cli is a command line time tracking application. Record time spent on projects in YAML files, and manage them from the command line.'
12
- # Add your other files here if you make them
13
- # Add lib files to lib.tempo.rb
14
12
  s.files = `git ls-files`.split("\n")
15
13
  s.require_paths << 'lib'
16
14
  s.has_rdoc = true
@@ -0,0 +1,31 @@
1
+ require "test_helper"
2
+
3
+ describe Tempo::TimeConflictError do
4
+
5
+ before(:each) do
6
+ @start_time = Time.new(2014, 1, 2, 7, 15)
7
+ @end_time = Time.new(2104, 1, 2, 9, 15)
8
+ @target_start_time = Time.new(2104, 1, 2, 8, 15)
9
+ @target_end_time = Time.new(2104, 1, 2, 8, 45)
10
+ end
11
+
12
+ it "can create a message with a target duration" do
13
+ @exception = Tempo::TimeConflictError.new @start_time, @end_time, @target_start_time, @target_end_time
14
+ @exception.to_s.must_equal "time <08:15 - 08:45> conflicts with existing record: 07:15 - 09:15"
15
+ end
16
+
17
+ it "can create a message with a target time" do
18
+ @exception = Tempo::TimeConflictError.new @start_time, @end_time, @target_start_time
19
+ @exception.to_s.must_equal "time <08:15> conflicts with existing record: 07:15 - 09:15"
20
+ end
21
+
22
+ it "can create a message without a target time" do
23
+ @exception = Tempo::TimeConflictError.new @start_time, @end_time
24
+ @exception.to_s.must_equal "time conflicts with existing record: 07:15 - 09:15"
25
+ end
26
+
27
+ it "can handle running time entries" do
28
+ @exception = Tempo::TimeConflictError.new @start_time, :running, @target_start_time
29
+ @exception.to_s.must_equal "time <08:15> conflicts with existing record: 07:15 - running"
30
+ end
31
+ end
@@ -78,7 +78,7 @@ describe Tempo do
78
78
  Tempo::Model::MessageLog.index[0].message.must_equal "day 1 pet the sheep"
79
79
  end
80
80
 
81
- it "loads the records for a the most recent day and return day" do
81
+ it "loads records for most recent and return day" do
82
82
  log_record_factory
83
83
  last_day = Tempo::Model::MessageLog.load_last_day
84
84
 
@@ -1,4 +1,5 @@
1
1
  require "test_helper"
2
+ require "pry"
2
3
 
3
4
  describe Tempo do
4
5
 
@@ -66,12 +67,22 @@ describe Tempo do
66
67
  Tempo::Model::TimeRecord.current.must_equal nil
67
68
  end
68
69
 
69
- it "has a running? method" do
70
+ it "has a running boolean method" do
70
71
  time_record_factory
71
72
  @record_1.running?.must_equal false
72
73
  @record_6.running?.must_equal true
73
74
  end
74
75
 
76
+ it "has a next record method" do
77
+ time_record_factory
78
+ @record_1.next_record.must_equal @record_2
79
+ @record_2.next_record.must_equal @record_3
80
+ @record_3.next_record.must_equal @record_4
81
+ @record_4.next_record.must_equal @record_5
82
+ @record_5.next_record.must_equal @record_6
83
+ @record_6.next_record.must_equal nil
84
+ end
85
+
75
86
  it "has a duration method returning seconds" do
76
87
  time_record_factory
77
88
  @record_1.duration.must_equal 1800
@@ -92,16 +103,38 @@ describe Tempo do
92
103
  @record_3.end_time.must_equal Time.new(2014,1,1,23,59)
93
104
  end
94
105
 
95
- it "closes out new projects if not the most recent" do
106
+ it "closes out new projects at if not the most recent" do
107
+ # Verify project closes out at the start time of the next time record,
108
+ # which is not necessarily the running record
96
109
  project_factory
97
110
  Tempo::Model::TimeRecord.clear_all
98
- @record_2 = Tempo::Model::TimeRecord.new({ project: @project_2, description: "day 1 drinking coffee, check on the mushrooms", start_time: Time.new(2014, 1, 1, 7, 30 ) })
99
- @record_1 = Tempo::Model::TimeRecord.new({ project: @project_1, description: "day 1 pet the sheep", start_time: Time.new(2014, 1, 1, 7 ) })
111
+ @record_1 = Tempo::Model::TimeRecord.new({project: @project_2,
112
+ description: "day 1 drinking coffee, check on the mushrooms",
113
+ start_time: Time.new(2014, 1, 1, 9, 30 ) })
114
+ @record_2 = Tempo::Model::TimeRecord.new({project: @project_2,
115
+ description: "day 1 pet the sheep",
116
+ start_time: Time.new(2014, 1, 1, 10, 30 ) })
117
+ @record_3 = Tempo::Model::TimeRecord.new({project: @project_1,
118
+ description: "make the coffee, check the weather",
119
+ start_time: Time.new(2014, 1, 1, 7 ) })
100
120
  Tempo::Model::TimeRecord.current.must_equal @record_2
101
- @record_1.end_time.must_equal @record_2.start_time
121
+ @record_3.end_time.must_equal @record_1.start_time
122
+ end
123
+
124
+ it "on init closes out new projects on the same day" do
125
+ project_factory
126
+ Tempo::Model::TimeRecord.clear_all
127
+ @record_1 = Tempo::Model::TimeRecord.new({project: @project_2,
128
+ description: "day 1 drinking coffee, check on the mushrooms",
129
+ start_time: Time.new(2014, 1, 2, 10, 30 ) })
130
+ @record_2 = Tempo::Model::TimeRecord.new({project: @project_2,
131
+ description: "day 1 pet the sheep",
132
+ start_time: Time.new(2014, 1, 1, 10, 30 ) })
133
+ Tempo::Model::TimeRecord.current.must_equal @record_1
134
+ @record_2.end_time.must_equal Time.new(2014, 1, 1, 23, 59 )
102
135
  end
103
136
 
104
- it "closes out all projects when init with an end time" do
137
+ it "closes earlier running when init with an end time" do
105
138
  project_factory
106
139
  Tempo::Model::TimeRecord.clear_all
107
140
  @record_1 = Tempo::Model::TimeRecord.new({ project: @project_2, description: "day 1 drinking coffee, check on the mushrooms", start_time: Time.new(2014, 1, 1, 7, 30 ) })
@@ -112,12 +145,12 @@ describe Tempo do
112
145
 
113
146
  it "errors when start time inside existing record" do
114
147
  time_record_factory
115
- proc { Tempo::Model::TimeRecord.new({ start_time: Time.new(2014, 1, 1, 12 ) }) }.must_raise ArgumentError
148
+ proc { Tempo::Model::TimeRecord.new({ start_time: Time.new(2014, 1, 1, 12 ) }) }.must_raise Tempo::TimeConflictError
116
149
  end
117
150
 
118
151
  it "errors when start time same as existing record" do
119
152
  time_record_factory
120
- proc { Tempo::Model::TimeRecord.new({ start_time: @record_1.start_time }) }.must_raise ArgumentError
153
+ proc { Tempo::Model::TimeRecord.new({ start_time: @record_1.start_time }) }.must_raise Tempo::TimeConflictError
121
154
  end
122
155
 
123
156
  it "errors when end time is before start time" do
@@ -146,19 +179,19 @@ describe Tempo do
146
179
  it "errors when end time in existing record" do
147
180
  Tempo::Model::TimeRecord.clear_all
148
181
  r1 = Tempo::Model::TimeRecord.new({ start_time: Time.new(2014, 1, 1, 10 ), end_time: Time.new(2014, 1, 1, 12 ) })
149
- proc { Tempo::Model::TimeRecord.new({ start_time: Time.new(2014, 1, 1, 8 ), end_time: Time.new(2014, 1, 1, 11 ) }) }.must_raise ArgumentError
182
+ proc { Tempo::Model::TimeRecord.new({ start_time: Time.new(2014, 1, 1, 8 ), end_time: Time.new(2014, 1, 1, 11 ) }) }.must_raise Tempo::TimeConflictError
150
183
  end
151
184
 
152
185
  it "errors when record spans an existing record" do
153
186
  Tempo::Model::TimeRecord.clear_all
154
187
  r1 = Tempo::Model::TimeRecord.new({ start_time: Time.new(2014, 1, 1, 10 ), end_time: Time.new(2014, 1, 1, 11 ) })
155
- proc { Tempo::Model::TimeRecord.new({ start_time: Time.new(2014, 1, 1, 8 ), end_time: Time.new(2014, 1, 1, 12 ) }) }.must_raise ArgumentError
188
+ proc { Tempo::Model::TimeRecord.new({ start_time: Time.new(2014, 1, 1, 8 ), end_time: Time.new(2014, 1, 1, 12 ) }) }.must_raise Tempo::TimeConflictError
156
189
  end
157
190
 
158
191
  it "errors when record spans a running record" do
159
192
  Tempo::Model::TimeRecord.clear_all
160
193
  r1 = Tempo::Model::TimeRecord.new({ start_time: Time.new(2014, 1, 1, 10 ) })
161
- proc { Tempo::Model::TimeRecord.new({ start_time: Time.new(2014, 1, 1, 8 ), end_time: Time.new(2014, 1, 1, 12 ) }) }.must_raise ArgumentError
194
+ proc { Tempo::Model::TimeRecord.new({ start_time: Time.new(2014, 1, 1, 8 ), end_time: Time.new(2014, 1, 1, 12 ) }) }.must_raise Tempo::TimeConflictError
162
195
  end
163
196
 
164
197
  it "has a valid start time check for existing record" do
@@ -173,6 +206,51 @@ describe Tempo do
173
206
  @record_2.valid_end_time?(@record_3.end_time).must_equal false
174
207
  end
175
208
 
209
+ it "can validate and update a start time" do
210
+ project_factory
211
+ Tempo::Model::TimeRecord.clear_all
212
+ @record = Tempo::Model::TimeRecord.new({project: @project_1,
213
+ start_time: Time.new(2014, 1, 1, 8 ),
214
+ end_time: Time.new(2014, 1, 1, 10 ) })
215
+ proc { @record.start_time = Time.new(2014, 1, 1, 11 )}.must_raise ArgumentError
216
+
217
+ @record.start_time = Time.new(2014, 1, 1, 9 )
218
+ @record.start_time.must_equal Time.new(2014, 1, 1, 9 )
219
+ end
220
+
221
+ it "can validate and update an end time" do
222
+ project_factory
223
+ Tempo::Model::TimeRecord.clear_all
224
+ @record = Tempo::Model::TimeRecord.new({project: @project_1,
225
+ start_time: Time.new(2014, 1, 1, 8 ),
226
+ end_time: Time.new(2014, 1, 1, 10 ) })
227
+
228
+ proc { @record.end_time = Time.new(2014, 1, 1, 7 )}.must_raise ArgumentError
229
+ @record.end_time = Time.new(2014, 1, 1, 11 )
230
+ @record.end_time.must_equal Time.new(2014, 1, 1, 11 )
231
+ end
232
+
233
+ it "can update a start and end time" do
234
+ project_factory
235
+ Tempo::Model::TimeRecord.clear_all
236
+ @record = Tempo::Model::TimeRecord.new({project: @project_3,
237
+ start_time: Time.new(2014, 1, 1, 8 ),
238
+ end_time: Time.new(2014, 1, 1, 10 ) })
239
+
240
+ @record.update_times(Time.new(2014, 1, 1, 8 ), Time.new(2014, 1, 1, 10 ))
241
+ end
242
+
243
+ it "closes earlier running when start time is updated" do
244
+ project_factory
245
+ Tempo::Model::TimeRecord.clear_all
246
+ @record_1 = Tempo::Model::TimeRecord.new({ project: @project_2, description: "record 1", start_time: Time.new(2014, 1, 1, 10 ) })
247
+ @record_2 = Tempo::Model::TimeRecord.new({ project: @project_1, description: "record 2", start_time: Time.new(2014, 1, 1, 7 ), end_time: Time.new(2014, 1, 1, 8 ) })
248
+ Tempo::Model::TimeRecord.current.must_equal @record_1
249
+
250
+ @record_2.update_times(Time.new(2014, 1, 1, 11 ), Time.new(2014, 1, 1, 12 ))
251
+ Tempo::Model::TimeRecord.current.must_equal nil
252
+ end
253
+
176
254
  it "comes with freeze dry for free" do
177
255
  Tempo::Model::TimeRecord.clear_all
178
256
  time_record_factory
@@ -19,7 +19,7 @@ describe Tempo do
19
19
 
20
20
  it "raises and error when view records of unknown format" do
21
21
  record = "an invalid record object"
22
- proc { Tempo::Views::Reporter.add_view_record record }.must_raise Tempo::Views::InvalidViewRecordError
22
+ proc { Tempo::Views::Reporter.add_view_record record }.must_raise Tempo::InvalidViewRecordError
23
23
  end
24
24
 
25
25
  it "sends the reports to the screen formatter on report" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tempo-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-12-02 00:00:00.000000000 Z
12
+ date: 2013-12-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -161,6 +161,7 @@ files:
161
161
  - lib/tempo/controllers/report_controller.rb
162
162
  - lib/tempo/controllers/start_controller.rb
163
163
  - lib/tempo/controllers/update_controller.rb
164
+ - lib/tempo/exceptions.rb
164
165
  - lib/tempo/models/base.rb
165
166
  - lib/tempo/models/composite.rb
166
167
  - lib/tempo/models/log.rb
@@ -186,6 +187,7 @@ files:
186
187
  - test/lib/file_record/record_test.rb
187
188
  - test/lib/tempo/controllers/base_controller_test.rb
188
189
  - test/lib/tempo/controllers/project_controller_test.rb
190
+ - test/lib/tempo/exceptions_test.rb
189
191
  - test/lib/tempo/models/base_test.rb
190
192
  - test/lib/tempo/models/composite_test.rb
191
193
  - test/lib/tempo/models/log_test.rb