webby 0.4.0 → 0.5.0
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/History.txt +7 -0
- data/Manifest.txt +6 -0
- data/README.txt +10 -9
- data/Rakefile +2 -0
- data/bin/webby +1 -0
- data/data/content/css/site.css +59 -0
- data/data/layouts/default.rhtml +9 -9
- data/data/tasks/create.rake +1 -0
- data/data/tasks/growl.rake +12 -0
- data/data/tasks/heel.rake +12 -6
- data/lib/webby.rb +35 -2
- data/lib/webby/auto_builder.rb +14 -6
- data/lib/webby/builder.rb +31 -23
- data/lib/webby/file.rb +9 -12
- data/lib/webby/main.rb +13 -7
- data/lib/webby/pages_db.rb +101 -12
- data/lib/webby/renderer.rb +94 -7
- data/lib/webby/resource.rb +46 -13
- data/lib/webby/stelan/paginator.rb +149 -0
- data/lib/webby/stelan/spawner.rb +337 -0
- data/spec/webby/file_spec.rb +1 -2
- data/website/Rakefile +2 -0
- data/website/content/css/coderay.css +111 -0
- data/website/content/css/site.css +121 -25
- data/website/content/download.txt +7 -1
- data/website/content/index.txt +7 -9
- data/website/content/manual.txt +23 -0
- data/website/content/robots.txt +6 -0
- data/website/content/tips_and_tricks.txt +2 -2
- data/website/content/tutorial.txt +8 -8
- data/website/layouts/default.rhtml +16 -13
- data/website/tasks/create.rake +2 -0
- data/website/tasks/growl.rake +12 -0
- data/website/tasks/heel.rake +12 -6
- metadata +19 -4
data/lib/webby/resource.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $Id: resource.rb
|
1
|
+
# $Id: resource.rb 46 2007-11-27 03:31:29Z tim_pease $
|
2
2
|
|
3
3
|
module Webby
|
4
4
|
|
@@ -54,6 +54,9 @@ class Resource
|
|
54
54
|
# Resource file modification time
|
55
55
|
attr_reader :mtime
|
56
56
|
|
57
|
+
# Resource page number (if needed)
|
58
|
+
attr_reader :number
|
59
|
+
|
57
60
|
# call-seq:
|
58
61
|
# Resource.new( filename ) => resource
|
59
62
|
#
|
@@ -66,6 +69,7 @@ class Resource
|
|
66
69
|
@ext = ::File.extname(@path).sub(%r/\A\.?/o, '')
|
67
70
|
@mtime = ::File.mtime @path
|
68
71
|
|
72
|
+
@number = nil
|
69
73
|
@rendering = false
|
70
74
|
|
71
75
|
# deal with the meta-data
|
@@ -119,7 +123,7 @@ class Resource
|
|
119
123
|
return @mdata['extension'] if @mdata.has_key? 'extension'
|
120
124
|
|
121
125
|
if @mdata.has_key? 'layout'
|
122
|
-
lyt = self.class.layouts.
|
126
|
+
lyt = self.class.layouts.find :filename => @mdata['layout']
|
123
127
|
break if lyt.nil?
|
124
128
|
return lyt.extension
|
125
129
|
end
|
@@ -138,18 +142,45 @@ class Resource
|
|
138
142
|
# the 'destination' propery in the resource's meta-data.
|
139
143
|
#
|
140
144
|
def destination
|
141
|
-
return @dest if defined? @dest
|
142
|
-
return @dest = ::Webby.
|
145
|
+
return @dest if defined? @dest and @dest
|
146
|
+
return @dest = ::Webby.cairn if is_layout?
|
143
147
|
|
144
148
|
@dest = if @mdata.has_key? 'destination' then @mdata['destination']
|
145
149
|
else File.join(dir, filename) end
|
146
150
|
|
147
151
|
@dest = File.join(::Webby.config['output_dir'], @dest)
|
152
|
+
@dest << @number.to_s if @number
|
148
153
|
@dest << '.'
|
149
154
|
@dest << extension
|
150
155
|
@dest
|
151
156
|
end
|
152
157
|
|
158
|
+
# call-seq
|
159
|
+
# href => string or nil
|
160
|
+
#
|
161
|
+
# Returns a string suitable for use as an href linking to this page. Nil
|
162
|
+
# is returned for layouts.
|
163
|
+
#
|
164
|
+
def href
|
165
|
+
return nil if is_layout?
|
166
|
+
return @href if defined? @href and @href
|
167
|
+
|
168
|
+
@href = destination.sub(::Webby.config['output_dir'], '')
|
169
|
+
@href
|
170
|
+
end
|
171
|
+
|
172
|
+
# call-seq:
|
173
|
+
# resource.number = Integer
|
174
|
+
#
|
175
|
+
# Sets the page number for the current resource to the given integer. This
|
176
|
+
# number is used to modify the output destination for resources that
|
177
|
+
# require pagination.
|
178
|
+
#
|
179
|
+
def number=( num )
|
180
|
+
@number = num
|
181
|
+
@dest = nil
|
182
|
+
end
|
183
|
+
|
153
184
|
# call-seq:
|
154
185
|
# render => string
|
155
186
|
#
|
@@ -160,11 +191,12 @@ class Resource
|
|
160
191
|
# Note, this only renders this resource. The returned string does not
|
161
192
|
# include any layout rendering.
|
162
193
|
#
|
163
|
-
def render
|
194
|
+
def render( renderer = nil )
|
164
195
|
raise Error, "page '#@path' is in a rendering loop" if @rendering
|
165
196
|
|
166
197
|
@rendering = true
|
167
|
-
|
198
|
+
renderer ||= Renderer.new(self)
|
199
|
+
content = renderer.render_page
|
168
200
|
@rendering = false
|
169
201
|
|
170
202
|
return content
|
@@ -213,23 +245,24 @@ class Resource
|
|
213
245
|
return @mdata['dirty'] if @mdata.has_key? 'dirty'
|
214
246
|
|
215
247
|
# if the destination file does not exist, then we are dirty
|
216
|
-
return
|
248
|
+
return true unless test ?e, destination
|
217
249
|
|
218
250
|
# if this file's mtime is larger than the destination file's
|
219
251
|
# mtime, then we are dirty
|
220
|
-
|
221
|
-
return
|
252
|
+
dirty = @mtime > File.mtime(destination)
|
253
|
+
return dirty if is_static? or dirty
|
222
254
|
|
223
255
|
# check to see if the layout is dirty, and it it is then we
|
224
256
|
# are dirty, too
|
225
257
|
if @mdata.has_key? 'layout'
|
226
|
-
lyt = self.class.layouts.
|
227
|
-
|
228
|
-
|
258
|
+
lyt = self.class.layouts.find :filename => @mdata['layout']
|
259
|
+
unless lyt.nil?
|
260
|
+
return true if lyt.dirty?
|
261
|
+
end
|
229
262
|
end
|
230
263
|
|
231
264
|
# if we got here, then we are not dirty
|
232
|
-
|
265
|
+
false
|
233
266
|
end
|
234
267
|
|
235
268
|
# call-seq:
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# This code was originally written by Bruce Williams, and it is available
|
2
|
+
# as the Paginator gem. I've added a few helper methods and modifications so
|
3
|
+
# it plays a little more nicely with Webby. Specifically, a Webby::Resource
|
4
|
+
# can be given to the Page and used to generate links to the previous and
|
5
|
+
# next pages.
|
6
|
+
#
|
7
|
+
# Many thanks to Bruce Williams for letting me use his work. Drop him a note
|
8
|
+
# of praise scribbled on the back of a $100 bill. He'd appreciate it.
|
9
|
+
|
10
|
+
require 'forwardable'
|
11
|
+
|
12
|
+
module Webby
|
13
|
+
class Paginator
|
14
|
+
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
class ArgumentError < ::ArgumentError; end
|
18
|
+
class MissingCountError < ArgumentError; end
|
19
|
+
class MissingSelectError < ArgumentError; end
|
20
|
+
|
21
|
+
attr_reader :per_page, :count, :resource
|
22
|
+
|
23
|
+
# Instantiate a new Paginator object
|
24
|
+
#
|
25
|
+
# Provide:
|
26
|
+
# * A total count of the number of objects to paginate
|
27
|
+
# * The number of objects in each page
|
28
|
+
# * A block that returns the array of items
|
29
|
+
# * The block is passed the item offset
|
30
|
+
# (and the number of items to show per page, for
|
31
|
+
# convenience, if the arity is 2)
|
32
|
+
def initialize(count, per_page, resource, &select)
|
33
|
+
@count, @per_page, @resource = count, per_page, resource
|
34
|
+
unless select
|
35
|
+
raise MissingSelectError, "Must provide block to select data for each page"
|
36
|
+
end
|
37
|
+
@select = select
|
38
|
+
end
|
39
|
+
|
40
|
+
# Total number of pages
|
41
|
+
def number_of_pages
|
42
|
+
(@count / @per_page).to_i + (@count % @per_page > 0 ? 1 : 0)
|
43
|
+
end
|
44
|
+
|
45
|
+
# First page object
|
46
|
+
def first
|
47
|
+
page 1
|
48
|
+
end
|
49
|
+
|
50
|
+
# Last page object
|
51
|
+
def last
|
52
|
+
page number_of_pages
|
53
|
+
end
|
54
|
+
|
55
|
+
def each
|
56
|
+
1.upto(number_of_pages) do |number|
|
57
|
+
yield page(number)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Retrieve page object by number
|
62
|
+
def page(number)
|
63
|
+
number = (n = number.to_i) > 0 ? n : 1
|
64
|
+
Page.new(self, number, lambda {
|
65
|
+
offset = (number - 1) * @per_page
|
66
|
+
args = [offset]
|
67
|
+
args << @per_page if @select.arity == 2
|
68
|
+
@select.call(*args)
|
69
|
+
})
|
70
|
+
end
|
71
|
+
|
72
|
+
# Page object
|
73
|
+
#
|
74
|
+
# Retrieves items for a page and provides metadata about the position
|
75
|
+
# of the page in the paginator
|
76
|
+
class Page
|
77
|
+
|
78
|
+
include Enumerable
|
79
|
+
|
80
|
+
attr_reader :number, :pager
|
81
|
+
|
82
|
+
def initialize(pager, number, select) #:nodoc:
|
83
|
+
@pager, @number = pager, number
|
84
|
+
@offset = (number - 1) * pager.per_page
|
85
|
+
@select = select
|
86
|
+
|
87
|
+
@pager.resource.number = number
|
88
|
+
end
|
89
|
+
|
90
|
+
# Retrieve the items for this page
|
91
|
+
# * Caches
|
92
|
+
def items
|
93
|
+
@items ||= @select.call
|
94
|
+
end
|
95
|
+
|
96
|
+
# Checks to see if there's a page before this one
|
97
|
+
def prev?
|
98
|
+
@number > 1
|
99
|
+
end
|
100
|
+
|
101
|
+
# Get previous page (if possible)
|
102
|
+
def prev
|
103
|
+
@pager.page(@number - 1) if prev?
|
104
|
+
end
|
105
|
+
|
106
|
+
# Checks to see if there's a page after this one
|
107
|
+
def next?
|
108
|
+
@number < @pager.number_of_pages
|
109
|
+
end
|
110
|
+
|
111
|
+
# Get next page (if possible)
|
112
|
+
def next
|
113
|
+
@pager.page(@number + 1) if next?
|
114
|
+
end
|
115
|
+
|
116
|
+
# The "item number" of the first item on this page
|
117
|
+
def first_item_number
|
118
|
+
1 + @offset
|
119
|
+
end
|
120
|
+
|
121
|
+
# The "item number" of the last item on this page
|
122
|
+
def last_item_number
|
123
|
+
if next?
|
124
|
+
@offset + @pager.per_page
|
125
|
+
else
|
126
|
+
@pager.count
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def ==(other) #:nodoc:
|
131
|
+
@pager == other.pager && self.number == other.number
|
132
|
+
end
|
133
|
+
|
134
|
+
def each(&block)
|
135
|
+
items.each(&block)
|
136
|
+
end
|
137
|
+
|
138
|
+
def method_missing(meth, *args, &block) #:nodoc:
|
139
|
+
if @pager.respond_to?(meth)
|
140
|
+
@pager.__send__(meth, *args, &block)
|
141
|
+
else
|
142
|
+
super
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end # class Paginator
|
149
|
+
end # module Webby
|
@@ -0,0 +1,337 @@
|
|
1
|
+
# $Id: spawner.rb 46 2007-11-27 03:31:29Z tim_pease $
|
2
|
+
|
3
|
+
require 'rbconfig'
|
4
|
+
require 'thread'
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
# == Synopsis
|
8
|
+
#
|
9
|
+
# A class for spawning child processes and ensuring those children continue
|
10
|
+
# running.
|
11
|
+
#
|
12
|
+
# == Details
|
13
|
+
#
|
14
|
+
# When a spawner is created it is given the command to run in a child
|
15
|
+
# process. This child process has +stdin+, +stdout+, and +stderr+ redirected
|
16
|
+
# to +/dev/null+ (this works even on Windows). When the child dies for any
|
17
|
+
# reason, the spawner will restart a new child process in the exact same
|
18
|
+
# manner as the original.
|
19
|
+
#
|
20
|
+
class Spawner
|
21
|
+
|
22
|
+
@dev_null = test(?e, "/dev/null") ? "/dev/null" : "NUL:"
|
23
|
+
|
24
|
+
c = ::Config::CONFIG
|
25
|
+
ruby = File.join(c['bindir'], c['ruby_install_name']) << c['EXEEXT']
|
26
|
+
@ruby = if system('%s -e exit' % ruby) then ruby
|
27
|
+
elsif system('ruby -e exit') then 'ruby'
|
28
|
+
else warn 'no ruby in PATH/CONFIG'
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
attr_reader :ruby
|
33
|
+
attr_reader :dev_null
|
34
|
+
|
35
|
+
def finalizer( cids )
|
36
|
+
pid = $$
|
37
|
+
lambda do
|
38
|
+
break unless pid == $$
|
39
|
+
cids.kill 'TERM', :all
|
40
|
+
end # lambda
|
41
|
+
end # finalizer
|
42
|
+
end
|
43
|
+
|
44
|
+
# call-seq:
|
45
|
+
# Spawner.new( command, *args, opts = {} )
|
46
|
+
#
|
47
|
+
# Creates a new spawner that will execute the given external _command_ in
|
48
|
+
# a sub-process. The calling semantics of <code>Kernel::exec</code> are
|
49
|
+
# used to execute the _command_. Any number of optional _args_ can be
|
50
|
+
# passed to the _command_.
|
51
|
+
#
|
52
|
+
# Available options:
|
53
|
+
#
|
54
|
+
# :spawn => the number of child processes to spawn
|
55
|
+
# :pause => wait time (in seconds) before respawning after termination
|
56
|
+
# :ruby => the Ruby interpreter to use when spawning children
|
57
|
+
# :env => a hash for the child process environment
|
58
|
+
# :stdin => stdin child processes will read from
|
59
|
+
# :stdout => stdout child processes will write to
|
60
|
+
# :stderr => stderr child processes will write to
|
61
|
+
#
|
62
|
+
# The <code>:env</code> option is used to add environemnt variables to
|
63
|
+
# child processes when they are spawned.
|
64
|
+
#
|
65
|
+
# *Note:* all spawned child processes will use the same stdin, stdout, and
|
66
|
+
# stderr if they are given in the options. Otherwise they all default to
|
67
|
+
# <code>/dev/null</code> on *NIX and <code>NUL:</code> on Windows.
|
68
|
+
#
|
69
|
+
def initialize( *args )
|
70
|
+
config = {
|
71
|
+
:ruby => self.class.ruby,
|
72
|
+
:spawn => 1,
|
73
|
+
:pause => 0,
|
74
|
+
:stdin => self.class.dev_null,
|
75
|
+
:stdout => self.class.dev_null,
|
76
|
+
:stderr => self.class.dev_null
|
77
|
+
}
|
78
|
+
config.merge! args.pop if Hash === args.last
|
79
|
+
config[:argv] = args
|
80
|
+
|
81
|
+
raise ArgumentError, 'wrong number of arguments' if args.empty?
|
82
|
+
|
83
|
+
@stop = true
|
84
|
+
@cids = []
|
85
|
+
@group = ThreadGroup.new
|
86
|
+
|
87
|
+
@spawn = config.delete(:spawn)
|
88
|
+
@pause = config.delete(:pause)
|
89
|
+
@ruby = config.delete(:ruby)
|
90
|
+
|
91
|
+
@tmp = child_program(config)
|
92
|
+
|
93
|
+
class << @cids
|
94
|
+
# call-seq:
|
95
|
+
# sync {block}
|
96
|
+
#
|
97
|
+
# Executes the given block in a synchronized fashion -- i.e. only a
|
98
|
+
# single thread can execute at a time. Uses Mutex under the hood.
|
99
|
+
#
|
100
|
+
def sync(&b)
|
101
|
+
@mutex ||= Mutex.new
|
102
|
+
@mutex.synchronize(&b)
|
103
|
+
end
|
104
|
+
|
105
|
+
# call-seq:
|
106
|
+
# kill( signal, num ) => number killed
|
107
|
+
# kill( signal, :all ) => number killed
|
108
|
+
#
|
109
|
+
# Send the _signal_ to a given _num_ of child processes or all child
|
110
|
+
# processes if <code>:all</code> is given instead of a number. Returns
|
111
|
+
# the number of child processes killed.
|
112
|
+
#
|
113
|
+
def kill( signal, arg )
|
114
|
+
return if empty?
|
115
|
+
|
116
|
+
ary = sync do
|
117
|
+
case arg
|
118
|
+
when :all: self.dup
|
119
|
+
when Integer: self.slice(0,arg)
|
120
|
+
else raise ArgumentError end
|
121
|
+
end
|
122
|
+
|
123
|
+
ary.each do |cid|
|
124
|
+
begin
|
125
|
+
Process.kill(signal, cid)
|
126
|
+
rescue SystemCallError
|
127
|
+
sync {delete cid}
|
128
|
+
end
|
129
|
+
end
|
130
|
+
ary.length
|
131
|
+
end # def kill
|
132
|
+
end # class << @cids
|
133
|
+
|
134
|
+
end # def initialize
|
135
|
+
|
136
|
+
attr_reader :spawn
|
137
|
+
attr_accessor :pause
|
138
|
+
|
139
|
+
# call-seq:
|
140
|
+
# spawner.spawn = num
|
141
|
+
#
|
142
|
+
# Set the number of child processes to spawn. If the new spawn number is
|
143
|
+
# less than the current number, then spawner threads will die
|
144
|
+
#
|
145
|
+
def spawn=( num )
|
146
|
+
num = num.abs
|
147
|
+
diff, @spawn = num - @spawn, num
|
148
|
+
return unless running?
|
149
|
+
|
150
|
+
if diff > 0
|
151
|
+
diff.times {_spawn}
|
152
|
+
elsif diff < 0
|
153
|
+
@cids.kill 'TERM', diff.abs
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# call-seq:
|
158
|
+
# start => self
|
159
|
+
#
|
160
|
+
# Spawn the sub-processes.
|
161
|
+
#
|
162
|
+
def start
|
163
|
+
return self if running?
|
164
|
+
@stop = false
|
165
|
+
|
166
|
+
@cleanup = Spawner.finalizer(@cids)
|
167
|
+
ObjectSpace.define_finalizer(self, @cleanup)
|
168
|
+
|
169
|
+
@spawn.times {_spawn}
|
170
|
+
self
|
171
|
+
end
|
172
|
+
|
173
|
+
# call-seq:
|
174
|
+
# stop( timeout = 5 ) => self
|
175
|
+
#
|
176
|
+
# Stop any spawned sub-processes.
|
177
|
+
#
|
178
|
+
def stop( timeout = 5 )
|
179
|
+
return self unless running?
|
180
|
+
@stop = true
|
181
|
+
|
182
|
+
@cleanup.call
|
183
|
+
ObjectSpace.undefine_finalizer(self)
|
184
|
+
|
185
|
+
# the cleanup call sends SIGTERM to all the child processes
|
186
|
+
# however, some might still be hanging around, so we are going to wait
|
187
|
+
# for a timeout interval and then send a SIGKILL to any remaining child
|
188
|
+
# processes
|
189
|
+
nap_time = 0.05 * timeout # sleep for 5% of the timeout interval
|
190
|
+
timeout = Time.now + timeout
|
191
|
+
|
192
|
+
until @cids.empty?
|
193
|
+
sleep nap_time
|
194
|
+
unless Time.now < timeout
|
195
|
+
@cids.kill 'KILL', :all
|
196
|
+
@cids.clear
|
197
|
+
@group.list.each {|t| t.kill}
|
198
|
+
break
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
self
|
203
|
+
end
|
204
|
+
|
205
|
+
# call-seq:
|
206
|
+
# restart( timeout = 5 )
|
207
|
+
#
|
208
|
+
def restart( timeout = 5 )
|
209
|
+
stop( timeout )
|
210
|
+
start
|
211
|
+
end
|
212
|
+
|
213
|
+
# call-seq:
|
214
|
+
# running?
|
215
|
+
#
|
216
|
+
# Returns +true+ if the spawner is currently running; returns +false+
|
217
|
+
# otherwise.
|
218
|
+
#
|
219
|
+
def running?
|
220
|
+
!@stop
|
221
|
+
end
|
222
|
+
|
223
|
+
# call-seq:
|
224
|
+
# join( timeout = nil ) => spawner or nil
|
225
|
+
#
|
226
|
+
# The calling thread will suspend execution until all child processes have
|
227
|
+
# been stopped. Does not return until all spawner threads have exited (the
|
228
|
+
# child processes have been stopped) or until _timeout seconds have
|
229
|
+
# passed. If the timeout expires +nil+ will be returned; otherwise the
|
230
|
+
# spawner is returned.
|
231
|
+
#
|
232
|
+
def join( limit = nil )
|
233
|
+
loop do
|
234
|
+
t = @group.list.first
|
235
|
+
break if t.nil?
|
236
|
+
return nil unless t.join(limit)
|
237
|
+
end
|
238
|
+
self
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
private
|
243
|
+
|
244
|
+
# call-seq:
|
245
|
+
# _spawn => thread
|
246
|
+
#
|
247
|
+
# Creates a thread that will spawn the sub-process via
|
248
|
+
# <code>IO::popen</code>. If the sub-process terminates, it will be
|
249
|
+
# respawned until the +stop+ message is sent to this spawner.
|
250
|
+
#
|
251
|
+
# If an Exception is encountered during the spawning process, a message
|
252
|
+
# will be printed to stderr and the thread will exit.
|
253
|
+
#
|
254
|
+
def _spawn
|
255
|
+
t = Thread.new do
|
256
|
+
catch(:die) do
|
257
|
+
loop do
|
258
|
+
begin
|
259
|
+
io = IO.popen("#{@ruby} #{@tmp.path}", 'r')
|
260
|
+
cid = io.gets.to_i
|
261
|
+
|
262
|
+
@cids.sync {@cids << cid} if cid > 0
|
263
|
+
Process.wait cid
|
264
|
+
rescue Exception => e
|
265
|
+
STDERR.puts e.inspect
|
266
|
+
STDERR.puts e.backtrace.join("\n")
|
267
|
+
throw :die
|
268
|
+
ensure
|
269
|
+
io.close rescue nil
|
270
|
+
@cids.sync {
|
271
|
+
@cids.delete cid
|
272
|
+
throw :die unless @cids.length < @spawn
|
273
|
+
}
|
274
|
+
end
|
275
|
+
|
276
|
+
throw :die if @stop
|
277
|
+
sleep @pause
|
278
|
+
|
279
|
+
end # loop
|
280
|
+
end # catch(:die)
|
281
|
+
end # Thread.new
|
282
|
+
|
283
|
+
@group.add t
|
284
|
+
t
|
285
|
+
end
|
286
|
+
|
287
|
+
# call-seq:
|
288
|
+
# child_program( config ) => tempfile
|
289
|
+
#
|
290
|
+
# Creates a child Ruby program based on the given _config_ hash. The
|
291
|
+
# following hash keys are used:
|
292
|
+
#
|
293
|
+
# :argv => command and arguments passed to <code>Kernel::exec</code>
|
294
|
+
# :env => environment variables for the child process
|
295
|
+
# :cwd => the current working directory to use for the child process
|
296
|
+
# :stdin => stdin the child process will read from
|
297
|
+
# :stdout => stdout the child process will write to
|
298
|
+
# :stderr => stderr the child process will write to
|
299
|
+
#
|
300
|
+
def child_program( config )
|
301
|
+
config = Marshal.dump(config)
|
302
|
+
|
303
|
+
tmp = Tempfile.new(self.class.name.downcase)
|
304
|
+
tmp.write <<-PROG
|
305
|
+
begin
|
306
|
+
config = Marshal.load(#{config.inspect})
|
307
|
+
|
308
|
+
argv = config[:argv]
|
309
|
+
env = config[:env]
|
310
|
+
cwd = config[:cwd]
|
311
|
+
stdin = config[:stdin]
|
312
|
+
stdout = config[:stdout]
|
313
|
+
stderr = config[:stderr]
|
314
|
+
|
315
|
+
Dir.chdir cwd if cwd
|
316
|
+
env.each {|k,v| ENV[k.to_s] = v.to_s} if env
|
317
|
+
rescue Exception => e
|
318
|
+
STDERR.warn e
|
319
|
+
abort
|
320
|
+
end
|
321
|
+
|
322
|
+
STDOUT.puts Process.pid
|
323
|
+
STDOUT.flush
|
324
|
+
|
325
|
+
STDIN.reopen stdin
|
326
|
+
STDOUT.reopen stdout
|
327
|
+
STDERR.reopen stderr
|
328
|
+
|
329
|
+
exec *argv
|
330
|
+
PROG
|
331
|
+
|
332
|
+
tmp.close
|
333
|
+
tmp
|
334
|
+
end
|
335
|
+
end # class Spawner
|
336
|
+
|
337
|
+
# EOF
|