one_inch_punch 0.3.3 → 0.4.0

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