one_inch_punch 0.3.3 → 0.4.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.
data/History.txt CHANGED
@@ -1,3 +1,14 @@
1
+ == 0.4.0 2008-08-17
2
+
3
+ * 1 minor enhancement:
4
+ * added 'summary' command
5
+ * shows a breakdown of time spent on a project, using the times in log messages to indicate activities
6
+ * accepts before/after options
7
+ * 1 tiny enhancement
8
+ * full status now includes the last log message if the project is punched in
9
+ * 1 tiny bugfix
10
+ * full status now works with "short" (only display what's punched-in) instead of always showing 'out'
11
+
1
12
  == 0.3.3 2008-06-25
2
13
 
3
14
  * 1 tiny enhancement:
data/bin/punch CHANGED
@@ -118,6 +118,13 @@ commands = {
118
118
  end
119
119
  end,
120
120
  'list' => lambda { |project| puts Punch.list(project, OPTIONS).to_yaml },
121
+ 'summary' => lambda do |project|
122
+ if project
123
+ puts Punch.summary(project, OPTIONS.merge(:format => true)).to_yaml
124
+ else
125
+ puts "Project required"
126
+ end
127
+ end
121
128
  }
122
129
 
123
130
  if command_code = commands[command]
data/lib/punch.rb CHANGED
@@ -2,6 +2,8 @@ $:.unshift(File.dirname(__FILE__)) unless
2
2
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
3
 
4
4
  require 'yaml'
5
+ require 'time'
6
+ require 'enumerator'
5
7
  require 'punch/core_ext'
6
8
  require 'punch/instance'
7
9
 
@@ -49,7 +51,8 @@ class Punch
49
51
  stats = {}
50
52
  projects.each { |project| stats[project] = status(project, options) }
51
53
  if options[:short]
52
- stats.reject! { |k, v| v != 'in' }
54
+ stats.reject! { |k, v| v.nil? }
55
+ stats.reject! { |k, v| v != 'in' and v[:status] != 'in' }
53
56
  stats = 'out' if stats.empty?
54
57
  end
55
58
  return stats
@@ -67,7 +70,13 @@ class Punch
67
70
  return status unless options[:full]
68
71
  return status if status.nil?
69
72
 
70
- { :status => status, :time => time_data[status] }
73
+ if status == 'in'
74
+ message = (time_data['log'] || []).last
75
+ end
76
+
77
+ result = { :status => status, :time => time_data[status] }
78
+ result[:message] = message if message
79
+ result
71
80
  end
72
81
 
73
82
  def out?(project)
@@ -142,6 +151,34 @@ class Punch
142
151
  true
143
152
  end
144
153
 
154
+ def summary(project, options = {})
155
+ return unless list_data = list(project, options)
156
+ summary = Hash.new(0)
157
+
158
+ list_data.each do |time_data|
159
+ unless (time_data['log'] || []).empty?
160
+ log = time_data['log'].collect do |l|
161
+ msg, time = l.split('@')
162
+ msg = msg.strip
163
+ msg = 'unspecified' if msg == 'punch in'
164
+ { :msg => msg, :time => Time.parse(time) }
165
+ end
166
+
167
+ log << { :msg => 'punch out', :time => Time.now } unless log.last[:msg] == 'punch out'
168
+
169
+ log.each_cons(2) do |a, b|
170
+ summary[a[:msg]] += (b[:time] - a[:time]).to_i
171
+ end
172
+ else
173
+ summary['unspecified'] += ((time_data['out'] || Time.now) - time_data['in']).to_i
174
+ end
175
+ end
176
+
177
+ summary.reject! { |k, v| v == 0 }
178
+ summary.each { |k, v| summary[k] = v.elapsed_time } if options[:format]
179
+ summary
180
+ end
181
+
145
182
 
146
183
  private
147
184
 
@@ -37,6 +37,10 @@ class Punch
37
37
  self.class.log(project, message, options)
38
38
  end
39
39
 
40
+ def summary(options = {})
41
+ self.class.summary(project, options)
42
+ end
43
+
40
44
  def ==(other)
41
45
  project == other.project
42
46
  end
data/lib/punch/version.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  class Punch
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 3
5
- TINY = 3
4
+ MINOR = 4
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -190,6 +190,11 @@ describe 'punch command' do
190
190
  run_command('status', '--short')
191
191
  end
192
192
 
193
+ it 'should handle both --short and --full options together' do
194
+ Punch.should.receive(:status).with(nil, :short => true, :full => true)
195
+ run_command('status', '--short', '--full')
196
+ end
197
+
193
198
  it 'should not write the data' do
194
199
  Punch.should.receive(:write).never
195
200
  run_command('status')
@@ -706,6 +711,99 @@ describe 'punch command' do
706
711
  end
707
712
  end
708
713
 
714
+ describe "when the command is 'summary'" do
715
+ before do
716
+ Punch.stub!(:summary)
717
+ end
718
+
719
+ it 'should load punch data' do
720
+ Punch.should.receive(:load)
721
+ run_command('summary', @project)
722
+ end
723
+
724
+ it 'should get the summary for the requested project' do
725
+ Punch.should.receive(:summary) do |proj, _|
726
+ proj.should == @project
727
+ end
728
+ run_command('summary', @project)
729
+ end
730
+
731
+ it 'should output the summary' do
732
+ result = 'summary data'
733
+ Punch.stub!(:summary).and_return(result)
734
+ self.should.receive(:puts).with(result.to_yaml)
735
+ run_command('summary', @project)
736
+ end
737
+
738
+ describe 'when options specified' do
739
+ it "should pass on an 'after' time option given by --after" do
740
+ time_option = '2008-08-26 09:47'
741
+ time = Time.local(2008, 8, 26, 9, 47)
742
+ Punch.should.receive(:summary) do |proj, options|
743
+ proj.should == @project
744
+ options[:after].should == time
745
+ end
746
+ run_command('summary', @project, '--after', time_option)
747
+ end
748
+
749
+ it "should pass on a 'before' time option given by --before" do
750
+ time_option = '2008-08-23 15:39'
751
+ time = Time.local(2008, 8, 23, 15, 39)
752
+ Punch.should.receive(:summary) do |proj, options|
753
+ proj.should == @project
754
+ options[:before].should == time
755
+ end
756
+ run_command('summary', @project, '--before', time_option)
757
+ end
758
+
759
+ it 'should handle a time option given as a date' do
760
+ time_option = '2008-08-23'
761
+ time = Time.local(2008, 8, 23)
762
+ Punch.should.receive(:summary) do |proj, options|
763
+ proj.should == @project
764
+ options[:before].should == time
765
+ end
766
+ run_command('summary', @project, '--before', time_option)
767
+ end
768
+
769
+ it 'should also pass the formatting option' do
770
+ time_option = '2008-08-23'
771
+ Punch.should.receive(:summary) do |proj, options|
772
+ proj.should == @project
773
+ options[:format].should == true
774
+ end
775
+ run_command('summary', @project, '--before', time_option)
776
+ end
777
+ end
778
+
779
+ it 'should pass only the formatting option if no options specified' do
780
+ Punch.should.receive(:summary) do |proj, options|
781
+ proj.should == @project
782
+ options[:format].should == true
783
+ end
784
+ run_command('summary', @project)
785
+ end
786
+
787
+ it 'should not write the data' do
788
+ Punch.should.receive(:write).never
789
+ run_command('summary')
790
+ end
791
+
792
+ describe 'when no project given' do
793
+ it 'should display an error message' do
794
+ self.should.receive(:puts) do |output|
795
+ output.should.match(/project.+require/i)
796
+ end
797
+ run_command('summary')
798
+ end
799
+
800
+ it 'should not punch in' do
801
+ Punch.should.receive(:summary).never
802
+ run_command('summary')
803
+ end
804
+ end
805
+ end
806
+
709
807
  describe 'when the command is unknown' do
710
808
  it 'should not error' do
711
809
  lambda { run_command('bunk') }.should.not.raise
@@ -382,6 +382,56 @@ describe Punch, 'instance' do
382
382
  end
383
383
  end
384
384
 
385
+ it 'should give project summary' do
386
+ @punch.should.respond_to(:summary)
387
+ end
388
+
389
+ describe 'giving project summary' do
390
+ before do
391
+ @summary = 'summary val'
392
+ Punch.stub!(:summary).and_return(@summary)
393
+ end
394
+
395
+ it 'should accept options' do
396
+ lambda { @punch.summary(:format => true) }.should.not.raise(ArgumentError)
397
+ end
398
+
399
+ it 'should not require options' do
400
+ lambda { @punch.summary }.should.not.raise(ArgumentError)
401
+ end
402
+
403
+ it 'should delegate to the class' do
404
+ Punch.should.receive(:summary)
405
+ @punch.summary
406
+ end
407
+
408
+ it 'should pass the project when delegating to the class' do
409
+ Punch.should.receive(:summary) do |proj, _|
410
+ proj.should == @project
411
+ end
412
+ @punch.summary
413
+ end
414
+
415
+ it 'should pass the options when delegating to the class' do
416
+ options = { :format => true }
417
+ Punch.should.receive(:summary) do |_, opts|
418
+ opts.should == options
419
+ end
420
+ @punch.summary(options)
421
+ end
422
+
423
+ it 'should pass an empty hash if no options given' do
424
+ Punch.should.receive(:summary) do |_, opts|
425
+ opts.should == {}
426
+ end
427
+ @punch.summary
428
+ end
429
+
430
+ it 'should return the value returned by the class method' do
431
+ @punch.summary.should == @summary
432
+ end
433
+ end
434
+
385
435
  describe 'equality' do
386
436
  it 'should be equal to another instance for the same project' do
387
437
  Punch.new('proj').should == Punch.new('proj')
data/spec/punch_spec.rb CHANGED
@@ -228,7 +228,7 @@ describe Punch do
228
228
  Punch.status('other project', :full => true).should.be.nil
229
229
  end
230
230
 
231
- it 'should return the full status if all projects if nil is given as the project' do
231
+ it 'should return the full status of all projects if nil is given as the project' do
232
232
  Punch.status(nil, :full => true).should == {
233
233
  @projects['out'] => { :status => 'out', :time => @now + 12 },
234
234
  @projects['in'] => { :status => 'in', :time => @now }
@@ -242,68 +242,150 @@ describe Punch do
242
242
  }
243
243
  end
244
244
 
245
- describe 'when given a :short option' do
246
- it "should return 'in' if the project is currently punched in" do
247
- Punch.status(@projects['in'], :short => true).should == 'in'
245
+ it 'should include a message for a punched-in project with log messages' do
246
+ message = 'some test message'
247
+ @data[@projects['in']].last['log'] = [message]
248
+ Punch.status(@projects['in'], :full => true).should == { :status => 'in', :time => @now, :message => message }
249
+ end
250
+
251
+ it 'should use the last log message for punched-in projects' do
252
+ message = 'some test message'
253
+ @data[@projects['in']].last['log'] = ['some other message', message]
254
+ Punch.status(@projects['in'], :full => true).should == { :status => 'in', :time => @now, :message => message }
255
+ end
256
+
257
+ it 'should not include a message for a punched-out project with log messages' do
258
+ @data[@projects['out']].last['log'] = ['some message']
259
+ Punch.status(@projects['out'], :full => true).should == { :status => 'out', :time => @now + 12 }
260
+ end
261
+ end
262
+
263
+ describe 'when given a :short option' do
264
+ it "should return 'in' if the project is currently punched in" do
265
+ Punch.status(@projects['in'], :short => true).should == 'in'
266
+ end
267
+
268
+ it "should return 'out' if the project is currently punched out" do
269
+ Punch.status(@projects['out'], :short => true).should == 'out'
270
+ end
271
+
272
+ it 'should return nil if project does not exist' do
273
+ Punch.status('other project', :short => true).should.be.nil
274
+ end
275
+
276
+ describe 'handling multiple projects' do
277
+ before do
278
+ @projects['in2'] = 'bingbang'
279
+ @projects['out2'] = 'boopadope'
280
+ @data[@projects['in2']] = [ { 'in' => @now - 5 } ]
281
+ @data[@projects['out2']] = [ { 'in' => @now - 500, 'out' => @now - 20 } ]
282
+ Punch.data = @data
248
283
  end
249
284
 
250
- it "should return 'out' if the project is currently punched out" do
251
- Punch.status(@projects['out'], :short => true).should == 'out'
285
+ it 'should return just the punched-in projects if nil is given as the project' do
286
+ Punch.status(nil, :short => true).should == {
287
+ @projects['in'] => 'in',
288
+ @projects['in2'] => 'in'
289
+ }
252
290
  end
253
291
 
254
- it 'should return nil if project does not exist' do
255
- Punch.status('other project', :short => true).should.be.nil
292
+ it 'should return just the punched-in projects if no project given' do
293
+ Punch.status(:short => true).should == {
294
+ @projects['in'] => 'in',
295
+ @projects['in2'] => 'in'
296
+ }
256
297
  end
257
298
 
258
- describe 'handling multiple projects' do
259
- before do
260
- @projects['in2'] = 'bingbang'
261
- @projects['out2'] = 'boopadope'
262
- @data[@projects['in2']] = [ { 'in' => @now - 5 } ]
263
- @data[@projects['out2']] = [ { 'in' => @now - 500, 'out' => @now - 20 } ]
264
- Punch.data = @data
265
- end
299
+ it 'should not include empty projects' do
300
+ @data['empty_project'] = []
301
+ Punch.data = @data
266
302
 
267
- it 'should return just the punched-in projects if nil is given as the project' do
268
- Punch.status(nil, :short => true).should == {
269
- @projects['in'] => 'in',
270
- @projects['in2'] => 'in'
271
- }
272
- end
303
+ Punch.status(:short => true).should == {
304
+ @projects['in'] => 'in',
305
+ @projects['in2'] => 'in'
306
+ }
307
+ end
308
+
309
+ it "should return 'out' if all projects are punched out" do
310
+ @data.delete(@projects['in'])
311
+ @data.delete(@projects['in2'])
312
+ Punch.data = @data
273
313
 
274
- it 'should return just the punched-in projects if no project given' do
275
- Punch.status(:short => true).should == {
276
- @projects['in'] => 'in',
277
- @projects['in2'] => 'in'
278
- }
279
- end
314
+ Punch.status(:short => true).should == 'out'
315
+ end
316
+
317
+ it "should return 'out' if all projects are punched out or empty" do
318
+ @data.delete(@projects['in'])
319
+ @data.delete(@projects['in2'])
320
+ @data['empty_project'] = []
321
+ Punch.data = @data
322
+
323
+ Punch.status(:short => true).should == 'out'
324
+ end
325
+ end
326
+ end
327
+
328
+ describe 'when given both :short and :full options' do
329
+ it 'should return the full status of a punched-in project' do
330
+ Punch.status(@projects['in'], :short => true, :full => true).should == { :status => 'in', :time => @now }
331
+ end
332
+
333
+ it 'should return the full status of a punched-out project' do
334
+ Punch.status(@projects['out'], :short => true, :full => true).should == { :status => 'out', :time => @now + 12 }
335
+ end
336
+
337
+ it 'should return nil if project does not exist' do
338
+ Punch.status('other project', :short => true, :full => true).should.be.nil
339
+ end
340
+
341
+ describe 'handling multiple projects' do
342
+ before do
343
+ @projects['in2'] = 'bingbang'
344
+ @projects['out2'] = 'boopadope'
345
+ @data[@projects['in2']] = [ { 'in' => @now - 5 } ]
346
+ @data[@projects['out2']] = [ { 'in' => @now - 500, 'out' => @now - 20 } ]
347
+ Punch.data = @data
348
+ end
349
+
350
+ it 'should return the full status of just the punched-in projects if nil is given as the project' do
351
+ Punch.status(nil, :short => true, :full => true).should == {
352
+ @projects['in'] => { :status => 'in', :time => @now },
353
+ @projects['in2'] => { :status => 'in', :time => @now - 5 }
354
+ }
355
+ end
356
+
357
+ it 'should return the full status of just the punched-in projects if no project given' do
358
+ Punch.status(:short => true, :full => true).should == {
359
+ @projects['in'] => { :status => 'in', :time => @now },
360
+ @projects['in2'] => { :status => 'in', :time => @now - 5 }
361
+ }
362
+ end
363
+
364
+ it 'should not include empty projects' do
365
+ @data['empty_project'] = []
366
+ Punch.data = @data
280
367
 
281
- it 'should not include empty projects' do
282
- @data['empty_project'] = []
283
- Punch.data = @data
284
-
285
- Punch.status(:short => true).should == {
286
- @projects['in'] => 'in',
287
- @projects['in2'] => 'in'
288
- }
289
- end
368
+ Punch.status(:short => true, :full => true).should == {
369
+ @projects['in'] => { :status => 'in', :time => @now },
370
+ @projects['in2'] => { :status => 'in', :time => @now - 5 }
371
+ }
372
+ end
373
+
374
+ it "should return 'out' if all projects are punched out" do
375
+ @data.delete(@projects['in'])
376
+ @data.delete(@projects['in2'])
377
+ Punch.data = @data
290
378
 
291
- it "should return 'out' if all projects are punched out" do
292
- @data.delete(@projects['in'])
293
- @data.delete(@projects['in2'])
294
- Punch.data = @data
295
-
296
- Punch.status(:short => true).should == 'out'
297
- end
379
+ Punch.status(:short => true, :full => true).should == 'out'
380
+ end
381
+
382
+ it "should return 'out' if all projects are punched out or empty" do
383
+ @data.delete(@projects['in'])
384
+ @data.delete(@projects['in2'])
385
+ @data['empty_project'] = []
386
+ Punch.data = @data
298
387
 
299
- it "should return 'out' if all projects are punched out or empty" do
300
- @data.delete(@projects['in'])
301
- @data.delete(@projects['in2'])
302
- @data['empty_project'] = []
303
- Punch.data = @data
304
-
305
- Punch.status(:short => true).should == 'out'
306
- end
388
+ Punch.status(:short => true, :full => true).should == 'out'
307
389
  end
308
390
  end
309
391
  end
@@ -1404,4 +1486,158 @@ describe Punch do
1404
1486
  end
1405
1487
  end
1406
1488
  end
1489
+
1490
+ it 'should provide a summary of project time use' do
1491
+ Punch.should.respond_to(:summary)
1492
+ end
1493
+
1494
+ describe 'providing a summary of project time use' do
1495
+ def format_time(time)
1496
+ time.strftime()
1497
+ end
1498
+
1499
+ before do
1500
+ @message = 'test usage'
1501
+ @now = Time.now
1502
+ Time.stub!(:now).and_return(@now)
1503
+ @project = 'test project'
1504
+ @data = { @project => [ {'in' => @now - 500, 'out' => @now - 100} ] }
1505
+ @time_data = @data[@project].last
1506
+ @time_format = '%Y-%m-%dT%H:%M:%S%z'
1507
+ @time_data['log'] = ["punch in @ #{@time_data['in'].strftime(@time_format)}", "#{@message} @ #{@time_data['in'].strftime(@time_format)}", "punch out @ #{@time_data['out'].strftime(@time_format)}"]
1508
+
1509
+ Punch.instance_eval do
1510
+ class << self
1511
+ public :data, :data=
1512
+ end
1513
+ end
1514
+ Punch.data = @data
1515
+ end
1516
+
1517
+ it 'should accept a project name' do
1518
+ lambda { Punch.summary('proj') }.should.not.raise(ArgumentError)
1519
+ end
1520
+
1521
+ it 'should require a project name' do
1522
+ lambda { Punch.summary }.should.raise(ArgumentError)
1523
+ end
1524
+
1525
+ describe 'when the project exists' do
1526
+ it 'should use the log message to indicate time usage' do
1527
+ Punch.summary(@project).should == { @message => 400 }
1528
+ end
1529
+
1530
+ it 'should break down a punched-in time based on log message times' do
1531
+ other_message = 'some other message'
1532
+ @time_data['log'][-1,0] = "#{other_message} @ #{(@time_data['out'] - 100).strftime(@time_format)}"
1533
+ Punch.summary(@project).should == { @message => 300, other_message => 100 }
1534
+ end
1535
+
1536
+ it 'should leave out any messages with empty times' do
1537
+ other_message = 'some other message'
1538
+ @time_data['log'][-1,0] = "#{other_message} @ #{(@time_data['out'] - 100).strftime(@time_format)}"
1539
+ @time_data['log'][-1,0] = "some third message @ #{(@time_data['out']).strftime(@time_format)}"
1540
+ Punch.summary(@project).should == { @message => 300, other_message => 100 }
1541
+ end
1542
+
1543
+ it 'should record unspecified time use' do
1544
+ @time_data['log'][1] = "#{@message} @ #{(@time_data['in'] + 100).strftime(@time_format)}"
1545
+ Punch.summary(@project).should == { @message => 300, 'unspecified' => 100 }
1546
+ end
1547
+
1548
+ it 'should record the block of time as unspecified if there are no log messages' do
1549
+ @time_data['log'] = []
1550
+ Punch.summary(@project).should == { 'unspecified' => 400 }
1551
+ end
1552
+
1553
+ it 'should record the block of time as unspecified if there is no log' do
1554
+ @time_data.delete('log')
1555
+ Punch.summary(@project).should == { 'unspecified' => 400 }
1556
+ end
1557
+
1558
+ it 'should summarize and combine all time data for the project' do
1559
+ other_message = 'some other message'
1560
+ @data = { @project => [
1561
+ {'in' => @now - 650, 'out' => @now - 600, 'log' => ["punch in @ #{(@now - 650).strftime(@time_format)}", "#{@message} @ #{(@now - 650).strftime(@time_format)}", "punch out @ #{(@now - 600).strftime(@time_format)}"]},
1562
+ {'in' => @now - 400, 'out' => @now - 350, 'log' => ["punch in @ #{(@now - 400).strftime(@time_format)}", "punch out @ #{(@now - 350).strftime(@time_format)}"]},
1563
+ {'in' => @now - 300, 'out' => @now - 150, 'log' => ["punch in @ #{(@now - 300).strftime(@time_format)}", "#{@message} @ #{(@now - 200).strftime(@time_format)}", "punch out @ #{(@now - 150).strftime(@time_format)}"]},
1564
+ {'in' => @now - 100, 'out' => @now + 250, 'log' => ["punch in @ #{(@now - 100).strftime(@time_format)}", "#{other_message} @ #{(@now - 50).strftime(@time_format)}", "punch out @ #{(@now + 250).strftime(@time_format)}"]}
1565
+ ] }
1566
+ Punch.data = @data
1567
+
1568
+ Punch.summary(@project).should == { 'unspecified' => 200, @message => 100, other_message => 300 }
1569
+ end
1570
+
1571
+ it 'should allow options' do
1572
+ lambda { Punch.summary('proj', :after => Time.now) }.should.not.raise(ArgumentError)
1573
+ end
1574
+
1575
+ describe 'handling options' do
1576
+ before do
1577
+ @other_message = 'some other message'
1578
+ @data = { @project => [
1579
+ {'in' => @now - 650, 'out' => @now - 600, 'log' => ["punch in @ #{(@now - 650).strftime(@time_format)}", "#{@message} @ #{(@now - 650).strftime(@time_format)}", "punch out @ #{(@now - 600).strftime(@time_format)}"]},
1580
+ {'in' => @now - 400, 'out' => @now - 350, 'log' => ["punch in @ #{(@now - 400).strftime(@time_format)}", "punch out @ #{(@now - 350).strftime(@time_format)}"]},
1581
+ {'in' => @now - 300, 'out' => @now - 150, 'log' => ["punch in @ #{(@now - 300).strftime(@time_format)}", "#{@message} @ #{(@now - 200).strftime(@time_format)}", "punch out @ #{(@now - 150).strftime(@time_format)}"]},
1582
+ {'in' => @now - 100, 'out' => @now + 250, 'log' => ["punch in @ #{(@now - 100).strftime(@time_format)}", "#{@other_message} @ #{(@now - 50).strftime(@time_format)}", "punch out @ #{(@now + 250).strftime(@time_format)}"]}
1583
+ ] }
1584
+ Punch.data = @data
1585
+ end
1586
+
1587
+ it 'should restrict the summary to times only after a certain time' do
1588
+ Punch.summary(@project, :after => @now - 401).should == { 'unspecified' => 200, @message => 50, @other_message => 300 }
1589
+ end
1590
+
1591
+ it 'should restrict the summary to times only before a certain time' do
1592
+ Punch.summary(@project, :before => @now - 149).should == { 'unspecified' => 150, @message => 100 }
1593
+ end
1594
+
1595
+ it 'should restrict the summary to times only within a time range' do
1596
+ Punch.summary(@project, :after => @now - 401, :before => @now - 149).should == { 'unspecified' => 150, @message => 50 }
1597
+ end
1598
+
1599
+ it 'should format the time spent if passed a format option' do
1600
+ Punch.summary(@project, :format => true).should == { 'unspecified' => '03:20', @message => '01:40', @other_message => '05:00' }
1601
+ end
1602
+ end
1603
+
1604
+ describe 'when the project is currently punched in' do
1605
+ before do
1606
+ @data = { @project => [ {'in' => @now - 500} ] }
1607
+ @time_data = @data[@project].last
1608
+ @time_data['log'] = ["punch in @ #{@time_data['in'].strftime(@time_format)}", "#{@message} @ #{(@time_data['in'] + 200).strftime(@time_format)}"]
1609
+ Punch.data = @data
1610
+ end
1611
+
1612
+ it 'should summarize time up to now' do
1613
+ Punch.summary(@project).should == { 'unspecified' => 200, @message => 300 }
1614
+ end
1615
+
1616
+ it 'should handle time data with no specific log messages' do
1617
+ @time_data['log'].pop
1618
+ Punch.summary(@project).should == { 'unspecified' => 500 }
1619
+ end
1620
+
1621
+ it 'should handle time data with no log messages' do
1622
+ @time_data['log'] = []
1623
+ Punch.summary(@project).should == { 'unspecified' => 500 }
1624
+ end
1625
+
1626
+ it 'should handle time data with no log' do
1627
+ @time_data.delete('log')
1628
+ Punch.summary(@project).should == { 'unspecified' => 500 }
1629
+ end
1630
+ end
1631
+ end
1632
+
1633
+ describe 'when the project does not exist' do
1634
+ before do
1635
+ @project = 'non-existent project'
1636
+ end
1637
+
1638
+ it 'should return nil' do
1639
+ Punch.summary(@project).should.be.nil
1640
+ end
1641
+ end
1642
+ end
1407
1643
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: one_inch_punch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yossef Mendelssohn
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-25 00:00:00 -05:00
12
+ date: 2009-08-17 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -40,7 +40,7 @@ dependencies:
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: 1.8.0
43
+ version: 2.3.3
44
44
  version:
45
45
  description: a simple time-tracking tool
46
46
  email:
@@ -105,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
105
  requirements: []
106
106
 
107
107
  rubyforge_project: yomendel
108
- rubygems_version: 1.3.4
108
+ rubygems_version: 1.3.5
109
109
  signing_key:
110
110
  specification_version: 3
111
111
  summary: a simple time-tracking tool