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.
- data/README +6 -6
- data/bin/punch +363 -262
- data/gemspec.rb +1 -1
- 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
|
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
|
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
|
27
|
-
. punch in projectname
|
28
|
-
. punch in projectname
|
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, :
|
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
|
43
|
-
. punch out projectname
|
44
|
-
. punch out projectname
|
45
|
-
. punch out projectname
|
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 :
|
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 '
|
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
|
-
|
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
|
-
|
277
|
+
#
|
278
|
+
# bootstrap libs and litter some crap into ruby built-ins ;-)
|
279
|
+
#
|
262
280
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
281
|
+
BEGIN {
|
282
|
+
require 'yaml'
|
283
|
+
require 'yaml/store'
|
284
|
+
require 'time'
|
285
|
+
require 'pathname'
|
286
|
+
require 'tempfile'
|
287
|
+
require 'fileutils'
|
270
288
|
|
271
|
-
|
272
|
-
|
289
|
+
begin
|
290
|
+
require 'rubygems'
|
291
|
+
rescue
|
292
|
+
42
|
293
|
+
end
|
273
294
|
|
274
|
-
|
295
|
+
begin
|
296
|
+
require 'main'
|
297
|
+
rescue
|
298
|
+
abort "gem install main"
|
299
|
+
end
|
275
300
|
|
276
|
-
|
301
|
+
begin
|
302
|
+
require 'orderedhash'
|
303
|
+
rescue
|
304
|
+
abort "gem install orderedhash"
|
305
|
+
end
|
277
306
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
307
|
+
begin
|
308
|
+
require 'chronic'
|
309
|
+
rescue
|
310
|
+
abort "gem install chronic"
|
282
311
|
end
|
283
312
|
|
284
|
-
|
285
|
-
|
313
|
+
Home = File.expand_path(ENV['HOME'] || '~')
|
314
|
+
|
315
|
+
### hackity hack, don't talk back
|
286
316
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
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
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
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
|
-
|
315
|
-
|
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
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
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
|
-
|
387
|
+
end
|
326
388
|
end
|
327
389
|
end
|
328
390
|
end
|
329
|
-
end #}}}
|
330
391
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
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
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
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
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
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
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
if
|
394
|
-
|
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
|
-
|
404
|
-
|
405
|
-
|
406
|
-
"punch
|
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
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
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
|
-
|
430
|
-
|
431
|
-
|
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
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
entry =
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
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
|
-
|
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
|
-
|
531
|
+
@db.roots
|
454
532
|
end
|
455
533
|
end
|
456
|
-
h
|
457
534
|
end
|
458
|
-
end #}}}
|
459
535
|
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
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
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
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
|
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
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
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
|
593
|
+
total
|
491
594
|
end
|
492
|
-
total
|
493
595
|
end
|
494
|
-
end #}}}
|
495
596
|
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
597
|
+
def delete project
|
598
|
+
@db.transaction do
|
599
|
+
@db.delete project
|
600
|
+
project
|
601
|
+
end
|
500
602
|
end
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
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
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
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
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
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
|
-
|
534
|
-
|
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.
|
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
|
-
|
32
|
-
|
33
|
-
|
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::
|
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::
|
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::
|
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::
|
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
|
+
|