punch 0.0.1 → 0.0.2

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