tempo-cli 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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