forkhandle 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/Rakefile +374 -0
- data/forkhandle.gemspec +29 -0
- data/lib/forkhandle.rb +96 -0
- metadata +47 -0
data/Rakefile
ADDED
@@ -0,0 +1,374 @@
|
|
1
|
+
This.rubyforge_project = 'codeforpeople'
|
2
|
+
This.author = "Ara T. Howard"
|
3
|
+
This.email = "ara.t.howard@gmail.com"
|
4
|
+
This.homepage = "https://github.com/ahoward/#{ This.lib }"
|
5
|
+
|
6
|
+
task :license do
|
7
|
+
open('LICENSE', 'w'){|fd| fd.puts "same as ruby's"}
|
8
|
+
end
|
9
|
+
|
10
|
+
task :default do
|
11
|
+
puts((Rake::Task.tasks.map{|task| task.name.gsub(/::/,':')} - ['default']).sort)
|
12
|
+
end
|
13
|
+
|
14
|
+
task :test do
|
15
|
+
run_tests!
|
16
|
+
end
|
17
|
+
|
18
|
+
namespace :test do
|
19
|
+
task(:unit){ run_tests!(:unit) }
|
20
|
+
task(:functional){ run_tests!(:functional) }
|
21
|
+
task(:integration){ run_tests!(:integration) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def run_tests!(which = nil)
|
25
|
+
which ||= '**'
|
26
|
+
test_dir = File.join(This.dir, "test")
|
27
|
+
test_glob ||= File.join(test_dir, "#{ which }/**_test.rb")
|
28
|
+
test_rbs = Dir.glob(test_glob).sort
|
29
|
+
|
30
|
+
div = ('=' * 119)
|
31
|
+
line = ('-' * 119)
|
32
|
+
|
33
|
+
test_rbs.each_with_index do |test_rb, index|
|
34
|
+
testno = index + 1
|
35
|
+
command = "#{ This.ruby } -I ./lib -I ./test/lib #{ test_rb }"
|
36
|
+
|
37
|
+
puts
|
38
|
+
say(div, :color => :cyan, :bold => true)
|
39
|
+
say("@#{ testno } => ", :bold => true, :method => :print)
|
40
|
+
say(command, :color => :cyan, :bold => true)
|
41
|
+
say(line, :color => :cyan, :bold => true)
|
42
|
+
|
43
|
+
system(command)
|
44
|
+
|
45
|
+
say(line, :color => :cyan, :bold => true)
|
46
|
+
|
47
|
+
status = $?.exitstatus
|
48
|
+
|
49
|
+
if status.zero?
|
50
|
+
say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print)
|
51
|
+
say("SUCCESS", :color => :green, :bold => true)
|
52
|
+
else
|
53
|
+
say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print)
|
54
|
+
say("FAILURE", :color => :red, :bold => true)
|
55
|
+
end
|
56
|
+
say(line, :color => :cyan, :bold => true)
|
57
|
+
|
58
|
+
exit(status) unless status.zero?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
task :gemspec do
|
64
|
+
ignore_extensions = ['git', 'svn', 'tmp', /sw./, 'bak', 'gem']
|
65
|
+
ignore_directories = ['pkg']
|
66
|
+
ignore_files = ['test/log']
|
67
|
+
|
68
|
+
shiteless =
|
69
|
+
lambda do |list|
|
70
|
+
list.delete_if do |entry|
|
71
|
+
next unless test(?e, entry)
|
72
|
+
extension = File.basename(entry).split(%r/[.]/).last
|
73
|
+
ignore_extensions.any?{|ext| ext === extension}
|
74
|
+
end
|
75
|
+
list.delete_if do |entry|
|
76
|
+
next unless test(?d, entry)
|
77
|
+
dirname = File.expand_path(entry)
|
78
|
+
ignore_directories.any?{|dir| File.expand_path(dir) == dirname}
|
79
|
+
end
|
80
|
+
list.delete_if do |entry|
|
81
|
+
next unless test(?f, entry)
|
82
|
+
filename = File.expand_path(entry)
|
83
|
+
ignore_files.any?{|file| File.expand_path(file) == filename}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
lib = This.lib
|
88
|
+
object = This.object
|
89
|
+
version = This.version
|
90
|
+
files = shiteless[Dir::glob("**/**")]
|
91
|
+
executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)}
|
92
|
+
#has_rdoc = true #File.exist?('doc')
|
93
|
+
test_files = "test/#{ lib }.rb" if File.file?("test/#{ lib }.rb")
|
94
|
+
summary = object.respond_to?(:summary) ? object.summary : "summary: #{ lib } kicks the ass"
|
95
|
+
description = object.respond_to?(:description) ? object.description : "description: #{ lib } kicks the ass"
|
96
|
+
|
97
|
+
if This.extensions.nil?
|
98
|
+
This.extensions = []
|
99
|
+
extensions = This.extensions
|
100
|
+
%w( Makefile configure extconf.rb ).each do |ext|
|
101
|
+
extensions << ext if File.exists?(ext)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
extensions = [extensions].flatten.compact
|
105
|
+
|
106
|
+
template =
|
107
|
+
if test(?e, 'gemspec.erb')
|
108
|
+
Template{ IO.read('gemspec.erb') }
|
109
|
+
else
|
110
|
+
Template {
|
111
|
+
<<-__
|
112
|
+
## #{ lib }.gemspec
|
113
|
+
#
|
114
|
+
|
115
|
+
Gem::Specification::new do |spec|
|
116
|
+
spec.name = #{ lib.inspect }
|
117
|
+
spec.version = #{ version.inspect }
|
118
|
+
spec.platform = Gem::Platform::RUBY
|
119
|
+
spec.summary = #{ lib.inspect }
|
120
|
+
spec.description = #{ description.inspect }
|
121
|
+
|
122
|
+
spec.files =\n#{ files.sort.pretty_inspect }
|
123
|
+
spec.executables = #{ executables.inspect }
|
124
|
+
|
125
|
+
spec.require_path = "lib"
|
126
|
+
|
127
|
+
spec.test_files = #{ test_files.inspect }
|
128
|
+
|
129
|
+
### spec.add_dependency 'lib', '>= version'
|
130
|
+
#### spec.add_dependency 'map'
|
131
|
+
|
132
|
+
spec.extensions.push(*#{ extensions.inspect })
|
133
|
+
|
134
|
+
spec.rubyforge_project = #{ This.rubyforge_project.inspect }
|
135
|
+
spec.author = #{ This.author.inspect }
|
136
|
+
spec.email = #{ This.email.inspect }
|
137
|
+
spec.homepage = #{ This.homepage.inspect }
|
138
|
+
end
|
139
|
+
__
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
Fu.mkdir_p(This.pkgdir)
|
144
|
+
gemspec = "#{ lib }.gemspec"
|
145
|
+
open(gemspec, "w"){|fd| fd.puts(template)}
|
146
|
+
This.gemspec = gemspec
|
147
|
+
end
|
148
|
+
|
149
|
+
task :gem => [:clean, :gemspec] do
|
150
|
+
Fu.mkdir_p(This.pkgdir)
|
151
|
+
before = Dir['*.gem']
|
152
|
+
cmd = "gem build #{ This.gemspec }"
|
153
|
+
`#{ cmd }`
|
154
|
+
after = Dir['*.gem']
|
155
|
+
gem = ((after - before).first || after.first) or abort('no gem!')
|
156
|
+
Fu.mv(gem, This.pkgdir)
|
157
|
+
This.gem = File.join(This.pkgdir, File.basename(gem))
|
158
|
+
end
|
159
|
+
|
160
|
+
task :readme do
|
161
|
+
samples = ''
|
162
|
+
prompt = '~ > '
|
163
|
+
lib = This.lib
|
164
|
+
version = This.version
|
165
|
+
|
166
|
+
Dir['sample*/*'].sort.each do |sample|
|
167
|
+
samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
|
168
|
+
|
169
|
+
cmd = "cat #{ sample }"
|
170
|
+
samples << Util.indent(prompt + cmd, 2) << "\n\n"
|
171
|
+
samples << Util.indent(`#{ cmd }`, 4) << "\n"
|
172
|
+
|
173
|
+
cmd = "ruby #{ sample }"
|
174
|
+
samples << Util.indent(prompt + cmd, 2) << "\n\n"
|
175
|
+
|
176
|
+
cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -I ./lib #{ sample })'"
|
177
|
+
samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n"
|
178
|
+
end
|
179
|
+
|
180
|
+
template =
|
181
|
+
if test(?e, 'readme.erb')
|
182
|
+
Template{ IO.read('readme.erb') }
|
183
|
+
else
|
184
|
+
Template {
|
185
|
+
<<-__
|
186
|
+
NAME
|
187
|
+
#{ lib }
|
188
|
+
|
189
|
+
DESCRIPTION
|
190
|
+
|
191
|
+
INSTALL
|
192
|
+
gem install #{ lib }
|
193
|
+
|
194
|
+
SAMPLES
|
195
|
+
#{ samples }
|
196
|
+
__
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
open("README", "w"){|fd| fd.puts template}
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
task :clean do
|
205
|
+
Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)}
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
task :release => [:clean, :gemspec, :gem] do
|
210
|
+
gems = Dir[File.join(This.pkgdir, '*.gem')].flatten
|
211
|
+
raise "which one? : #{ gems.inspect }" if gems.size > 1
|
212
|
+
raise "no gems?" if gems.size < 1
|
213
|
+
|
214
|
+
cmd = "gem push #{ This.gem }"
|
215
|
+
puts cmd
|
216
|
+
puts
|
217
|
+
system(cmd)
|
218
|
+
abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero?
|
219
|
+
|
220
|
+
cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.gem }"
|
221
|
+
puts cmd
|
222
|
+
puts
|
223
|
+
system(cmd)
|
224
|
+
abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero?
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
|
229
|
+
|
230
|
+
|
231
|
+
BEGIN {
|
232
|
+
# support for this rakefile
|
233
|
+
#
|
234
|
+
$VERBOSE = nil
|
235
|
+
|
236
|
+
require 'ostruct'
|
237
|
+
require 'erb'
|
238
|
+
require 'fileutils'
|
239
|
+
require 'rbconfig'
|
240
|
+
require 'pp'
|
241
|
+
|
242
|
+
# fu shortcut
|
243
|
+
#
|
244
|
+
Fu = FileUtils
|
245
|
+
|
246
|
+
# cache a bunch of stuff about this rakefile/environment
|
247
|
+
#
|
248
|
+
This = OpenStruct.new
|
249
|
+
|
250
|
+
This.file = File.expand_path(__FILE__)
|
251
|
+
This.dir = File.dirname(This.file)
|
252
|
+
This.pkgdir = File.join(This.dir, 'pkg')
|
253
|
+
|
254
|
+
# grok lib
|
255
|
+
#
|
256
|
+
lib = ENV['LIB']
|
257
|
+
unless lib
|
258
|
+
lib = File.basename(Dir.pwd).sub(/[-].*$/, '')
|
259
|
+
end
|
260
|
+
This.lib = lib
|
261
|
+
|
262
|
+
# grok version
|
263
|
+
#
|
264
|
+
version = ENV['VERSION']
|
265
|
+
unless version
|
266
|
+
require "./lib/#{ This.lib }"
|
267
|
+
This.name = lib.capitalize
|
268
|
+
This.object = eval(This.name)
|
269
|
+
version = This.object.send(:version)
|
270
|
+
end
|
271
|
+
This.version = version
|
272
|
+
|
273
|
+
# we need to know the name of the lib an it's version
|
274
|
+
#
|
275
|
+
abort('no lib') unless This.lib
|
276
|
+
abort('no version') unless This.version
|
277
|
+
|
278
|
+
# discover full path to this ruby executable
|
279
|
+
#
|
280
|
+
c = Config::CONFIG
|
281
|
+
bindir = c["bindir"] || c['BINDIR']
|
282
|
+
ruby_install_name = c['ruby_install_name'] || c['RUBY_INSTALL_NAME'] || 'ruby'
|
283
|
+
ruby_ext = c['EXEEXT'] || ''
|
284
|
+
ruby = File.join(bindir, (ruby_install_name + ruby_ext))
|
285
|
+
This.ruby = ruby
|
286
|
+
|
287
|
+
# some utils
|
288
|
+
#
|
289
|
+
module Util
|
290
|
+
def indent(s, n = 2)
|
291
|
+
s = unindent(s)
|
292
|
+
ws = ' ' * n
|
293
|
+
s.gsub(%r/^/, ws)
|
294
|
+
end
|
295
|
+
|
296
|
+
def unindent(s)
|
297
|
+
indent = nil
|
298
|
+
s.each_line do |line|
|
299
|
+
next if line =~ %r/^\s*$/
|
300
|
+
indent = line[%r/^\s*/] and break
|
301
|
+
end
|
302
|
+
indent ? s.gsub(%r/^#{ indent }/, "") : s
|
303
|
+
end
|
304
|
+
extend self
|
305
|
+
end
|
306
|
+
|
307
|
+
# template support
|
308
|
+
#
|
309
|
+
class Template
|
310
|
+
def initialize(&block)
|
311
|
+
@block = block
|
312
|
+
@template = block.call.to_s
|
313
|
+
end
|
314
|
+
def expand(b=nil)
|
315
|
+
ERB.new(Util.unindent(@template)).result((b||@block).binding)
|
316
|
+
end
|
317
|
+
alias_method 'to_s', 'expand'
|
318
|
+
end
|
319
|
+
def Template(*args, &block) Template.new(*args, &block) end
|
320
|
+
|
321
|
+
# colored console output support
|
322
|
+
#
|
323
|
+
This.ansi = {
|
324
|
+
:clear => "\e[0m",
|
325
|
+
:reset => "\e[0m",
|
326
|
+
:erase_line => "\e[K",
|
327
|
+
:erase_char => "\e[P",
|
328
|
+
:bold => "\e[1m",
|
329
|
+
:dark => "\e[2m",
|
330
|
+
:underline => "\e[4m",
|
331
|
+
:underscore => "\e[4m",
|
332
|
+
:blink => "\e[5m",
|
333
|
+
:reverse => "\e[7m",
|
334
|
+
:concealed => "\e[8m",
|
335
|
+
:black => "\e[30m",
|
336
|
+
:red => "\e[31m",
|
337
|
+
:green => "\e[32m",
|
338
|
+
:yellow => "\e[33m",
|
339
|
+
:blue => "\e[34m",
|
340
|
+
:magenta => "\e[35m",
|
341
|
+
:cyan => "\e[36m",
|
342
|
+
:white => "\e[37m",
|
343
|
+
:on_black => "\e[40m",
|
344
|
+
:on_red => "\e[41m",
|
345
|
+
:on_green => "\e[42m",
|
346
|
+
:on_yellow => "\e[43m",
|
347
|
+
:on_blue => "\e[44m",
|
348
|
+
:on_magenta => "\e[45m",
|
349
|
+
:on_cyan => "\e[46m",
|
350
|
+
:on_white => "\e[47m"
|
351
|
+
}
|
352
|
+
def say(phrase, *args)
|
353
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
354
|
+
options[:color] = args.shift.to_s.to_sym unless args.empty?
|
355
|
+
keys = options.keys
|
356
|
+
keys.each{|key| options[key.to_s.to_sym] = options.delete(key)}
|
357
|
+
|
358
|
+
color = options[:color]
|
359
|
+
bold = options.has_key?(:bold)
|
360
|
+
|
361
|
+
parts = [phrase]
|
362
|
+
parts.unshift(This.ansi[color]) if color
|
363
|
+
parts.unshift(This.ansi[:bold]) if bold
|
364
|
+
parts.push(This.ansi[:clear]) if parts.size > 1
|
365
|
+
|
366
|
+
method = options[:method] || :puts
|
367
|
+
|
368
|
+
Kernel.send(method, parts.join)
|
369
|
+
end
|
370
|
+
|
371
|
+
# always run out of the project dir
|
372
|
+
#
|
373
|
+
Dir.chdir(This.dir)
|
374
|
+
}
|
data/forkhandle.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
## forkhandle.gemspec
|
2
|
+
#
|
3
|
+
|
4
|
+
Gem::Specification::new do |spec|
|
5
|
+
spec.name = "forkhandle"
|
6
|
+
spec.version = "0.0.1"
|
7
|
+
spec.platform = Gem::Platform::RUBY
|
8
|
+
spec.summary = "forkhandle"
|
9
|
+
spec.description = "description: forkhandle kicks the ass"
|
10
|
+
|
11
|
+
spec.files =
|
12
|
+
["Rakefile", "forkhandle.gemspec", "lib", "lib/forkhandle.rb"]
|
13
|
+
|
14
|
+
spec.executables = []
|
15
|
+
|
16
|
+
spec.require_path = "lib"
|
17
|
+
|
18
|
+
spec.test_files = nil
|
19
|
+
|
20
|
+
### spec.add_dependency 'lib', '>= version'
|
21
|
+
#### spec.add_dependency 'map'
|
22
|
+
|
23
|
+
spec.extensions.push(*[])
|
24
|
+
|
25
|
+
spec.rubyforge_project = "codeforpeople"
|
26
|
+
spec.author = "Ara T. Howard"
|
27
|
+
spec.email = "ara.t.howard@gmail.com"
|
28
|
+
spec.homepage = "https://github.com/ahoward/forkhandle"
|
29
|
+
end
|
data/lib/forkhandle.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# managing connection across forks and threads is tricky. most libraries use
|
2
|
+
# and icky idiom that requires each and every client to configure it's own
|
3
|
+
# forking logic, something like
|
4
|
+
#
|
5
|
+
# MyLameLib.after_for do
|
6
|
+
# # close handles you should close
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# many libs also do not provide you with per-thread connection, making MT a
|
10
|
+
# manual process.
|
11
|
+
#
|
12
|
+
# a teeny bit of code can solve both. the concept is simple:
|
13
|
+
#
|
14
|
+
# maintain a table of connections scoped by process id and thread id. any
|
15
|
+
# miss will trigger auto-scrubbing the table, but only connections from
|
16
|
+
# another process (we've been forked) will be closed. this gives
|
17
|
+
#
|
18
|
+
# * per thread connections
|
19
|
+
#
|
20
|
+
# * per process connections
|
21
|
+
#
|
22
|
+
# * auto-matic cleanup after a fork
|
23
|
+
#
|
24
|
+
module ForkHandle
|
25
|
+
def version
|
26
|
+
'0.0.1'
|
27
|
+
end
|
28
|
+
|
29
|
+
@handles = Hash.new
|
30
|
+
@pid = Process.pid
|
31
|
+
@tid = Thread.current.object_id
|
32
|
+
|
33
|
+
attr_accessor :handles
|
34
|
+
attr_accessor :pid
|
35
|
+
attr_accessor :tid
|
36
|
+
|
37
|
+
class Key
|
38
|
+
def for(*args)
|
39
|
+
case
|
40
|
+
when args.size == 1 && args.first.is_a?(Key)
|
41
|
+
args.first
|
42
|
+
else
|
43
|
+
new(*args)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(pid, tid, key)
|
48
|
+
@pid = pid
|
49
|
+
@tid = tid
|
50
|
+
@key = key
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def key_for(key)
|
55
|
+
Key.for(@pid, @tid, key)
|
56
|
+
end
|
57
|
+
|
58
|
+
def get(key, &block)
|
59
|
+
@handles.fetch(key_for(key)) do
|
60
|
+
clear!
|
61
|
+
block.call
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
alias_method :fetch, :get
|
66
|
+
|
67
|
+
def set(key, value)
|
68
|
+
@handles[key_for(key)] = value
|
69
|
+
end
|
70
|
+
|
71
|
+
def clear!
|
72
|
+
each do |key, val|
|
73
|
+
next if key.pid == pid
|
74
|
+
|
75
|
+
begin
|
76
|
+
val.close
|
77
|
+
rescue
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
extend(ForkHandle)
|
84
|
+
end
|
85
|
+
|
86
|
+
Forkhandle = ForkHandle
|
87
|
+
|
88
|
+
|
89
|
+
__END__
|
90
|
+
|
91
|
+
ForkHandle.get(:default){ build_default_connection }
|
92
|
+
|
93
|
+
ForkHandle.set(:default, build_default_connection)
|
94
|
+
|
95
|
+
|
96
|
+
|
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: forkhandle
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ara T. Howard
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-19 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! 'description: forkhandle kicks the ass'
|
15
|
+
email: ara.t.howard@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- Rakefile
|
21
|
+
- forkhandle.gemspec
|
22
|
+
- lib/forkhandle.rb
|
23
|
+
homepage: https://github.com/ahoward/forkhandle
|
24
|
+
licenses: []
|
25
|
+
post_install_message:
|
26
|
+
rdoc_options: []
|
27
|
+
require_paths:
|
28
|
+
- lib
|
29
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project: codeforpeople
|
43
|
+
rubygems_version: 1.8.23
|
44
|
+
signing_key:
|
45
|
+
specification_version: 3
|
46
|
+
summary: forkhandle
|
47
|
+
test_files: []
|