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 +11 -0
- data/bin/punch +7 -0
- data/lib/punch.rb +39 -2
- data/lib/punch/instance.rb +4 -0
- data/lib/punch/version.rb +2 -2
- data/spec/punch_command_spec.rb +98 -0
- data/spec/punch_instance_spec.rb +50 -0
- data/spec/punch_spec.rb +288 -52
- metadata +4 -4
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
|
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
|
-
|
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
|
|
data/lib/punch/instance.rb
CHANGED
data/lib/punch/version.rb
CHANGED
data/spec/punch_command_spec.rb
CHANGED
@@ -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
|
data/spec/punch_instance_spec.rb
CHANGED
@@ -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
|
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
|
-
|
246
|
-
|
247
|
-
|
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
|
251
|
-
Punch.status(
|
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
|
255
|
-
Punch.status(
|
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
|
-
|
259
|
-
|
260
|
-
|
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
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
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
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
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
|
-
|
282
|
-
@
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
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
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
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
|
-
|
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.
|
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-
|
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:
|
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.
|
108
|
+
rubygems_version: 1.3.5
|
109
109
|
signing_key:
|
110
110
|
specification_version: 3
|
111
111
|
summary: a simple time-tracking tool
|