webby 0.9.3-x86-mswin32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (229) hide show
  1. data/History.txt +206 -0
  2. data/Manifest.txt +228 -0
  3. data/README.txt +92 -0
  4. data/Rakefile +49 -0
  5. data/bin/webby +41 -0
  6. data/bin/webby-gen +41 -0
  7. data/examples/blog/Sitefile +7 -0
  8. data/examples/blog/tasks/blog.rake +72 -0
  9. data/examples/blog/templates/atom_feed.erb +40 -0
  10. data/examples/blog/templates/blog/month.erb +22 -0
  11. data/examples/blog/templates/blog/post.erb +16 -0
  12. data/examples/blog/templates/blog/year.erb +22 -0
  13. data/examples/presentation/Sitefile +10 -0
  14. data/examples/presentation/content/css/uv/twilight.css +137 -0
  15. data/examples/presentation/content/presentation/_sample_code.txt +10 -0
  16. data/examples/presentation/content/presentation/index.txt +63 -0
  17. data/examples/presentation/content/presentation/s5/blank.gif +0 -0
  18. data/examples/presentation/content/presentation/s5/bodybg.gif +0 -0
  19. data/examples/presentation/content/presentation/s5/framing.css +23 -0
  20. data/examples/presentation/content/presentation/s5/iepngfix.htc +42 -0
  21. data/examples/presentation/content/presentation/s5/opera.css +7 -0
  22. data/examples/presentation/content/presentation/s5/outline.css +15 -0
  23. data/examples/presentation/content/presentation/s5/pretty.css +86 -0
  24. data/examples/presentation/content/presentation/s5/print.css +25 -0
  25. data/examples/presentation/content/presentation/s5/s5-core.css +9 -0
  26. data/examples/presentation/content/presentation/s5/slides.css +3 -0
  27. data/examples/presentation/content/presentation/s5/slides.js +553 -0
  28. data/examples/presentation/layouts/presentation.txt +43 -0
  29. data/examples/presentation/templates/_code_partial.erb +13 -0
  30. data/examples/presentation/templates/presentation.erb +40 -0
  31. data/examples/tumblog/Sitefile +9 -0
  32. data/examples/tumblog/content/css/tumblog.css +308 -0
  33. data/examples/tumblog/content/images/tumblog/permalink.gif +0 -0
  34. data/examples/tumblog/content/images/tumblog/rss.gif +0 -0
  35. data/examples/tumblog/content/tumblog/200806/the-noble-chicken/index.txt +12 -0
  36. data/examples/tumblog/content/tumblog/200807/historical-perspectives-on-the-classic-chicken-joke/index.txt +12 -0
  37. data/examples/tumblog/content/tumblog/200807/mad-city-chickens/index.txt +10 -0
  38. data/examples/tumblog/content/tumblog/200807/the-wisdom-of-the-dutch/index.txt +11 -0
  39. data/examples/tumblog/content/tumblog/200807/up-a-tree/index.txt +13 -0
  40. data/examples/tumblog/content/tumblog/index.txt +37 -0
  41. data/examples/tumblog/content/tumblog/rss.txt +37 -0
  42. data/examples/tumblog/layouts/tumblog/default.txt +44 -0
  43. data/examples/tumblog/layouts/tumblog/post.txt +15 -0
  44. data/examples/tumblog/lib/tumblog_helper.rb +32 -0
  45. data/examples/tumblog/tasks/tumblog.rake +30 -0
  46. data/examples/tumblog/templates/atom_feed.erb +40 -0
  47. data/examples/tumblog/templates/tumblog/conversation.erb +12 -0
  48. data/examples/tumblog/templates/tumblog/link.erb +10 -0
  49. data/examples/tumblog/templates/tumblog/photo.erb +13 -0
  50. data/examples/tumblog/templates/tumblog/post.erb +12 -0
  51. data/examples/tumblog/templates/tumblog/quote.erb +11 -0
  52. data/examples/webby/Sitefile +19 -0
  53. data/examples/webby/content/communicate/index.txt +28 -0
  54. data/examples/webby/content/css/background.gif +0 -0
  55. data/examples/webby/content/css/blueprint/print.css +76 -0
  56. data/examples/webby/content/css/blueprint/screen.css +696 -0
  57. data/examples/webby/content/css/coderay.css +96 -0
  58. data/examples/webby/content/css/site.css +196 -0
  59. data/examples/webby/content/css/uv/twilight.css +137 -0
  60. data/examples/webby/content/index.txt +37 -0
  61. data/examples/webby/content/learn/index.txt +28 -0
  62. data/examples/webby/content/reference/index.txt +204 -0
  63. data/examples/webby/content/release-notes/index.txt +21 -0
  64. data/examples/webby/content/release-notes/rel-0-9-0/index.txt +74 -0
  65. data/examples/webby/content/release-notes/rel-0-9-1/index.txt +93 -0
  66. data/examples/webby/content/release-notes/rel-0-9-2/index.txt +14 -0
  67. data/examples/webby/content/release-notes/rel-0-9-3/index.txt +49 -0
  68. data/examples/webby/content/robots.txt +6 -0
  69. data/examples/webby/content/script/jquery.corner.js +152 -0
  70. data/examples/webby/content/script/jquery.js +31 -0
  71. data/examples/webby/content/sitemap.txt +31 -0
  72. data/examples/webby/content/tips_and_tricks/index.txt +97 -0
  73. data/examples/webby/content/tutorial/index.txt +135 -0
  74. data/examples/webby/content/user-manual/index.txt +419 -0
  75. data/examples/webby/layouts/default.txt +49 -0
  76. data/examples/webby/templates/page.erb +10 -0
  77. data/examples/website/Sitefile +7 -0
  78. data/examples/website/content/css/blueprint/License.txt +21 -0
  79. data/examples/website/content/css/blueprint/Readme.txt +100 -0
  80. data/examples/website/content/css/blueprint/compressed/print.css +76 -0
  81. data/examples/website/content/css/blueprint/compressed/screen.css +696 -0
  82. data/examples/website/content/css/blueprint/lib/forms.css +45 -0
  83. data/examples/website/content/css/blueprint/lib/grid.css +193 -0
  84. data/examples/website/content/css/blueprint/lib/grid.png +0 -0
  85. data/examples/website/content/css/blueprint/lib/ie.css +30 -0
  86. data/examples/website/content/css/blueprint/lib/reset.css +39 -0
  87. data/examples/website/content/css/blueprint/lib/typography.css +116 -0
  88. data/examples/website/content/css/blueprint/plugins/buttons/Readme +31 -0
  89. data/examples/website/content/css/blueprint/plugins/buttons/buttons.css +97 -0
  90. data/examples/website/content/css/blueprint/plugins/buttons/icons/cross.png +0 -0
  91. data/examples/website/content/css/blueprint/plugins/buttons/icons/key.png +0 -0
  92. data/examples/website/content/css/blueprint/plugins/buttons/icons/tick.png +0 -0
  93. data/examples/website/content/css/blueprint/plugins/css-classes/Readme +14 -0
  94. data/examples/website/content/css/blueprint/plugins/css-classes/css-classes.css +24 -0
  95. data/examples/website/content/css/blueprint/plugins/fancy-type/Readme +22 -0
  96. data/examples/website/content/css/blueprint/plugins/fancy-type/fancy-type-compressed.css +5 -0
  97. data/examples/website/content/css/blueprint/plugins/fancy-type/fancy-type.css +74 -0
  98. data/examples/website/content/css/blueprint/print.css +68 -0
  99. data/examples/website/content/css/blueprint/screen.css +22 -0
  100. data/examples/website/content/css/coderay.css +111 -0
  101. data/examples/website/content/css/site.css +67 -0
  102. data/examples/website/content/index.txt +19 -0
  103. data/examples/website/layouts/default.txt +58 -0
  104. data/examples/website/lib/breadcrumbs.rb +28 -0
  105. data/examples/website/templates/_partial.erb +10 -0
  106. data/examples/website/templates/page.erb +18 -0
  107. data/lib/webby.rb +221 -0
  108. data/lib/webby/apps.rb +12 -0
  109. data/lib/webby/apps/generator.rb +276 -0
  110. data/lib/webby/apps/main.rb +255 -0
  111. data/lib/webby/auto_builder.rb +157 -0
  112. data/lib/webby/builder.rb +180 -0
  113. data/lib/webby/core_ext/enumerable.rb +11 -0
  114. data/lib/webby/core_ext/hash.rb +28 -0
  115. data/lib/webby/core_ext/kernel.rb +26 -0
  116. data/lib/webby/core_ext/string.rb +163 -0
  117. data/lib/webby/core_ext/time.rb +9 -0
  118. data/lib/webby/filters.rb +83 -0
  119. data/lib/webby/filters/basepath.rb +97 -0
  120. data/lib/webby/filters/erb.rb +9 -0
  121. data/lib/webby/filters/haml.rb +18 -0
  122. data/lib/webby/filters/markdown.rb +16 -0
  123. data/lib/webby/filters/outline.rb +309 -0
  124. data/lib/webby/filters/sass.rb +17 -0
  125. data/lib/webby/filters/slides.rb +56 -0
  126. data/lib/webby/filters/textile.rb +16 -0
  127. data/lib/webby/filters/tidy.rb +76 -0
  128. data/lib/webby/filters/wiki_words.rb +14 -0
  129. data/lib/webby/helpers.rb +30 -0
  130. data/lib/webby/helpers/capture_helper.rb +141 -0
  131. data/lib/webby/helpers/coderay_helper.rb +69 -0
  132. data/lib/webby/helpers/graphviz_helper.rb +136 -0
  133. data/lib/webby/helpers/tag_helper.rb +65 -0
  134. data/lib/webby/helpers/tex_img_helper.rb +133 -0
  135. data/lib/webby/helpers/ultraviolet_helper.rb +63 -0
  136. data/lib/webby/helpers/url_helper.rb +241 -0
  137. data/lib/webby/journal.rb +126 -0
  138. data/lib/webby/link_validator.rb +152 -0
  139. data/lib/webby/renderer.rb +386 -0
  140. data/lib/webby/resources.rb +136 -0
  141. data/lib/webby/resources/db.rb +251 -0
  142. data/lib/webby/resources/layout.rb +54 -0
  143. data/lib/webby/resources/meta_file.rb +211 -0
  144. data/lib/webby/resources/page.rb +81 -0
  145. data/lib/webby/resources/partial.rb +85 -0
  146. data/lib/webby/resources/resource.rb +201 -0
  147. data/lib/webby/resources/static.rb +36 -0
  148. data/lib/webby/stelan/mktemp.rb +135 -0
  149. data/lib/webby/stelan/paginator.rb +165 -0
  150. data/lib/webby/stelan/spawner.rb +339 -0
  151. data/lib/webby/tasks/build.rake +27 -0
  152. data/lib/webby/tasks/create.rake +22 -0
  153. data/lib/webby/tasks/deploy.rake +22 -0
  154. data/lib/webby/tasks/growl.rake +16 -0
  155. data/lib/webby/tasks/validate.rake +19 -0
  156. data/spec/core_ext/hash_spec.rb +47 -0
  157. data/spec/core_ext/string_spec.rb +110 -0
  158. data/spec/core_ext/time_spec.rb +19 -0
  159. data/spec/data/hooligans/bad_meta_data_1.txt +34 -0
  160. data/spec/data/hooligans/bad_meta_data_2.txt +34 -0
  161. data/spec/data/outline/basic.out +81 -0
  162. data/spec/data/outline/basic.txt +25 -0
  163. data/spec/data/outline/no_clobber.out +86 -0
  164. data/spec/data/outline/numbering.out +81 -0
  165. data/spec/data/outline/numbering_only.out +21 -0
  166. data/spec/data/outline/toc_range_1.out +66 -0
  167. data/spec/data/outline/toc_range_2.out +55 -0
  168. data/spec/data/outline/toc_style.out +81 -0
  169. data/spec/data/site/Sitefile +9 -0
  170. data/spec/data/site/content/_partial.txt +10 -0
  171. data/spec/data/site/content/css/coderay.css +111 -0
  172. data/spec/data/site/content/css/site.css +67 -0
  173. data/spec/data/site/content/css/tumblog.css +308 -0
  174. data/spec/data/site/content/images/tumblog/permalink.gif +0 -0
  175. data/spec/data/site/content/images/tumblog/rss.gif +0 -0
  176. data/spec/data/site/content/index.txt +19 -0
  177. data/spec/data/site/content/photos.txt +21 -0
  178. data/spec/data/site/content/tumblog/200806/the-noble-chicken/index.txt +12 -0
  179. data/spec/data/site/content/tumblog/200807/historical-perspectives-on-the-classic-chicken-joke/index.txt +12 -0
  180. data/spec/data/site/content/tumblog/200807/mad-city-chickens/index.txt +10 -0
  181. data/spec/data/site/content/tumblog/200807/the-wisdom-of-the-dutch/index.txt +11 -0
  182. data/spec/data/site/content/tumblog/200807/up-a-tree/index.txt +13 -0
  183. data/spec/data/site/content/tumblog/index.txt +37 -0
  184. data/spec/data/site/content/tumblog/rss.txt +37 -0
  185. data/spec/data/site/layouts/default.txt +58 -0
  186. data/spec/data/site/layouts/tumblog/default.txt +44 -0
  187. data/spec/data/site/layouts/tumblog/post.txt +15 -0
  188. data/spec/data/site/lib/breadcrumbs.rb +28 -0
  189. data/spec/data/site/lib/tumblog_helper.rb +32 -0
  190. data/spec/data/site/tasks/tumblog.rake +30 -0
  191. data/spec/data/site/templates/_partial.erb +10 -0
  192. data/spec/data/site/templates/atom_feed.erb +40 -0
  193. data/spec/data/site/templates/page.erb +18 -0
  194. data/spec/data/site/templates/presentation.erb +40 -0
  195. data/spec/data/site/templates/tumblog/conversation.erb +12 -0
  196. data/spec/data/site/templates/tumblog/link.erb +10 -0
  197. data/spec/data/site/templates/tumblog/photo.erb +13 -0
  198. data/spec/data/site/templates/tumblog/post.erb +12 -0
  199. data/spec/data/site/templates/tumblog/quote.erb +11 -0
  200. data/spec/spec.opts +1 -0
  201. data/spec/spec_helper.rb +51 -0
  202. data/spec/webby/apps/generator_spec.rb +116 -0
  203. data/spec/webby/apps/main_spec.rb +88 -0
  204. data/spec/webby/filters/basepath_spec.rb +167 -0
  205. data/spec/webby/filters/outline_spec.rb +92 -0
  206. data/spec/webby/filters/textile_spec.rb +20 -0
  207. data/spec/webby/helpers/capture_helper_spec.rb +56 -0
  208. data/spec/webby/renderer_spec.rb +139 -0
  209. data/spec/webby/resources/db_spec.rb +250 -0
  210. data/spec/webby/resources/layout_spec.rb +83 -0
  211. data/spec/webby/resources/meta_file_spec.rb +171 -0
  212. data/spec/webby/resources/page_spec.rb +111 -0
  213. data/spec/webby/resources/partial_spec.rb +58 -0
  214. data/spec/webby/resources/resource_spec.rb +219 -0
  215. data/spec/webby/resources/static_spec.rb +49 -0
  216. data/spec/webby/resources_spec.rb +69 -0
  217. data/tasks/ann.rake +81 -0
  218. data/tasks/bones.rake +21 -0
  219. data/tasks/gem.rake +187 -0
  220. data/tasks/git.rake +41 -0
  221. data/tasks/manifest.rake +48 -0
  222. data/tasks/notes.rake +28 -0
  223. data/tasks/post_load.rake +39 -0
  224. data/tasks/rdoc.rake +51 -0
  225. data/tasks/rubyforge.rake +55 -0
  226. data/tasks/setup.rb +268 -0
  227. data/tasks/spec.rake +55 -0
  228. data/tasks/website.rake +38 -0
  229. 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