punch 0.0.1 → 0.0.2

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.
Files changed (4) hide show
  1. data/README +6 -6
  2. data/bin/punch +363 -262
  3. data/gemspec.rb +1 -1
  4. metadata +53 -46
data/README CHANGED
@@ -2,13 +2,11 @@ NAME
2
2
  punch
3
3
 
4
4
  SYNOPSIS
5
- punch (in|out|log|clock|status|list|total|delete|dump) [options]+
6
-
7
- URIS
8
- http://codeforpeople.com/lib/ruby/punch
9
- http://rubyforge.org/projects/codeforpeople/
5
+ punch (in|out|log|clock|status|list|total|delete|dump|parse) [options]+
10
6
 
11
7
  DESCRIPTION
8
+ i can has time tracking!
9
+
12
10
  punch is a k.i.s.s. tool for tracking the hours spent on various projects.
13
11
  it supports logging hours under a project name, adding notes about work
14
12
  done during that period, and several very simple reporting tools that
@@ -21,7 +19,9 @@ PARAMETERS
21
19
 
22
20
  EXAMPLES
23
21
  . punch in projectname
24
- . punch log projectname "can has time tracking"
22
+ . punch in projectname 'an hour ago'
23
+ . punch log projectname 'rewriting your java app in ruby...'
25
24
  . punch out projectname
26
25
  . punch total projectname --after 2007-12-03 --before 2007-12-07
26
+ . punch total projectname --after 'yesterday morning' --before 'today at noon'
27
27
 
