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.
Files changed (4) hide show
  1. data/Rakefile +374 -0
  2. data/forkhandle.gemspec +29 -0
  3. data/lib/forkhandle.rb +96 -0
  4. 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
+ }
@@ -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: []