punch 0.0.1
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 +27 -0
- data/bin/punch +536 -0
- data/gemspec.rb +38 -0
- data/install.rb +214 -0
- metadata +85 -0
data/README
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
NAME
|
2
|
+
punch
|
3
|
+
|
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/
|
10
|
+
|
11
|
+
DESCRIPTION
|
12
|
+
punch is a k.i.s.s. tool for tracking the hours spent on various projects.
|
13
|
+
it supports logging hours under a project name, adding notes about work
|
14
|
+
done during that period, and several very simple reporting tools that
|
15
|
+
operate over a window of time.
|
16
|
+
|
17
|
+
run 'punch help modename' for more info.
|
18
|
+
|
19
|
+
PARAMETERS
|
20
|
+
--help, -h
|
21
|
+
|
22
|
+
EXAMPLES
|
23
|
+
. punch in projectname
|
24
|
+
. punch log projectname "can has time tracking"
|
25
|
+
. punch out projectname
|
26
|
+
. punch total projectname --after 2007-12-03 --before 2007-12-07
|
27
|
+
|
data/bin/punch
ADDED
@@ -0,0 +1,536 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
Main {
|
4
|
+
description <<-txt
|
5
|
+
punch is a k.i.s.s. tool for tracking the hours spent on various projects.
|
6
|
+
it supports logging hours under a project name, adding notes about work
|
7
|
+
done during that period, and several very simple reporting tools that
|
8
|
+
operate over a window of time.
|
9
|
+
|
10
|
+
run 'punch help modename' for more info.
|
11
|
+
txt
|
12
|
+
|
13
|
+
examples <<-txt
|
14
|
+
. punch in projectname
|
15
|
+
. punch log projectname "can has time tracking"
|
16
|
+
. punch out projectname
|
17
|
+
. punch total projectname --after 2007-12-03 --before 2007-12-07
|
18
|
+
txt
|
19
|
+
|
20
|
+
|
21
|
+
mode(:in){
|
22
|
+
description 'punch in on a project'
|
23
|
+
|
24
|
+
examples <<-txt
|
25
|
+
. 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'
|
29
|
+
txt
|
30
|
+
|
31
|
+
mixin :argument_project, :option_db, :option_now, :option_message
|
32
|
+
|
33
|
+
run{ y db.punch(:in, project, 'now' => now, 'message' => message) }
|
34
|
+
}
|
35
|
+
|
36
|
+
|
37
|
+
mode(:out){
|
38
|
+
description 'punch out of a project'
|
39
|
+
|
40
|
+
examples <<-txt
|
41
|
+
. 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'
|
46
|
+
txt
|
47
|
+
|
48
|
+
mixin :argument_project, :option_db, :option_now, :option_message
|
49
|
+
|
50
|
+
run{ y db.punch(:out, project, 'now' => now, 'message' => message) }
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
mode(:log){
|
55
|
+
description 'log a message for a project you are currently logged into'
|
56
|
+
|
57
|
+
examples <<-txt
|
58
|
+
. punch log projectname 'bloody xml!'
|
59
|
+
. punch log projectname should have used yaml
|
60
|
+
txt
|
61
|
+
|
62
|
+
mixin :argument_project, :option_db, :argument_message
|
63
|
+
|
64
|
+
run{ y db.log(project, message) }
|
65
|
+
}
|
66
|
+
|
67
|
+
|
68
|
+
mode(:clock){
|
69
|
+
description 'punch in and out retroactively'
|
70
|
+
|
71
|
+
examples <<-txt
|
72
|
+
. punch clock projectname 2007-01-01T09am 2007-01-01T05pm -m "working on new year's day"
|
73
|
+
txt
|
74
|
+
|
75
|
+
mixin :argument_project, :argument_punch_in, :argument_punch_out, :option_db, :option_message
|
76
|
+
|
77
|
+
run{ y db.clock(project, punch_in, punch_out, 'message' => message) }
|
78
|
+
}
|
79
|
+
|
80
|
+
mode(:status){
|
81
|
+
description 'shows the status of named project, or all projects iff none given'
|
82
|
+
|
83
|
+
examples <<-txt
|
84
|
+
. punch status
|
85
|
+
. punch status projectname
|
86
|
+
txt
|
87
|
+
|
88
|
+
mixin :optional_project, :option_db
|
89
|
+
|
90
|
+
run{ y db.status(project) }
|
91
|
+
}
|
92
|
+
|
93
|
+
mode(:list){
|
94
|
+
description 'list a single/all projects, possibly filtering by time'
|
95
|
+
|
96
|
+
examples <<-txt
|
97
|
+
. punch list projectname
|
98
|
+
. punch list projectname --after 2007-01-01 --before 2007-01-31
|
99
|
+
. punch list
|
100
|
+
. punch list -A 2007-01-01 -B 2007-01-31
|
101
|
+
txt
|
102
|
+
|
103
|
+
mixin :optional_project, :option_after, :option_before, :option_db
|
104
|
+
|
105
|
+
run{ y db.list(project, 'window' => (after .. before)) }
|
106
|
+
}
|
107
|
+
|
108
|
+
mode(:total){
|
109
|
+
description 'total the time for a single/all projects, possibly filtering by time'
|
110
|
+
|
111
|
+
examples <<-txt
|
112
|
+
. punch total projectname
|
113
|
+
. punch total projectname --after 2007-01-01 --before 2007-01-31
|
114
|
+
. punch total
|
115
|
+
. punch total -A 2007-01-01 -B 2007-01-31
|
116
|
+
txt
|
117
|
+
|
118
|
+
mixin :optional_project, :option_after, :option_before, :option_db
|
119
|
+
|
120
|
+
run{ y db.total(project, 'window' => (after .. before)) }
|
121
|
+
}
|
122
|
+
|
123
|
+
mode(:delete){
|
124
|
+
description 'delete all records for a project'
|
125
|
+
|
126
|
+
examples <<-txt
|
127
|
+
. punch delete projectname
|
128
|
+
txt
|
129
|
+
|
130
|
+
mixin :argument_project, :option_db
|
131
|
+
|
132
|
+
run{
|
133
|
+
y db.delete(project)
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
mode(:dump){
|
138
|
+
description 'cat the yaml db with lock held'
|
139
|
+
|
140
|
+
examples <<-txt
|
141
|
+
. punch dump
|
142
|
+
. punch dump projectname
|
143
|
+
txt
|
144
|
+
|
145
|
+
mixin :optional_project, :option_db
|
146
|
+
|
147
|
+
run{ y db.dump(project) }
|
148
|
+
}
|
149
|
+
|
150
|
+
|
151
|
+
mixin :argument_project do
|
152
|
+
argument(:project){
|
153
|
+
attr
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
mixin :optional_project do
|
158
|
+
argument(:project){
|
159
|
+
optional
|
160
|
+
attr
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
mixin :argument_punch_in do
|
165
|
+
argument(:punch_in){
|
166
|
+
cast{|value| Time.parse value}
|
167
|
+
attr
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
mixin :argument_punch_out do
|
172
|
+
argument(:punch_out){
|
173
|
+
cast{|value| Time.parse value}
|
174
|
+
attr
|
175
|
+
}
|
176
|
+
end
|
177
|
+
|
178
|
+
mixin :argument_message do
|
179
|
+
argument(:message){
|
180
|
+
attr
|
181
|
+
arity -2
|
182
|
+
}
|
183
|
+
end
|
184
|
+
|
185
|
+
mixin :option_message do
|
186
|
+
option(:message, :m){
|
187
|
+
argument :required
|
188
|
+
attr
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
mixin :option_now do
|
193
|
+
option(:now){
|
194
|
+
optional
|
195
|
+
desc 'consider this time as the current time'
|
196
|
+
cast{|value| Time.parse value}
|
197
|
+
default Time::Now
|
198
|
+
attr
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
mixin :option_db do
|
203
|
+
option(:db){
|
204
|
+
argument :required
|
205
|
+
default File.join(Home, '.punch.yml')
|
206
|
+
attr{|param| DB.new(param.value) }
|
207
|
+
}
|
208
|
+
end
|
209
|
+
|
210
|
+
mixin :option_after do
|
211
|
+
option(:after, :A){
|
212
|
+
argument :required
|
213
|
+
desc 'limit data shown to entries after this iso8601 timestamp'
|
214
|
+
default Time::Beginning
|
215
|
+
cast :time
|
216
|
+
attr
|
217
|
+
}
|
218
|
+
end
|
219
|
+
|
220
|
+
mixin :option_before do
|
221
|
+
option(:before, :B){
|
222
|
+
argument :required
|
223
|
+
desc 'limit data shown to entries before this iso8601 timestamp'
|
224
|
+
default Time::End
|
225
|
+
cast :time
|
226
|
+
attr
|
227
|
+
}
|
228
|
+
end
|
229
|
+
|
230
|
+
run{ help! }
|
231
|
+
}
|
232
|
+
|
233
|
+
|
234
|
+
BEGIN {
|
235
|
+
require 'yaml'
|
236
|
+
require 'yaml/store'
|
237
|
+
require 'time'
|
238
|
+
require 'pathname'
|
239
|
+
|
240
|
+
begin
|
241
|
+
require 'rubygems'
|
242
|
+
gem 'main', '~> 2.6.0'
|
243
|
+
rescue
|
244
|
+
42
|
245
|
+
end
|
246
|
+
|
247
|
+
begin
|
248
|
+
require 'main'
|
249
|
+
rescue
|
250
|
+
abort "gem install main"
|
251
|
+
end
|
252
|
+
|
253
|
+
begin
|
254
|
+
require 'orderedhash'
|
255
|
+
rescue
|
256
|
+
abort "gem install orderedhash"
|
257
|
+
end
|
258
|
+
|
259
|
+
Home = File.expand_path(ENV['HOME'] || '~')
|
260
|
+
|
261
|
+
### hackity hack, don't talk back
|
262
|
+
|
263
|
+
class Object #{{{
|
264
|
+
def yaml_inline!
|
265
|
+
class << self
|
266
|
+
def to_yaml_style() :inline end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end #}}}
|
270
|
+
|
271
|
+
class Time #{{{
|
272
|
+
Beginning = Time.at(0).iso8601
|
273
|
+
|
274
|
+
End = Time.at((2**31)-1).iso8601
|
275
|
+
|
276
|
+
Now = Time.now.iso8601
|
277
|
+
|
278
|
+
Null = Time.at(0).instance_eval do
|
279
|
+
def to_s() "" end
|
280
|
+
def inspect() "" end
|
281
|
+
self
|
282
|
+
end
|
283
|
+
|
284
|
+
def to_s(n=0) iso8601(n) end
|
285
|
+
alias_method 'inspect', 'to_s'
|
286
|
+
|
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'
|
292
|
+
end
|
293
|
+
Parse.call string
|
294
|
+
end
|
295
|
+
|
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]
|
313
|
+
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 )
|
319
|
+
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 ) )
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end #}}}
|
330
|
+
|
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]
|
336
|
+
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
|
345
|
+
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
|
384
|
+
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'
|
395
|
+
end
|
396
|
+
entry = create_entry entry
|
397
|
+
entry['log'] = [entry['log'], message].flatten.compact
|
398
|
+
list[-1] = entry
|
399
|
+
entry
|
400
|
+
end
|
401
|
+
end #}}}
|
402
|
+
|
403
|
+
def clock project, punch_in, punch_out, options = {} #{{{
|
404
|
+
@db.transaction do
|
405
|
+
log = [
|
406
|
+
"punch in @ #{ punch_in }",
|
407
|
+
options.getopt('message'),
|
408
|
+
"punch out @ #{ punch_out }",
|
409
|
+
].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)
|
428
|
+
end
|
429
|
+
end.map{|entry| create_entry entry}
|
430
|
+
else
|
431
|
+
@db.roots
|
432
|
+
end
|
433
|
+
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!
|
450
|
+
if project
|
451
|
+
h[project] = status if project == root
|
452
|
+
else
|
453
|
+
h[root] = status
|
454
|
+
end
|
455
|
+
end
|
456
|
+
h
|
457
|
+
end
|
458
|
+
end #}}}
|
459
|
+
|
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)
|
468
|
+
end
|
469
|
+
filtered = { project => selected }
|
470
|
+
else
|
471
|
+
filtered = Hash.new
|
472
|
+
@db.roots.each do |project|
|
473
|
+
selected = @db[project.to_s].select do |entry|
|
474
|
+
a, b = entry.values_at 'in', 'out'
|
475
|
+
window === Time.parse(a.to_s) and window === Time.parse(b.to_s)
|
476
|
+
end
|
477
|
+
filtered.update project => selected
|
478
|
+
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)
|
489
|
+
end
|
490
|
+
total.update project => ('%0.2d:%0.2d:%0.2d' % sum.hms)
|
491
|
+
end
|
492
|
+
total
|
493
|
+
end
|
494
|
+
end #}}}
|
495
|
+
|
496
|
+
def delete project #{{{
|
497
|
+
@db.transaction do
|
498
|
+
@db.delete project
|
499
|
+
project
|
500
|
+
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|
|
513
|
+
dumped[project] = Array.new
|
514
|
+
@db[project].each do |entry|
|
515
|
+
dumped[project] << create_entry(entry)
|
516
|
+
end
|
517
|
+
end
|
518
|
+
end
|
519
|
+
dumped
|
520
|
+
end
|
521
|
+
end #}}}
|
522
|
+
|
523
|
+
def create project #{{{
|
524
|
+
project = project.to_s.strip
|
525
|
+
@db[project.to_s] ||= Array.new unless project.empty?
|
526
|
+
end #}}}
|
527
|
+
|
528
|
+
def create_entry hash = {} #{{{
|
529
|
+
entry = OrderedHash.new
|
530
|
+
%w[ in out log ].each do |key|
|
531
|
+
entry[key] = hash.getopt key
|
532
|
+
end
|
533
|
+
entry
|
534
|
+
end #}}}
|
535
|
+
end #}}}
|
536
|
+
}
|
data/gemspec.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
lib, version = File::basename(File::dirname(File::expand_path(__FILE__))).split %r/-/, 2
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
Gem::Specification::new do |spec|
|
6
|
+
$VERBOSE = nil
|
7
|
+
|
8
|
+
shiteless = lambda do |list|
|
9
|
+
list.delete_if do |file|
|
10
|
+
file =~ %r/\.svn/ or
|
11
|
+
file =~ %r/\.tmp/
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
spec.name = lib
|
16
|
+
spec.version = version
|
17
|
+
spec.platform = Gem::Platform::RUBY
|
18
|
+
spec.summary = lib
|
19
|
+
|
20
|
+
spec.files = shiteless[Dir::glob("**/**")]
|
21
|
+
spec.executables = shiteless[Dir::glob("bin/*")].map{|exe| File::basename exe}
|
22
|
+
|
23
|
+
spec.require_path = "lib"
|
24
|
+
spec.autorequire = lib
|
25
|
+
|
26
|
+
spec.has_rdoc = File::exist? "doc"
|
27
|
+
spec.test_suite_file = "test/#{ lib }.rb" if File::directory? "test"
|
28
|
+
spec.add_dependency 'main', '>= 2.6.0'
|
29
|
+
spec.add_dependency 'systemu', '>= 1.2.0'
|
30
|
+
spec.add_dependency 'orderedhash', '>= 0.0.3'
|
31
|
+
spec.add_dependency 'attributes', '5.0.0'
|
32
|
+
|
33
|
+
spec.extensions << "extconf.rb" if File::exists? "extconf.rb"
|
34
|
+
|
35
|
+
spec.author = "Ara T. Howard"
|
36
|
+
spec.email = "ara.t.howard@gmail.com"
|
37
|
+
spec.homepage = "http://codeforpeople.com/lib/ruby/#{ lib }/"
|
38
|
+
end
|
data/install.rb
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rbconfig'
|
3
|
+
require 'find'
|
4
|
+
require 'ftools'
|
5
|
+
require 'tempfile'
|
6
|
+
include Config
|
7
|
+
|
8
|
+
LIBDIR = "lib"
|
9
|
+
LIBDIR_MODE = 0644
|
10
|
+
|
11
|
+
BINDIR = "bin"
|
12
|
+
BINDIR_MODE = 0755
|
13
|
+
|
14
|
+
|
15
|
+
$srcdir = CONFIG["srcdir"]
|
16
|
+
$version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
|
17
|
+
$libdir = File.join(CONFIG["libdir"], "ruby", $version)
|
18
|
+
$archdir = File.join($libdir, CONFIG["arch"])
|
19
|
+
$site_libdir = $:.find {|x| x =~ /site_ruby$/}
|
20
|
+
$bindir = CONFIG["bindir"] || CONFIG['BINDIR']
|
21
|
+
$ruby_install_name = CONFIG['ruby_install_name'] || CONFIG['RUBY_INSTALL_NAME'] || 'ruby'
|
22
|
+
$ruby_ext = CONFIG['EXEEXT'] || ''
|
23
|
+
$ruby = File.join($bindir, ($ruby_install_name + $ruby_ext))
|
24
|
+
|
25
|
+
if !$site_libdir
|
26
|
+
$site_libdir = File.join($libdir, "site_ruby")
|
27
|
+
elsif $site_libdir !~ %r/#{Regexp.quote($version)}/
|
28
|
+
$site_libdir = File.join($site_libdir, $version)
|
29
|
+
end
|
30
|
+
|
31
|
+
def install_rb(srcdir=nil, destdir=nil, mode=nil, bin=nil)
|
32
|
+
#{{{
|
33
|
+
path = []
|
34
|
+
dir = []
|
35
|
+
Find.find(srcdir) do |f|
|
36
|
+
next unless FileTest.file?(f)
|
37
|
+
next if (f = f[srcdir.length+1..-1]) == nil
|
38
|
+
next if (/CVS$/ =~ File.dirname(f))
|
39
|
+
next if (/\.svn/ =~ File.dirname(f))
|
40
|
+
next if f =~ %r/\.lnk/
|
41
|
+
next if f =~ %r/\.svn/
|
42
|
+
next if f =~ %r/\.swp/
|
43
|
+
next if f =~ %r/\.svn/
|
44
|
+
path.push f
|
45
|
+
dir |= [File.dirname(f)]
|
46
|
+
end
|
47
|
+
for f in dir
|
48
|
+
next if f == "."
|
49
|
+
next if f == "CVS"
|
50
|
+
File::makedirs(File.join(destdir, f))
|
51
|
+
end
|
52
|
+
for f in path
|
53
|
+
next if (/\~$/ =~ f)
|
54
|
+
next if (/^\./ =~ File.basename(f))
|
55
|
+
unless bin
|
56
|
+
File::install(File.join(srcdir, f), File.join(destdir, f), mode, true)
|
57
|
+
else
|
58
|
+
from = File.join(srcdir, f)
|
59
|
+
to = File.join(destdir, f)
|
60
|
+
shebangify(from) do |sf|
|
61
|
+
$deferr.print from, " -> ", File::catname(from, to), "\n"
|
62
|
+
$deferr.printf "chmod %04o %s\n", mode, to
|
63
|
+
File::install(sf, to, mode, false)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
#}}}
|
68
|
+
end
|
69
|
+
def shebangify f
|
70
|
+
#{{{
|
71
|
+
open(f) do |fd|
|
72
|
+
buf = fd.read 42
|
73
|
+
if buf =~ %r/^\s*#\s*!.*ruby/o
|
74
|
+
ftmp = Tempfile::new("#{ $$ }_#{ File::basename(f) }")
|
75
|
+
begin
|
76
|
+
fd.rewind
|
77
|
+
ftmp.puts "#!#{ $ruby }"
|
78
|
+
while((buf = fd.read(8192)))
|
79
|
+
ftmp.write buf
|
80
|
+
end
|
81
|
+
ftmp.close
|
82
|
+
yield ftmp.path
|
83
|
+
ensure
|
84
|
+
ftmp.close!
|
85
|
+
end
|
86
|
+
else
|
87
|
+
yield f
|
88
|
+
end
|
89
|
+
end
|
90
|
+
#}}}
|
91
|
+
end
|
92
|
+
def ARGV.switch
|
93
|
+
#{{{
|
94
|
+
return nil if self.empty?
|
95
|
+
arg = self.shift
|
96
|
+
return nil if arg == '--'
|
97
|
+
if arg =~ /^-(.)(.*)/
|
98
|
+
return arg if $1 == '-'
|
99
|
+
raise 'unknown switch "-"' if $2.index('-')
|
100
|
+
self.unshift "-#{$2}" if $2.size > 0
|
101
|
+
"-#{$1}"
|
102
|
+
else
|
103
|
+
self.unshift arg
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
#}}}
|
107
|
+
end
|
108
|
+
def ARGV.req_arg
|
109
|
+
#{{{
|
110
|
+
self.shift || raise('missing argument')
|
111
|
+
#}}}
|
112
|
+
end
|
113
|
+
def linkify d, linked = []
|
114
|
+
#--{{{
|
115
|
+
if test ?d, d
|
116
|
+
versioned = Dir[ File::join(d, "*-[0-9].[0-9].[0-9].rb") ]
|
117
|
+
versioned.each do |v|
|
118
|
+
src, dst = v, v.gsub(%r/\-[\d\.]+\.rb$/, '.rb')
|
119
|
+
lnk = nil
|
120
|
+
begin
|
121
|
+
if test ?l, dst
|
122
|
+
lnk = "#{ dst }.lnk"
|
123
|
+
puts "#{ dst } -> #{ lnk }"
|
124
|
+
File::rename dst, lnk
|
125
|
+
end
|
126
|
+
unless test ?e, dst
|
127
|
+
puts "#{ src } -> #{ dst }"
|
128
|
+
File::copy src, dst
|
129
|
+
linked << dst
|
130
|
+
end
|
131
|
+
ensure
|
132
|
+
if lnk
|
133
|
+
at_exit do
|
134
|
+
puts "#{ lnk } -> #{ dst }"
|
135
|
+
File::rename lnk, dst
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
linked
|
142
|
+
#--}}}
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
#
|
147
|
+
# main program
|
148
|
+
#
|
149
|
+
|
150
|
+
libdir = $site_libdir
|
151
|
+
bindir = $bindir
|
152
|
+
no_linkify = false
|
153
|
+
linked = nil
|
154
|
+
help = false
|
155
|
+
|
156
|
+
usage = <<-usage
|
157
|
+
#{ File::basename $0 }
|
158
|
+
-d, --destdir <destdir>
|
159
|
+
-l, --libdir <libdir>
|
160
|
+
-b, --bindir <bindir>
|
161
|
+
-r, --ruby <ruby>
|
162
|
+
-n, --no_linkify
|
163
|
+
-s, --sudo
|
164
|
+
-h, --help
|
165
|
+
usage
|
166
|
+
|
167
|
+
begin
|
168
|
+
while switch = ARGV.switch
|
169
|
+
case switch
|
170
|
+
when '-d', '--destdir'
|
171
|
+
libdir = ARGV.req_arg
|
172
|
+
when '-l', '--libdir'
|
173
|
+
libdir = ARGV.req_arg
|
174
|
+
when '-b', '--bindir'
|
175
|
+
bindir = ARGV.req_arg
|
176
|
+
when '-r', '--ruby'
|
177
|
+
$ruby = ARGV.req_arg
|
178
|
+
when '-n', '--no_linkify'
|
179
|
+
no_linkify = true
|
180
|
+
when '-s', '--sudo'
|
181
|
+
sudo = 'sudo'
|
182
|
+
when '-h', '--help'
|
183
|
+
help = true
|
184
|
+
else
|
185
|
+
raise "unknown switch #{switch.dump}"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
rescue
|
189
|
+
STDERR.puts $!.to_s
|
190
|
+
STDERR.puts usage
|
191
|
+
exit 1
|
192
|
+
end
|
193
|
+
|
194
|
+
if help
|
195
|
+
STDOUT.puts usage
|
196
|
+
exit
|
197
|
+
end
|
198
|
+
|
199
|
+
system "#{ sudo } #{ $ruby } pre-install.rb" if test(?s, 'pre-install.rb')
|
200
|
+
|
201
|
+
unless no_linkify
|
202
|
+
linked = linkify('lib') + linkify('bin')
|
203
|
+
end
|
204
|
+
|
205
|
+
system "#{ $ruby } extconf.rb && make && #{ sudo } make install" if test(?s, 'extconf.rb')
|
206
|
+
|
207
|
+
install_rb(LIBDIR, libdir, LIBDIR_MODE)
|
208
|
+
install_rb(BINDIR, bindir, BINDIR_MODE, bin=true)
|
209
|
+
|
210
|
+
if linked
|
211
|
+
linked.each{|path| File::rm_f path}
|
212
|
+
end
|
213
|
+
|
214
|
+
system "#{ sudo } #{ $ruby } post-install.rb" if test(?s, 'post-install.rb')
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: punch
|
5
|
+
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:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- 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: []
|
48
|
+
|
49
|
+
dependencies:
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: main
|
52
|
+
version_requirement:
|
53
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 2.6.0
|
58
|
+
version:
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: systemu
|
61
|
+
version_requirement:
|
62
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 1.2.0
|
67
|
+
version:
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: orderedhash
|
70
|
+
version_requirement:
|
71
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.0.3
|
76
|
+
version:
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: attributes
|
79
|
+
version_requirement:
|
80
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
81
|
+
requirements:
|
82
|
+
- - "="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 5.0.0
|
85
|
+
version:
|