data/bin/punch CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Main {
4
4
  description <<-txt
5
+ i can has time tracking!
6
+
5
7
  punch is a k.i.s.s. tool for tracking the hours spent on various projects.
6
8
  it supports logging hours under a project name, adding notes about work
7
9
  done during that period, and several very simple reporting tools that
@@ -12,9 +14,11 @@ Main {
12
14
 
13
15
  examples <<-txt
14
16
  . punch in projectname
15
- . punch log projectname "can has time tracking"
17
+ . punch in projectname 'an hour ago'
18
+ . punch log projectname 'rewriting your java app in ruby...'
16
19
  . punch out projectname
17
20
  . punch total projectname --after 2007-12-03 --before 2007-12-07
21
+ . punch total projectname --after 'yesterday morning' --before 'today at noon'
18
22
  txt
19
23
 
20
24
 
@@ -23,29 +27,32 @@ Main {
23
27
 
24
28
  examples <<-txt
25
29
  . punch in projectname
26
- . punch in projectname --now 2007-01-01T09
27
- . punch in projectname -n now
28
- . punch in projectname -n 2007-01-01T09 --message 'new years day'
30
+ . punch in projectname 2007-01-01T09
31
+ . punch in projectname now
32
+ . punch in projectname 'today at around noon'
33
+ . punch in projectname 2007-01-01T09 --message 'new years day'
29
34
  txt
30
35
 
31
- mixin :argument_project, :option_db, :option_now, :option_message
36
+ mixin :argument_project, :optional_now, :option_db, :option_message, :option_noop
32
37
 
33
38
  run{ y db.punch(:in, project, 'now' => now, 'message' => message) }
34
39
  }
35
40
 
36
41
 
37
42
  mode(:out){
38
- description 'punch out of a project'
43
+ description 'punch out of a project or, iff no project given, all open projects'
39
44
 
40
45
  examples <<-txt
41
46
  . punch out projectname
42
- . punch out projectname --now 2007-01-01T09
43
- . punch out projectname -n now
44
- . punch out projectname -n 2007-01-01T17 --message 'new years day'
45
- . punch out projectname -n 2007-01-01T05pm -m 'new years day'
47
+ . punch out projectname 2007-01-01T09
48
+ . punch out projectname now
49
+ . punch out projectname 2007-01-01T17 --message 'new years day'
50
+ . punch out projectname 2007-01-01T05pm -m 'new years day'
51
+ . punch out projectname an hour ago
52
+ . punch out
46
53
  txt
47
54
 
48
- mixin :argument_project, :option_db, :option_now, :option_message
55
+ mixin :optional_project, :optional_now, :option_db, :option_message, :option_noop
49
56
 
50
57
  run{ y db.punch(:out, project, 'now' => now, 'message' => message) }
51
58
  }
@@ -55,11 +62,11 @@ Main {
55
62
  description 'log a message for a project you are currently logged into'
56
63
 
57
64
  examples <<-txt
58
- . punch log projectname 'bloody xml!'
65
+ . punch log projectname 'xml - welcome to my pain cave!'
59
66
  . punch log projectname should have used yaml
60
67
  txt
61
68
 
62
- mixin :argument_project, :option_db, :argument_message
69
+ mixin :argument_project, :option_db, :argument_message, :option_noop
63
70
 
64
71
  run{ y db.log(project, message) }
65
72
  }
@@ -72,7 +79,7 @@ Main {
72
79
  . punch clock projectname 2007-01-01T09am 2007-01-01T05pm -m "working on new year's day"
73
80
  txt
74
81
 
75
- mixin :argument_project, :argument_punch_in, :argument_punch_out, :option_db, :option_message
82
+ mixin :argument_project, :argument_punch_in, :argument_punch_out, :option_db, :option_message, :option_noop
76
83
 
77
84
  run{ y db.clock(project, punch_in, punch_out, 'message' => message) }
78
85
  }
@@ -127,11 +134,9 @@ Main {
127
134
  . punch delete projectname
128
135
  txt
129
136
 
130
- mixin :argument_project, :option_db
137
+ mixin :argument_project, :option_db, :option_noop
131
138
 
132
- run{
133
- y db.delete(project)
134
- }
139
+ run{ y db.delete(project) }
135
140
  }
136
141
 
137
142
  mode(:dump){
@@ -147,6 +152,23 @@ Main {
147
152
  run{ y db.dump(project) }
148
153
  }
149
154
 
155
+ mode(:parse){
156
+ description 'show how a time string will be parsed'
157
+
158
+ examples <<-txt
159
+ . punch parse 'yesterday morning'
160
+ . punch parse today at noon
161
+ . punch parse yesterday at 9pm
162
+ txt
163
+
164
+ argument(:spec){
165
+ arity -2
166
+ attr
167
+ }
168
+
169
+ run{ y Time.parse(spec.join(' ')) }
170
+ }
171
+
150
172
 
151
173
  mixin :argument_project do
152
174
  argument(:project){
@@ -190,8 +212,8 @@ Main {
190
212
  end
191
213
 
192
214
  mixin :option_now do
193
- option(:now){
194
- optional
215
+ option(:now, :n){
216
+ argument :required
195
217
  desc 'consider this time as the current time'
196
218
  cast{|value| Time.parse value}
197
219
  default Time::Now
@@ -199,6 +221,16 @@ Main {
199
221
  }
200
222
  end
201
223
 
224
+ mixin :optional_now do
225
+ argument(:now){
226
+ optional
227
+ desc 'consider this time as the current time'
228
+ arity -2
229
+ default Time::Now
230
+ }
231
+ attribute(:now){ Time.parse param['now'].values.join(' ') }
232
+ end
233
+
202
234
  mixin :option_db do
203
235
  option(:db){
204
236
  argument :required
@@ -207,6 +239,12 @@ Main {
207
239
  }
208
240
  end
209
241
 
242
+ mixin :option_noop do
243
+ option(:noop, :N){
244
+ cast{|value| $NOOP = value}
245
+ }
246
+ end
247
+
210
248
  mixin :option_after do
211
249
  option(:after, :A){
212
250
  argument :required
@@ -231,306 +269,369 @@ Main {
231
269
  }
232
270
 
233
271
 
234
- BEGIN {
235
- require 'yaml'
236
- require 'yaml/store'
237
- require 'time'
238
- require 'pathname'
239
272
 
240
- begin
241
- require 'rubygems'
242
- gem 'main', '~> 2.6.0'
243
- rescue
244
- 42
245
- end
246
273
 
247
- begin
248
- require 'main'
249
- rescue
250
- abort "gem install main"
251
- end
252
274
 
253
- begin
254
- require 'orderedhash'
255
- rescue
256
- abort "gem install orderedhash"
257
- end
258
275
 
259
- Home = File.expand_path(ENV['HOME'] || '~')
260
276
 
261
- ### hackity hack, don't talk back
277
+ #
278
+ # bootstrap libs and litter some crap into ruby built-ins ;-)
279
+ #
262
280
 
263
- class Object #{{{
264
- def yaml_inline!
265
- class << self
266
- def to_yaml_style() :inline end
267
- end
268
- end
269
- end #}}}
281
+ BEGIN {
282
+ require 'yaml'
283
+ require 'yaml/store'
284
+ require 'time'
285
+ require 'pathname'
286
+ require 'tempfile'
287
+ require 'fileutils'
270
288
 
271
- class Time #{{{
272
- Beginning = Time.at(0).iso8601
289
+ begin
290
+ require 'rubygems'
291
+ rescue
292
+ 42
293
+ end
273
294
 
274
- End = Time.at((2**31)-1).iso8601
295
+ begin
296
+ require 'main'
297
+ rescue
298
+ abort "gem install main"
299
+ end
275
300
 
276
- Now = Time.now.iso8601
301
+ begin
302
+ require 'orderedhash'
303
+ rescue
304
+ abort "gem install orderedhash"
305
+ end
277
306
 
278
- Null = Time.at(0).instance_eval do
279
- def to_s() "" end
280
- def inspect() "" end
281
- self
307
+ begin
308
+ require 'chronic'
309
+ rescue
310
+ abort "gem install chronic"
282
311
  end
283
312
 
284
- def to_s(n=0) iso8601(n) end
285
- alias_method 'inspect', 'to_s'
313
+ Home = File.expand_path(ENV['HOME'] || '~')
314
+
315
+ ### hackity hack, don't talk back
286
316
 
287
- ### hack to fix Time.parse bug
288
- Parse = Time.method 'parse'
289
- def self.parse string
290
- if string =~ %r'^\d\d\d\d-\d\d-\d\dT\d\d:?$'
291
- string = string.sub(%r/:$/,'') + ':00'
317
+ class Object
318
+ def yaml_inline!
319
+ class << self
320
+ def to_yaml_style() :inline end
321
+ end
292
322
  end
293
- Parse.call string
294
323
  end
295
324
 
296
- def to_yaml( opts = {} )
297
- YAML::quick_emit( object_id, opts ) do |out|
298
- tz = "Z"
299
- # from the tidy Tobias Peters <t-peters@gmx.de> Thanks!
300
- unless self.utc?
301
- utc_same_instant = self.dup.utc
302
- utc_same_writing = Time.utc(year,month,day,hour,min,sec,usec)
303
- difference_to_utc = utc_same_writing - utc_same_instant
304
- if (difference_to_utc < 0)
305
- difference_sign = '-'
306
- absolute_difference = -difference_to_utc
307
- else
308
- difference_sign = '+'
309
- absolute_difference = difference_to_utc
310
- end
311
- difference_minutes = (absolute_difference/60).round
312
- tz = "%s%02d:%02d" % [ difference_sign, difference_minutes / 60, difference_minutes % 60]
325
+ class Time
326
+ Beginning = Time.at(0).iso8601
327
+
328
+ End = Time.at((2**31)-1).iso8601
329
+
330
+ Now = Time.now.iso8601
331
+
332
+ Null = Time.at(0).instance_eval do
333
+ def to_s() "" end
334
+ def inspect() "" end
335
+ self
336
+ end
337
+
338
+ def to_s(n=0) iso8601(n) end
339
+ alias_method 'inspect', 'to_s'
340
+
341
+ ### hack to fix Time.parse bug
342
+ Parse = Time.method 'parse'
343
+ def self.parse string
344
+ if string =~ %r'^\d\d\d\d-\d\d-\d\dT\d\d:?$'
345
+ string = string.sub(%r/:$/,'') + ':00'
346
+ end
347
+ if string =~ %r/\ban\b/
348
+ string = string.sub(%r/\ban\b/, '1')
313
349
  end
314
- standard = self.strftime( "%Y-%m-%dT%H:%M:%S" )
315
- standard += ".%02d" % [usec] #if usec.nonzero?
316
- standard += "%s" % [tz]
317
- if to_yaml_properties.empty?
318
- out.scalar( taguri, standard, :plain )
350
+ if string =~ %r'^\d\d\d\d-\d\d-\d\d'
351
+ Parse.call string
319
352
  else
320
- out.map( taguri, to_yaml_style ) do |map|
321
- map.add( 'at', standard )
322
- to_yaml_properties.each do |m|
323
- map.add( m, instance_variable_get( m ) )
353
+ Chronic.parse string, :context => :past
354
+ end
355
+ end
356
+
357
+ def to_yaml( opts = {} )
358
+ YAML::quick_emit( object_id, opts ) do |out|
359
+ tz = "Z"
360
+ # from the tidy Tobias Peters <t-peters@gmx.de> Thanks!
361
+ unless self.utc?
362
+ utc_same_instant = self.dup.utc
363
+ utc_same_writing = Time.utc(year,month,day,hour,min,sec,usec)
364
+ difference_to_utc = utc_same_writing - utc_same_instant
365
+ if (difference_to_utc < 0)
366
+ difference_sign = '-'
367
+ absolute_difference = -difference_to_utc
368
+ else
369
+ difference_sign = '+'
370
+ absolute_difference = difference_to_utc
371
+ end
372
+ difference_minutes = (absolute_difference/60).round
373
+ tz = "%s%02d:%02d" % [ difference_sign, difference_minutes / 60, difference_minutes % 60]
374
+ end
375
+ standard = self.strftime( "%Y-%m-%dT%H:%M:%S" )
376
+ standard += ".%02d" % [usec] #if usec.nonzero?
377
+ standard += "%s" % [tz]
378
+ if to_yaml_properties.empty?
379
+ out.scalar( taguri, standard, :plain )
380
+ else
381
+ out.map( taguri, to_yaml_style ) do |map|
382
+ map.add( 'at', standard )
383
+ to_yaml_properties.each do |m|
384
+ map.add( m, instance_variable_get( m ) )
385
+ end
324
386
  end
325
- end
387
+ end
326
388
  end
327
389
  end
328
390
  end
329
- end #}}}
330
391
 
331
- class Numeric #{{{
332
- def hms s = self.to_i
333
- h, s = s.divmod 3600
334
- m, s = s.divmod 60
335
- [h.to_i, m.to_i, s]
392
+ class Numeric
393
+ def hms s = self.to_i
394
+ h, s = s.divmod 3600
395
+ m, s = s.divmod 60
396
+ [h.to_i, m.to_i, s]
397
+ end
336
398
  end
337
- end #}}}
338
-
339
- class Hash #{{{
340
- def getopt key, default = nil
341
- return self[key.to_s] if has_key?(key.to_s)
342
- return self[key.to_s.to_sym] if has_key?(key.to_s.to_sym)
343
- return self[key] if has_key?(key)
344
- default
399
+
400
+ class String
401
+ def hms
402
+ h, m, s = split %r/:/, 3
403
+ [h.to_i, m.to_i, s.to_i]
404
+ end
345
405
  end
346
- end #}}}
347
-
348
- class DB #{{{
349
- attr 'pathname'
350
- attr 'db'
351
-
352
- def initialize pathname #{{{
353
- @pathname = Pathname.new pathname.to_s
354
- @db = YAML::Store.new @pathname.to_s
355
- end #}}}
356
-
357
- def punch which, project, options = {} #{{{
358
- now = options.getopt 'now'
359
- message = options.getopt 'message'
360
- message ||= "punch #{ which } @ #{ now }"
361
- @db.transaction do
362
- create project
363
- list = @db[project.to_s]
364
- entry = list.last
365
- case which.to_s
366
- when /in/
367
- if(entry and entry['out'].nil?)
368
- abort 'you first need to punch out'
369
- else
370
- entry = create_entry('in' => now, 'out' => nil, 'log' => [message].flatten.compact)
371
- list << entry
372
- end
373
- when /out/
374
- if(entry.nil? or (entry and (entry['in'].nil? or entry['out'])))
375
- abort 'you first need to punch in'
376
- else
377
- entry = create_entry entry
378
- entry['out'] = now
379
- entry['log'] = [entry['log'], message].flatten.compact
380
- list[-1] = entry
381
- end
382
- end
383
- entry
406
+
407
+ class Hash
408
+ def getopt key, default = nil
409
+ return self[key.to_s] if has_key?(key.to_s)
410
+ return self[key.to_s.to_sym] if has_key?(key.to_s.to_sym)
411
+ return self[key] if has_key?(key)
412
+ default
384
413
  end
385
- end #}}}
386
-
387
- def log project, message, options = {} #{{{
388
- @db.transaction do
389
- create project
390
- message = [message].flatten.compact.join(' ')
391
- list = @db[project.to_s]
392
- entry = list.last
393
- if(entry.nil? or (entry and entry['out']))
394
- abort 'you first need to punch in'
414
+ end
415
+
416
+ class DB
417
+ attr 'pathname'
418
+ attr 'db'
419
+
420
+ def initialize pathname
421
+ @pathname = Pathname.new pathname.to_s
422
+ if $NOOP
423
+ t = Tempfile.new Process.pid.to_s
424
+ t.write IO.read(pathname)
425
+ t.close
426
+ @db = YAML::Store.new t.path
427
+ else
428
+ @db = YAML::Store.new @pathname.to_s
395
429
  end
396
- entry = create_entry entry
397
- entry['log'] = [entry['log'], message].flatten.compact
398
- list[-1] = entry
399
- entry
400
430
  end
401
- end #}}}
402
431
 
403
- def clock project, punch_in, punch_out, options = {} #{{{
404
- @db.transaction do
405
- log = [
406
- "punch in @ #{ punch_in }",
432
+ def punch which, project, options = {}
433
+ now = options.getopt 'now'
434
+ messages = [
435
+ "punch #{ which } @ #{ now }",
407
436
  options.getopt('message'),
408
- "punch out @ #{ punch_out }",
409
437
  ].flatten.compact
410
- create project
411
- list = @db[project.to_s]
412
- entry = create_entry('in' => punch_in, 'out' => punch_out, 'log' => log)
413
- list << entry
414
- @db[project.to_s] = list.sort_by{|e| e.values_at('in', 'out')}
415
- entry
416
- end
417
- end #}}}
418
-
419
- def list project, options = {} #{{{
420
- window = options['window'] || options[:window]
421
- @db.transaction do
422
- create project
423
- if project
424
- @db[project.to_s].select do |entry|
425
- a, b = entry.values_at 'in', 'out'
426
- if a and b
427
- window === Time.parse(a.to_s) and window === Time.parse(b.to_s)
438
+ @db.transaction do
439
+ if project
440
+ create project
441
+ list = @db[project.to_s]
442
+ entry = list.last
443
+ case which.to_s
444
+ when /in/
445
+ if(entry and entry['out'].nil?)
446
+ abort 'you first need to punch out'
447
+ else
448
+ entry = create_entry('in' => now, 'out' => nil, 'log' => [messages].flatten.compact)
449
+ list << entry
450
+ end
451
+ when /out/
452
+ if(entry.nil? or (entry and (entry['in'].nil? or entry['out'])))
453
+ abort 'you first need to punch in'
454
+ else
455
+ entry = create_entry entry
456
+ sum = now - Time.parse(entry['in'].to_s)
457
+ entry['out'] = now
458
+ entry['log'] = [entry['log'], messages].flatten.compact
459
+ entry['total'] = ('%0.2d:%0.2d:%0.2d' % sum.hms)
460
+ list[-1] = entry
461
+ end
428
462
  end
429
- end.map{|entry| create_entry entry}
430
- else
431
- @db.roots
463
+ entry
464
+ else
465
+ entries = OrderedHash.new
466
+ @db.roots.each do |project|
467
+ list = @db[project]
468
+ next unless Array === list
469
+ list.each do |entry|
470
+ next unless entry
471
+ if(entry['in'] and entry['out'].nil?)
472
+ entry = create_entry entry
473
+ sum = now - Time.parse(entry['in'].to_s)
474
+ entry['out'] = now
475
+ entry['log'] = [entry['log'], messages].flatten.compact
476
+ entry['total'] = ('%0.2d:%0.2d:%0.2d' % sum.hms)
477
+ list[-1] = entry
478
+ (entries[project] ||= []) << entry
479
+ end
480
+ end
481
+ end
482
+ entries
483
+ end
432
484
  end
433
485
  end
434
- end #}}}
435
-
436
- def status project, options = {} #{{{
437
- @db.transaction do
438
- h = Hash.new
439
- @db.roots.each do |root|
440
- entry = @db[root].last
441
- a, b =
442
- if entry
443
- entry.values_at 'in', 'out'
444
- else
445
- [false, 'out']
446
- end
447
- #status = b ? "out @ #{ b }" : "in @ #{ a }"
448
- status = b ? ['out', b] : ['in', a]
449
- status.yaml_inline!
486
+
487
+ def log project, message, options = {}
488
+ @db.transaction do
489
+ create project
490
+ message = [message].flatten.compact.join(' ')
491
+ list = @db[project.to_s]
492
+ entry = list.last
493
+ if(entry.nil? or (entry and entry['out']))
494
+ abort 'you first need to punch in'
495
+ end
496
+ entry = create_entry entry
497
+ entry['log'] = [entry['log'], message].flatten.compact
498
+ list[-1] = entry
499
+ entry
500
+ end
501
+ end
502
+
503
+ def clock project, punch_in, punch_out, options = {}
504
+ @db.transaction do
505
+ log = [
506
+ "punch in @ #{ punch_in }",
507
+ options.getopt('message'),
508
+ "punch out @ #{ punch_out }",
509
+ ].flatten.compact
510
+ create project
511
+ list = @db[project.to_s]
512
+ entry = create_entry('in' => punch_in, 'out' => punch_out, 'log' => log)
513
+ list << entry
514
+ @db[project.to_s] = list.sort_by{|e| e.values_at('in', 'out')}
515
+ entry
516
+ end
517
+ end
518
+
519
+ def list project, options = {}
520
+ window = options['window'] || options[:window]
521
+ @db.transaction do
522
+ create project
450
523
  if project
451
- h[project] = status if project == root
524
+ @db[project.to_s].select do |entry|
525
+ a, b = entry.values_at 'in', 'out'
526
+ if a and b
527
+ window === Time.parse(a.to_s) and window === Time.parse(b.to_s)
528
+ end
529
+ end.map{|entry| create_entry entry}
452
530
  else
453
- h[root] = status
531
+ @db.roots
454
532
  end
455
533
  end
456
- h
457
534
  end
458
- end #}}}
459
535
 
460
- def total project, options = {} #{{{
461
- window = options['window'] || options[:window]
462
- @db.transaction do
463
- if project
464
- create project
465
- selected = @db[project.to_s].select do |entry|
466
- a, b = entry.values_at 'in', 'out'
467
- window === Time.parse(a.to_s) and window === Time.parse(b.to_s)
536
+ def status project, options = {}
537
+ @db.transaction do
538
+ h = Hash.new
539
+ @db.roots.each do |root|
540
+ entry = @db[root].last
541
+ a, b =
542
+ if entry
543
+ entry.values_at 'in', 'out'
544
+ else
545
+ [false, 'out']
546
+ end
547
+ #status = b ? "out @ #{ b }" : "in @ #{ a }"
548
+ #status = b ? ['out', b] : ['in', a]
549
+ #status.yaml_inline!
550
+ status = b ? Hash['out', b] : Hash['in', a]
551
+ if project
552
+ h[project] = status if project == root
553
+ else
554
+ h[root] = status
555
+ end
468
556
  end
469
- filtered = { project => selected }
470
- else
471
- filtered = Hash.new
472
- @db.roots.each do |project|
557
+ h
558
+ end
559
+ end
560
+
561
+ def total project, options = {}
562
+ window = options['window'] || options[:window]
563
+ @db.transaction do
564
+ if project
565
+ create project
473
566
  selected = @db[project.to_s].select do |entry|
474
567
  a, b = entry.values_at 'in', 'out'
475
568
  window === Time.parse(a.to_s) and window === Time.parse(b.to_s)
476
569
  end
477
- filtered.update project => selected
570
+ filtered = { project => selected }
571
+ else
572
+ filtered = Hash.new
573
+ @db.roots.each do |project|
574
+ selected = @db[project.to_s].select do |entry|
575
+ a, b = entry.values_at 'in', 'out'
576
+ window === Time.parse(a.to_s) and window === Time.parse(b.to_s)
577
+ end
578
+ filtered.update project => selected
579
+ end
478
580
  end
479
- end
480
- total = Hash.new
481
- filtered.each do |project, selected|
482
- sum = 0
483
- selected.each do |entry|
484
- a, b = entry.values_at 'in', 'out'
485
- next unless a and b
486
- a = Time.parse(a.to_s)
487
- b = Time.parse(b.to_s)
488
- sum += (b - a)
581
+ total = Hash.new
582
+ filtered.each do |project, selected|
583
+ sum = 0
584
+ selected.each do |entry|
585
+ a, b = entry.values_at 'in', 'out'
586
+ next unless a and b
587
+ a = Time.parse(a.to_s)
588
+ b = Time.parse(b.to_s)
589
+ sum += (b - a)
590
+ end
591
+ total.update project => ('%0.2d:%0.2d:%0.2d' % sum.hms)
489
592
  end
490
- total.update project => ('%0.2d:%0.2d:%0.2d' % sum.hms)
593
+ total
491
594
  end
492
- total
493
595
  end
494
- end #}}}
495
596
 
496
- def delete project #{{{
497
- @db.transaction do
498
- @db.delete project
499
- project
597
+ def delete project
598
+ @db.transaction do
599
+ @db.delete project
600
+ project
601
+ end
500
602
  end
501
- end #}}}
502
-
503
- def dump project #{{{
504
- @db.transaction do
505
- dumped = OrderedHash.new
506
- if project
507
- dumped[project] = Array.new
508
- @db[project].each do |entry|
509
- dumped[project] << create_entry(entry)
510
- end
511
- else
512
- @db.roots.each do |project|
603
+
604
+ def dump project
605
+ @db.transaction do
606
+ dumped = OrderedHash.new
607
+ if project
513
608
  dumped[project] = Array.new
514
609
  @db[project].each do |entry|
515
610
  dumped[project] << create_entry(entry)
516
611
  end
612
+ else
613
+ @db.roots.each do |project|
614
+ dumped[project] = Array.new
615
+ @db[project].each do |entry|
616
+ dumped[project] << create_entry(entry)
617
+ end
618
+ end
517
619
  end
620
+ dumped
518
621
  end
519
- dumped
520
622
  end
521
- end #}}}
522
623
 
523
- def create project #{{{
524
- project = project.to_s.strip
525
- @db[project.to_s] ||= Array.new unless project.empty?
526
- end #}}}
624
+ def create project
625
+ project = project.to_s.strip
626
+ @db[project.to_s] ||= Array.new unless project.empty?
627
+ end
527
628
 
528
- def create_entry hash = {} #{{{
529
- entry = OrderedHash.new
530
- %w[ in out log ].each do |key|
531
- entry[key] = hash.getopt key
629
+ def create_entry hash = {}
630
+ entry = OrderedHash.new
631
+ %w[ in out log total ].each do |key|
632
+ entry[key] = hash.getopt key
633
+ end
634
+ entry
532
635
  end
533
- entry
534
- end #}}}
535
- end #}}}
536
- }
636
+ end
637
+ }
data/gemspec.rb CHANGED
@@ -28,7 +28,7 @@ Gem::Specification::new do |spec|
28
28
  spec.add_dependency 'main', '>= 2.6.0'
29
29
  spec.add_dependency 'systemu', '>= 1.2.0'
30
30
  spec.add_dependency 'orderedhash', '>= 0.0.3'
31
- spec.add_dependency 'attributes', '5.0.0'
31
+ spec.add_dependency 'attributes', '>= 5.0.0'
32
32
 
33
33
  spec.extensions << "extconf.rb" if File::exists? "extconf.rb"
34
34
 
metadata CHANGED
@@ -1,56 +1,21 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.4
3
- specification_version: 1
4
2
  name: punch
5
3
  version: !ruby/object:Gem::Version
6
- version: 0.0.1
7
- date: 2007-12-06 00:00:00 -07:00
8
- summary: punch
9
- require_paths:
10
- - lib
11
- email: ara.t.howard@gmail.com
12
- homepage: http://codeforpeople.com/lib/ruby/punch/
13
- rubyforge_project:
14
- description:
15
- autorequire: punch
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: false
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
24
- version:
4
+ version: 0.0.2
25
5
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- post_install_message:
29
6
  authors:
30
7
  - Ara T. Howard
31
- files:
32
- - bin
33
- - bin/punch
34
- - gemspec.rb
35
- - install.rb
36
- - README
37
- test_files: []
38
-
39
- rdoc_options: []
40
-
41
- extra_rdoc_files: []
42
-
43
- executables:
44
- - punch
45
- extensions: []
46
-
47
- requirements: []
8
+ autorequire: punch
9
+ bindir: bin
10
+ cert_chain: []
48
11
 
12
+ date: 2008-03-29 00:00:00 -06:00
13
+ default_executable:
49
14
  dependencies:
50
15
  - !ruby/object:Gem::Dependency
51
16
  name: main
52
17
  version_requirement:
53
- version_requirements: !ruby/object:Gem::Version::Requirement
18
+ version_requirements: !ruby/object:Gem::Requirement
54
19
  requirements:
55
20
  - - ">="
56
21
  - !ruby/object:Gem::Version
@@ -59,7 +24,7 @@ dependencies:
59
24
  - !ruby/object:Gem::Dependency
60
25
  name: systemu
61
26
  version_requirement:
62
- version_requirements: !ruby/object:Gem::Version::Requirement
27
+ version_requirements: !ruby/object:Gem::Requirement
63
28
  requirements:
64
29
  - - ">="
65
30
  - !ruby/object:Gem::Version
@@ -68,7 +33,7 @@ dependencies:
68
33
  - !ruby/object:Gem::Dependency
69
34
  name: orderedhash
70
35
  version_requirement:
71
- version_requirements: !ruby/object:Gem::Version::Requirement
36
+ version_requirements: !ruby/object:Gem::Requirement
72
37
  requirements:
73
38
  - - ">="
74
39
  - !ruby/object:Gem::Version
@@ -77,9 +42,51 @@ dependencies:
77
42
  - !ruby/object:Gem::Dependency
78
43
  name: attributes
79
44
  version_requirement:
80
- version_requirements: !ruby/object:Gem::Version::Requirement
45
+ version_requirements: !ruby/object:Gem::Requirement
81
46
  requirements:
82
- - - "="
47
+ - - ">="
83
48
  - !ruby/object:Gem::Version
84
49
  version: 5.0.0
85
50
  version:
51
+ description:
52
+ email: ara.t.howard@gmail.com
53
+ executables:
54
+ - punch
55
+ extensions: []
56
+
57
+ extra_rdoc_files: []
58
+
59
+ files:
60
+ - bin
61
+ - bin/punch
62
+ - gemspec.rb
63
+ - install.rb
64
+ - README
65
+ has_rdoc: false
66
+ homepage: http://codeforpeople.com/lib/ruby/punch/
67
+ post_install_message:
68
+ rdoc_options: []
69
+
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: "0"
83
+ version:
84
+ requirements: []
85
+
86
+ rubyforge_project:
87
+ rubygems_version: 1.0.1
88
+ signing_key:
89
+ specification_version: 2
90
+ summary: punch
91
+ test_files: []
92
+