webby 0.9.3-x86-mswin32
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 +206 -0
- data/Manifest.txt +228 -0
- data/README.txt +92 -0
- data/Rakefile +49 -0
- data/bin/webby +41 -0
- data/bin/webby-gen +41 -0
- data/examples/blog/Sitefile +7 -0
- data/examples/blog/tasks/blog.rake +72 -0
- data/examples/blog/templates/atom_feed.erb +40 -0
- data/examples/blog/templates/blog/month.erb +22 -0
- data/examples/blog/templates/blog/post.erb +16 -0
- data/examples/blog/templates/blog/year.erb +22 -0
- data/examples/presentation/Sitefile +10 -0
- data/examples/presentation/content/css/uv/twilight.css +137 -0
- data/examples/presentation/content/presentation/_sample_code.txt +10 -0
- data/examples/presentation/content/presentation/index.txt +63 -0
- data/examples/presentation/content/presentation/s5/blank.gif +0 -0
- data/examples/presentation/content/presentation/s5/bodybg.gif +0 -0
- data/examples/presentation/content/presentation/s5/framing.css +23 -0
- data/examples/presentation/content/presentation/s5/iepngfix.htc +42 -0
- data/examples/presentation/content/presentation/s5/opera.css +7 -0
- data/examples/presentation/content/presentation/s5/outline.css +15 -0
- data/examples/presentation/content/presentation/s5/pretty.css +86 -0
- data/examples/presentation/content/presentation/s5/print.css +25 -0
- data/examples/presentation/content/presentation/s5/s5-core.css +9 -0
- data/examples/presentation/content/presentation/s5/slides.css +3 -0
- data/examples/presentation/content/presentation/s5/slides.js +553 -0
- data/examples/presentation/layouts/presentation.txt +43 -0
- data/examples/presentation/templates/_code_partial.erb +13 -0
- data/examples/presentation/templates/presentation.erb +40 -0
- data/examples/tumblog/Sitefile +9 -0
- data/examples/tumblog/content/css/tumblog.css +308 -0
- data/examples/tumblog/content/images/tumblog/permalink.gif +0 -0
- data/examples/tumblog/content/images/tumblog/rss.gif +0 -0
- data/examples/tumblog/content/tumblog/200806/the-noble-chicken/index.txt +12 -0
- data/examples/tumblog/content/tumblog/200807/historical-perspectives-on-the-classic-chicken-joke/index.txt +12 -0
- data/examples/tumblog/content/tumblog/200807/mad-city-chickens/index.txt +10 -0
- data/examples/tumblog/content/tumblog/200807/the-wisdom-of-the-dutch/index.txt +11 -0
- data/examples/tumblog/content/tumblog/200807/up-a-tree/index.txt +13 -0
- data/examples/tumblog/content/tumblog/index.txt +37 -0
- data/examples/tumblog/content/tumblog/rss.txt +37 -0
- data/examples/tumblog/layouts/tumblog/default.txt +44 -0
- data/examples/tumblog/layouts/tumblog/post.txt +15 -0
- data/examples/tumblog/lib/tumblog_helper.rb +32 -0
- data/examples/tumblog/tasks/tumblog.rake +30 -0
- data/examples/tumblog/templates/atom_feed.erb +40 -0
- data/examples/tumblog/templates/tumblog/conversation.erb +12 -0
- data/examples/tumblog/templates/tumblog/link.erb +10 -0
- data/examples/tumblog/templates/tumblog/photo.erb +13 -0
- data/examples/tumblog/templates/tumblog/post.erb +12 -0
- data/examples/tumblog/templates/tumblog/quote.erb +11 -0
- data/examples/webby/Sitefile +19 -0
- data/examples/webby/content/communicate/index.txt +28 -0
- data/examples/webby/content/css/background.gif +0 -0
- data/examples/webby/content/css/blueprint/print.css +76 -0
- data/examples/webby/content/css/blueprint/screen.css +696 -0
- data/examples/webby/content/css/coderay.css +96 -0
- data/examples/webby/content/css/site.css +196 -0
- data/examples/webby/content/css/uv/twilight.css +137 -0
- data/examples/webby/content/index.txt +37 -0
- data/examples/webby/content/learn/index.txt +28 -0
- data/examples/webby/content/reference/index.txt +204 -0
- data/examples/webby/content/release-notes/index.txt +21 -0
- data/examples/webby/content/release-notes/rel-0-9-0/index.txt +74 -0
- data/examples/webby/content/release-notes/rel-0-9-1/index.txt +93 -0
- data/examples/webby/content/release-notes/rel-0-9-2/index.txt +14 -0
- data/examples/webby/content/release-notes/rel-0-9-3/index.txt +49 -0
- data/examples/webby/content/robots.txt +6 -0
- data/examples/webby/content/script/jquery.corner.js +152 -0
- data/examples/webby/content/script/jquery.js +31 -0
- data/examples/webby/content/sitemap.txt +31 -0
- data/examples/webby/content/tips_and_tricks/index.txt +97 -0
- data/examples/webby/content/tutorial/index.txt +135 -0
- data/examples/webby/content/user-manual/index.txt +419 -0
- data/examples/webby/layouts/default.txt +49 -0
- data/examples/webby/templates/page.erb +10 -0
- data/examples/website/Sitefile +7 -0
- data/examples/website/content/css/blueprint/License.txt +21 -0
- data/examples/website/content/css/blueprint/Readme.txt +100 -0
- data/examples/website/content/css/blueprint/compressed/print.css +76 -0
- data/examples/website/content/css/blueprint/compressed/screen.css +696 -0
- data/examples/website/content/css/blueprint/lib/forms.css +45 -0
- data/examples/website/content/css/blueprint/lib/grid.css +193 -0
- data/examples/website/content/css/blueprint/lib/grid.png +0 -0
- data/examples/website/content/css/blueprint/lib/ie.css +30 -0
- data/examples/website/content/css/blueprint/lib/reset.css +39 -0
- data/examples/website/content/css/blueprint/lib/typography.css +116 -0
- data/examples/website/content/css/blueprint/plugins/buttons/Readme +31 -0
- data/examples/website/content/css/blueprint/plugins/buttons/buttons.css +97 -0
- data/examples/website/content/css/blueprint/plugins/buttons/icons/cross.png +0 -0
- data/examples/website/content/css/blueprint/plugins/buttons/icons/key.png +0 -0
- data/examples/website/content/css/blueprint/plugins/buttons/icons/tick.png +0 -0
- data/examples/website/content/css/blueprint/plugins/css-classes/Readme +14 -0
- data/examples/website/content/css/blueprint/plugins/css-classes/css-classes.css +24 -0
- data/examples/website/content/css/blueprint/plugins/fancy-type/Readme +22 -0
- data/examples/website/content/css/blueprint/plugins/fancy-type/fancy-type-compressed.css +5 -0
- data/examples/website/content/css/blueprint/plugins/fancy-type/fancy-type.css +74 -0
- data/examples/website/content/css/blueprint/print.css +68 -0
- data/examples/website/content/css/blueprint/screen.css +22 -0
- data/examples/website/content/css/coderay.css +111 -0
- data/examples/website/content/css/site.css +67 -0
- data/examples/website/content/index.txt +19 -0
- data/examples/website/layouts/default.txt +58 -0
- data/examples/website/lib/breadcrumbs.rb +28 -0
- data/examples/website/templates/_partial.erb +10 -0
- data/examples/website/templates/page.erb +18 -0
- data/lib/webby.rb +221 -0
- data/lib/webby/apps.rb +12 -0
- data/lib/webby/apps/generator.rb +276 -0
- data/lib/webby/apps/main.rb +255 -0
- data/lib/webby/auto_builder.rb +157 -0
- data/lib/webby/builder.rb +180 -0
- data/lib/webby/core_ext/enumerable.rb +11 -0
- data/lib/webby/core_ext/hash.rb +28 -0
- data/lib/webby/core_ext/kernel.rb +26 -0
- data/lib/webby/core_ext/string.rb +163 -0
- data/lib/webby/core_ext/time.rb +9 -0
- data/lib/webby/filters.rb +83 -0
- data/lib/webby/filters/basepath.rb +97 -0
- data/lib/webby/filters/erb.rb +9 -0
- data/lib/webby/filters/haml.rb +18 -0
- data/lib/webby/filters/markdown.rb +16 -0
- data/lib/webby/filters/outline.rb +309 -0
- data/lib/webby/filters/sass.rb +17 -0
- data/lib/webby/filters/slides.rb +56 -0
- data/lib/webby/filters/textile.rb +16 -0
- data/lib/webby/filters/tidy.rb +76 -0
- data/lib/webby/filters/wiki_words.rb +14 -0
- data/lib/webby/helpers.rb +30 -0
- data/lib/webby/helpers/capture_helper.rb +141 -0
- data/lib/webby/helpers/coderay_helper.rb +69 -0
- data/lib/webby/helpers/graphviz_helper.rb +136 -0
- data/lib/webby/helpers/tag_helper.rb +65 -0
- data/lib/webby/helpers/tex_img_helper.rb +133 -0
- data/lib/webby/helpers/ultraviolet_helper.rb +63 -0
- data/lib/webby/helpers/url_helper.rb +241 -0
- data/lib/webby/journal.rb +126 -0
- data/lib/webby/link_validator.rb +152 -0
- data/lib/webby/renderer.rb +386 -0
- data/lib/webby/resources.rb +136 -0
- data/lib/webby/resources/db.rb +251 -0
- data/lib/webby/resources/layout.rb +54 -0
- data/lib/webby/resources/meta_file.rb +211 -0
- data/lib/webby/resources/page.rb +81 -0
- data/lib/webby/resources/partial.rb +85 -0
- data/lib/webby/resources/resource.rb +201 -0
- data/lib/webby/resources/static.rb +36 -0
- data/lib/webby/stelan/mktemp.rb +135 -0
- data/lib/webby/stelan/paginator.rb +165 -0
- data/lib/webby/stelan/spawner.rb +339 -0
- data/lib/webby/tasks/build.rake +27 -0
- data/lib/webby/tasks/create.rake +22 -0
- data/lib/webby/tasks/deploy.rake +22 -0
- data/lib/webby/tasks/growl.rake +16 -0
- data/lib/webby/tasks/validate.rake +19 -0
- data/spec/core_ext/hash_spec.rb +47 -0
- data/spec/core_ext/string_spec.rb +110 -0
- data/spec/core_ext/time_spec.rb +19 -0
- data/spec/data/hooligans/bad_meta_data_1.txt +34 -0
- data/spec/data/hooligans/bad_meta_data_2.txt +34 -0
- data/spec/data/outline/basic.out +81 -0
- data/spec/data/outline/basic.txt +25 -0
- data/spec/data/outline/no_clobber.out +86 -0
- data/spec/data/outline/numbering.out +81 -0
- data/spec/data/outline/numbering_only.out +21 -0
- data/spec/data/outline/toc_range_1.out +66 -0
- data/spec/data/outline/toc_range_2.out +55 -0
- data/spec/data/outline/toc_style.out +81 -0
- data/spec/data/site/Sitefile +9 -0
- data/spec/data/site/content/_partial.txt +10 -0
- data/spec/data/site/content/css/coderay.css +111 -0
- data/spec/data/site/content/css/site.css +67 -0
- data/spec/data/site/content/css/tumblog.css +308 -0
- data/spec/data/site/content/images/tumblog/permalink.gif +0 -0
- data/spec/data/site/content/images/tumblog/rss.gif +0 -0
- data/spec/data/site/content/index.txt +19 -0
- data/spec/data/site/content/photos.txt +21 -0
- data/spec/data/site/content/tumblog/200806/the-noble-chicken/index.txt +12 -0
- data/spec/data/site/content/tumblog/200807/historical-perspectives-on-the-classic-chicken-joke/index.txt +12 -0
- data/spec/data/site/content/tumblog/200807/mad-city-chickens/index.txt +10 -0
- data/spec/data/site/content/tumblog/200807/the-wisdom-of-the-dutch/index.txt +11 -0
- data/spec/data/site/content/tumblog/200807/up-a-tree/index.txt +13 -0
- data/spec/data/site/content/tumblog/index.txt +37 -0
- data/spec/data/site/content/tumblog/rss.txt +37 -0
- data/spec/data/site/layouts/default.txt +58 -0
- data/spec/data/site/layouts/tumblog/default.txt +44 -0
- data/spec/data/site/layouts/tumblog/post.txt +15 -0
- data/spec/data/site/lib/breadcrumbs.rb +28 -0
- data/spec/data/site/lib/tumblog_helper.rb +32 -0
- data/spec/data/site/tasks/tumblog.rake +30 -0
- data/spec/data/site/templates/_partial.erb +10 -0
- data/spec/data/site/templates/atom_feed.erb +40 -0
- data/spec/data/site/templates/page.erb +18 -0
- data/spec/data/site/templates/presentation.erb +40 -0
- data/spec/data/site/templates/tumblog/conversation.erb +12 -0
- data/spec/data/site/templates/tumblog/link.erb +10 -0
- data/spec/data/site/templates/tumblog/photo.erb +13 -0
- data/spec/data/site/templates/tumblog/post.erb +12 -0
- data/spec/data/site/templates/tumblog/quote.erb +11 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/webby/apps/generator_spec.rb +116 -0
- data/spec/webby/apps/main_spec.rb +88 -0
- data/spec/webby/filters/basepath_spec.rb +167 -0
- data/spec/webby/filters/outline_spec.rb +92 -0
- data/spec/webby/filters/textile_spec.rb +20 -0
- data/spec/webby/helpers/capture_helper_spec.rb +56 -0
- data/spec/webby/renderer_spec.rb +139 -0
- data/spec/webby/resources/db_spec.rb +250 -0
- data/spec/webby/resources/layout_spec.rb +83 -0
- data/spec/webby/resources/meta_file_spec.rb +171 -0
- data/spec/webby/resources/page_spec.rb +111 -0
- data/spec/webby/resources/partial_spec.rb +58 -0
- data/spec/webby/resources/resource_spec.rb +219 -0
- data/spec/webby/resources/static_spec.rb +49 -0
- data/spec/webby/resources_spec.rb +69 -0
- data/tasks/ann.rake +81 -0
- data/tasks/bones.rake +21 -0
- data/tasks/gem.rake +187 -0
- data/tasks/git.rake +41 -0
- data/tasks/manifest.rake +48 -0
- data/tasks/notes.rake +28 -0
- data/tasks/post_load.rake +39 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +268 -0
- data/tasks/spec.rake +55 -0
- data/tasks/website.rake +38 -0
- metadata +365 -0
@@ -0,0 +1,165 @@
|
|
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, :filename, :directory
|
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
|
+
@meta_data = @resource._meta_data.dup
|
35
|
+
@filename = @resource.filename
|
36
|
+
@directory = @resource.directory
|
37
|
+
unless select
|
38
|
+
raise MissingSelectError, "Must provide block to select data for each page"
|
39
|
+
end
|
40
|
+
@select = select
|
41
|
+
end
|
42
|
+
|
43
|
+
# Total number of pages
|
44
|
+
def number_of_pages
|
45
|
+
(@count / @per_page).to_i + (@count % @per_page > 0 ? 1 : 0)
|
46
|
+
end
|
47
|
+
|
48
|
+
# First page object
|
49
|
+
def first
|
50
|
+
page 1
|
51
|
+
end
|
52
|
+
|
53
|
+
# Last page object
|
54
|
+
def last
|
55
|
+
page number_of_pages
|
56
|
+
end
|
57
|
+
|
58
|
+
def each
|
59
|
+
1.upto(number_of_pages) do |number|
|
60
|
+
yield page(number)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Retrieve page object by number
|
65
|
+
def page(number)
|
66
|
+
number = (n = number.to_i) > 0 ? n : 1
|
67
|
+
Page.new(self, number, lambda {
|
68
|
+
offset = (number - 1) * @per_page
|
69
|
+
args = [offset]
|
70
|
+
args << @per_page if @select.arity == 2
|
71
|
+
@select.call(*args)
|
72
|
+
})
|
73
|
+
end
|
74
|
+
|
75
|
+
# Finalizer method that should be called when the paginator is finished
|
76
|
+
def reset
|
77
|
+
resource._reset(@meta_data)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Page object
|
81
|
+
#
|
82
|
+
# Retrieves items for a page and provides metadata about the position
|
83
|
+
# of the page in the paginator
|
84
|
+
class Page
|
85
|
+
|
86
|
+
include Enumerable
|
87
|
+
|
88
|
+
attr_reader :number, :pager, :url
|
89
|
+
|
90
|
+
def initialize(pager, number, select) #:nodoc:
|
91
|
+
@pager, @number = pager, number
|
92
|
+
@offset = (number - 1) * pager.per_page
|
93
|
+
@select = select
|
94
|
+
|
95
|
+
@pager.reset
|
96
|
+
if number > 1
|
97
|
+
if ::Webby.site.create_mode == 'directory'
|
98
|
+
@pager.resource['directory'] = File.join(@pager.directory, number.to_s)
|
99
|
+
else
|
100
|
+
@pager.resource['filename'] = @pager.filename + number.to_s
|
101
|
+
end
|
102
|
+
end
|
103
|
+
@url = @pager.resource.url
|
104
|
+
end
|
105
|
+
|
106
|
+
# Retrieve the items for this page
|
107
|
+
# * Caches
|
108
|
+
def items
|
109
|
+
@items ||= @select.call
|
110
|
+
end
|
111
|
+
|
112
|
+
# Checks to see if there's a page before this one
|
113
|
+
def prev?
|
114
|
+
@number > 1
|
115
|
+
end
|
116
|
+
|
117
|
+
# Get previous page (if possible)
|
118
|
+
def prev
|
119
|
+
@pager.page(@number - 1) if prev?
|
120
|
+
end
|
121
|
+
|
122
|
+
# Checks to see if there's a page after this one
|
123
|
+
def next?
|
124
|
+
@number < @pager.number_of_pages
|
125
|
+
end
|
126
|
+
|
127
|
+
# Get next page (if possible)
|
128
|
+
def next
|
129
|
+
@pager.page(@number + 1) if next?
|
130
|
+
end
|
131
|
+
|
132
|
+
# The "item number" of the first item on this page
|
133
|
+
def first_item_number
|
134
|
+
1 + @offset
|
135
|
+
end
|
136
|
+
|
137
|
+
# The "item number" of the last item on this page
|
138
|
+
def last_item_number
|
139
|
+
if next?
|
140
|
+
@offset + @pager.per_page
|
141
|
+
else
|
142
|
+
@pager.count
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def ==(other) #:nodoc:
|
147
|
+
@pager == other.pager && self.number == other.number
|
148
|
+
end
|
149
|
+
|
150
|
+
def each(&block)
|
151
|
+
items.each(&block)
|
152
|
+
end
|
153
|
+
|
154
|
+
def method_missing(meth, *args, &block) #:nodoc:
|
155
|
+
if @pager.respond_to?(meth)
|
156
|
+
@pager.__send__(meth, *args, &block)
|
157
|
+
else
|
158
|
+
super
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
end # class Paginator
|
165
|
+
end # module Webby
|
@@ -0,0 +1,339 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
require 'thread'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
# :stopdoc:
|
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
|
+
# :startdoc:
|
338
|
+
|
339
|
+
# EOF
|