libgems 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 (177) hide show
  1. data/ChangeLog +5811 -0
  2. data/History.txt +887 -0
  3. data/LICENSE.txt +51 -0
  4. data/README.md +87 -0
  5. data/Rakefile +113 -0
  6. data/lib/gauntlet_libgems.rb +50 -0
  7. data/lib/libgems.rb +1246 -0
  8. data/lib/libgems/builder.rb +102 -0
  9. data/lib/libgems/command.rb +534 -0
  10. data/lib/libgems/command_manager.rb +182 -0
  11. data/lib/libgems/commands/build_command.rb +53 -0
  12. data/lib/libgems/commands/cert_command.rb +86 -0
  13. data/lib/libgems/commands/check_command.rb +80 -0
  14. data/lib/libgems/commands/cleanup_command.rb +106 -0
  15. data/lib/libgems/commands/contents_command.rb +98 -0
  16. data/lib/libgems/commands/dependency_command.rb +195 -0
  17. data/lib/libgems/commands/environment_command.rb +133 -0
  18. data/lib/libgems/commands/fetch_command.rb +67 -0
  19. data/lib/libgems/commands/generate_index_command.rb +133 -0
  20. data/lib/libgems/commands/help_command.rb +172 -0
  21. data/lib/libgems/commands/install_command.rb +178 -0
  22. data/lib/libgems/commands/list_command.rb +35 -0
  23. data/lib/libgems/commands/lock_command.rb +110 -0
  24. data/lib/libgems/commands/mirror_command.rb +111 -0
  25. data/lib/libgems/commands/outdated_command.rb +33 -0
  26. data/lib/libgems/commands/owner_command.rb +75 -0
  27. data/lib/libgems/commands/pristine_command.rb +93 -0
  28. data/lib/libgems/commands/push_command.rb +56 -0
  29. data/lib/libgems/commands/query_command.rb +280 -0
  30. data/lib/libgems/commands/rdoc_command.rb +91 -0
  31. data/lib/libgems/commands/search_command.rb +31 -0
  32. data/lib/libgems/commands/server_command.rb +86 -0
  33. data/lib/libgems/commands/sources_command.rb +157 -0
  34. data/lib/libgems/commands/specification_command.rb +125 -0
  35. data/lib/libgems/commands/stale_command.rb +27 -0
  36. data/lib/libgems/commands/uninstall_command.rb +83 -0
  37. data/lib/libgems/commands/unpack_command.rb +121 -0
  38. data/lib/libgems/commands/update_command.rb +160 -0
  39. data/lib/libgems/commands/which_command.rb +86 -0
  40. data/lib/libgems/config_file.rb +345 -0
  41. data/lib/libgems/custom_require.rb +44 -0
  42. data/lib/libgems/defaults.rb +101 -0
  43. data/lib/libgems/dependency.rb +227 -0
  44. data/lib/libgems/dependency_installer.rb +286 -0
  45. data/lib/libgems/dependency_list.rb +208 -0
  46. data/lib/libgems/doc_manager.rb +242 -0
  47. data/lib/libgems/errors.rb +35 -0
  48. data/lib/libgems/exceptions.rb +91 -0
  49. data/lib/libgems/ext.rb +18 -0
  50. data/lib/libgems/ext/builder.rb +56 -0
  51. data/lib/libgems/ext/configure_builder.rb +25 -0
  52. data/lib/libgems/ext/ext_conf_builder.rb +24 -0
  53. data/lib/libgems/ext/rake_builder.rb +39 -0
  54. data/lib/libgems/format.rb +81 -0
  55. data/lib/libgems/gem_openssl.rb +92 -0
  56. data/lib/libgems/gem_path_searcher.rb +100 -0
  57. data/lib/libgems/gem_runner.rb +79 -0
  58. data/lib/libgems/gemcutter_utilities.rb +49 -0
  59. data/lib/libgems/indexer.rb +720 -0
  60. data/lib/libgems/install_update_options.rb +125 -0
  61. data/lib/libgems/installer.rb +604 -0
  62. data/lib/libgems/local_remote_options.rb +135 -0
  63. data/lib/libgems/old_format.rb +153 -0
  64. data/lib/libgems/package.rb +97 -0
  65. data/lib/libgems/package/f_sync_dir.rb +23 -0
  66. data/lib/libgems/package/tar_header.rb +266 -0
  67. data/lib/libgems/package/tar_input.rb +222 -0
  68. data/lib/libgems/package/tar_output.rb +144 -0
  69. data/lib/libgems/package/tar_reader.rb +106 -0
  70. data/lib/libgems/package/tar_reader/entry.rb +141 -0
  71. data/lib/libgems/package/tar_writer.rb +241 -0
  72. data/lib/libgems/package_task.rb +126 -0
  73. data/lib/libgems/platform.rb +183 -0
  74. data/lib/libgems/remote_fetcher.rb +414 -0
  75. data/lib/libgems/require_paths_builder.rb +18 -0
  76. data/lib/libgems/requirement.rb +153 -0
  77. data/lib/libgems/security.rb +814 -0
  78. data/lib/libgems/server.rb +872 -0
  79. data/lib/libgems/source_index.rb +597 -0
  80. data/lib/libgems/source_info_cache.rb +395 -0
  81. data/lib/libgems/source_info_cache_entry.rb +56 -0
  82. data/lib/libgems/spec_fetcher.rb +337 -0
  83. data/lib/libgems/specification.rb +1487 -0
  84. data/lib/libgems/test_utilities.rb +147 -0
  85. data/lib/libgems/text.rb +65 -0
  86. data/lib/libgems/uninstaller.rb +278 -0
  87. data/lib/libgems/user_interaction.rb +527 -0
  88. data/lib/libgems/validator.rb +240 -0
  89. data/lib/libgems/version.rb +316 -0
  90. data/lib/libgems/version_option.rb +65 -0
  91. data/lib/rbconfig/datadir.rb +20 -0
  92. data/test/bogussources.rb +8 -0
  93. data/test/data/gem-private_key.pem +27 -0
  94. data/test/data/gem-public_cert.pem +20 -0
  95. data/test/fake_certlib/openssl.rb +7 -0
  96. data/test/foo/discover.rb +0 -0
  97. data/test/gem_installer_test_case.rb +97 -0
  98. data/test/gem_package_tar_test_case.rb +132 -0
  99. data/test/gemutilities.rb +605 -0
  100. data/test/insure_session.rb +43 -0
  101. data/test/mockgemui.rb +56 -0
  102. data/test/plugin/exception/libgems_plugin.rb +2 -0
  103. data/test/plugin/load/libgems_plugin.rb +1 -0
  104. data/test/plugin/standarderror/libgems_plugin.rb +2 -0
  105. data/test/private_key.pem +27 -0
  106. data/test/public_cert.pem +20 -0
  107. data/test/rubygems_plugin.rb +21 -0
  108. data/test/simple_gem.rb +66 -0
  109. data/test/test_config.rb +12 -0
  110. data/test/test_gem.rb +780 -0
  111. data/test/test_gem_builder.rb +27 -0
  112. data/test/test_gem_command.rb +178 -0
  113. data/test/test_gem_command_manager.rb +207 -0
  114. data/test/test_gem_commands_build_command.rb +74 -0
  115. data/test/test_gem_commands_cert_command.rb +124 -0
  116. data/test/test_gem_commands_check_command.rb +18 -0
  117. data/test/test_gem_commands_contents_command.rb +156 -0
  118. data/test/test_gem_commands_dependency_command.rb +216 -0
  119. data/test/test_gem_commands_environment_command.rb +144 -0
  120. data/test/test_gem_commands_fetch_command.rb +76 -0
  121. data/test/test_gem_commands_generate_index_command.rb +135 -0
  122. data/test/test_gem_commands_install_command.rb +315 -0
  123. data/test/test_gem_commands_list_command.rb +36 -0
  124. data/test/test_gem_commands_lock_command.rb +68 -0
  125. data/test/test_gem_commands_mirror_command.rb +60 -0
  126. data/test/test_gem_commands_outdated_command.rb +40 -0
  127. data/test/test_gem_commands_owner_command.rb +105 -0
  128. data/test/test_gem_commands_pristine_command.rb +108 -0
  129. data/test/test_gem_commands_push_command.rb +81 -0
  130. data/test/test_gem_commands_query_command.rb +426 -0
  131. data/test/test_gem_commands_server_command.rb +59 -0
  132. data/test/test_gem_commands_sources_command.rb +209 -0
  133. data/test/test_gem_commands_specification_command.rb +139 -0
  134. data/test/test_gem_commands_stale_command.rb +38 -0
  135. data/test/test_gem_commands_uninstall_command.rb +83 -0
  136. data/test/test_gem_commands_unpack_command.rb +199 -0
  137. data/test/test_gem_commands_update_command.rb +207 -0
  138. data/test/test_gem_commands_which_command.rb +66 -0
  139. data/test/test_gem_config_file.rb +287 -0
  140. data/test/test_gem_dependency.rb +149 -0
  141. data/test/test_gem_dependency_installer.rb +661 -0
  142. data/test/test_gem_dependency_list.rb +230 -0
  143. data/test/test_gem_doc_manager.rb +31 -0
  144. data/test/test_gem_ext_configure_builder.rb +84 -0
  145. data/test/test_gem_ext_ext_conf_builder.rb +173 -0
  146. data/test/test_gem_ext_rake_builder.rb +81 -0
  147. data/test/test_gem_format.rb +70 -0
  148. data/test/test_gem_gem_path_searcher.rb +78 -0
  149. data/test/test_gem_gem_runner.rb +45 -0
  150. data/test/test_gem_gemcutter_utilities.rb +103 -0
  151. data/test/test_gem_indexer.rb +673 -0
  152. data/test/test_gem_install_update_options.rb +68 -0
  153. data/test/test_gem_installer.rb +857 -0
  154. data/test/test_gem_local_remote_options.rb +97 -0
  155. data/test/test_gem_package_tar_header.rb +130 -0
  156. data/test/test_gem_package_tar_input.rb +112 -0
  157. data/test/test_gem_package_tar_output.rb +97 -0
  158. data/test/test_gem_package_tar_reader.rb +46 -0
  159. data/test/test_gem_package_tar_reader_entry.rb +109 -0
  160. data/test/test_gem_package_tar_writer.rb +144 -0
  161. data/test/test_gem_package_task.rb +59 -0
  162. data/test/test_gem_platform.rb +264 -0
  163. data/test/test_gem_remote_fetcher.rb +740 -0
  164. data/test/test_gem_requirement.rb +292 -0
  165. data/test/test_gem_server.rb +356 -0
  166. data/test/test_gem_silent_ui.rb +113 -0
  167. data/test/test_gem_source_index.rb +461 -0
  168. data/test/test_gem_spec_fetcher.rb +410 -0
  169. data/test/test_gem_specification.rb +1334 -0
  170. data/test/test_gem_stream_ui.rb +218 -0
  171. data/test/test_gem_text.rb +43 -0
  172. data/test/test_gem_uninstaller.rb +146 -0
  173. data/test/test_gem_validator.rb +63 -0
  174. data/test/test_gem_version.rb +181 -0
  175. data/test/test_gem_version_option.rb +89 -0
  176. data/test/test_kernel.rb +59 -0
  177. metadata +402 -0
@@ -0,0 +1,872 @@
1
+ require 'webrick'
2
+ require 'yaml'
3
+ require 'zlib'
4
+ require 'erb'
5
+
6
+ require 'libgems'
7
+ require 'libgems/doc_manager'
8
+
9
+ ##
10
+ # LibGems::Server and allows users to serve gems for consumption by
11
+ # `gem --remote-install`.
12
+ #
13
+ # gem_server starts an HTTP server on the given port and serves the following:
14
+ # * "/" - Browsing of gem spec files for installed gems
15
+ # * "/specs.#{LibGems.marshal_version}.gz" - specs name/version/platform index
16
+ # * "/latest_specs.#{LibGems.marshal_version}.gz" - latest specs
17
+ # name/version/platform index
18
+ # * "/quick/" - Individual gemspecs
19
+ # * "/gems" - Direct access to download the installable gems
20
+ # * "/rdoc?q=" - Search for installed rdoc documentation
21
+ # * legacy indexes:
22
+ # * "/Marshal.#{LibGems.marshal_version}" - Full SourceIndex dump of metadata
23
+ # for installed gems
24
+ # * "/yaml" - YAML dump of metadata for installed gems - deprecated
25
+ #
26
+ # == Usage
27
+ #
28
+ # gem_server = LibGems::Server.new LibGems.dir, 8089, false
29
+ # gem_server.run
30
+ #
31
+ #--
32
+ # TODO Refactor into a real WEBrick servlet to remove code duplication.
33
+
34
+ class LibGems::Server
35
+
36
+ attr_reader :spec_dirs
37
+
38
+ include ERB::Util
39
+ include LibGems::UserInteraction
40
+
41
+ SEARCH = <<-SEARCH
42
+ <form class="headerSearch" name="headerSearchForm" method="get" action="/rdoc">
43
+ <div id="search" style="float:right">
44
+ <label for="q">Filter/Search</label>
45
+ <input id="q" type="text" style="width:10em" name="q">
46
+ <button type="submit" style="display:none"></button>
47
+ </div>
48
+ </form>
49
+ SEARCH
50
+
51
+ DOC_TEMPLATE = <<-'DOC_TEMPLATE'
52
+ <?xml version="1.0" encoding="iso-8859-1"?>
53
+ <!DOCTYPE html
54
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
55
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
56
+
57
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
58
+ <head>
59
+ <title>#{LibGems::NAME} Documentation Index</title>
60
+ <link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
61
+ </head>
62
+ <body>
63
+ <div id="fileHeader">
64
+ <%= SEARCH %>
65
+ <h1>#{LibGems::NAME} Documentation Index</h1>
66
+ </div>
67
+ <!-- banner header -->
68
+
69
+ <div id="bodyContent">
70
+ <div id="contextContent">
71
+ <div id="description">
72
+ <h1>Summary</h1>
73
+ <p>There are <%=values["gem_count"]%> gems installed:</p>
74
+ <p>
75
+ <%= values["specs"].map { |v| "<a href=\"##{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>.
76
+ <h1>Gems</h1>
77
+
78
+ <dl>
79
+ <% values["specs"].each do |spec| %>
80
+ <dt>
81
+ <% if spec["first_name_entry"] then %>
82
+ <a name="<%=spec["name"]%>"></a>
83
+ <% end %>
84
+
85
+ <b><%=spec["name"]%> <%=spec["version"]%></b>
86
+
87
+ <% if spec["rdoc_installed"] then %>
88
+ <a href="<%=spec["doc_path"]%>">[rdoc]</a>
89
+ <% else %>
90
+ <span title="rdoc not installed">[rdoc]</span>
91
+ <% end %>
92
+
93
+ <% if spec["homepage"] then %>
94
+ <a href="<%=spec["homepage"]%>" title="<%=spec["homepage"]%>">[www]</a>
95
+ <% else %>
96
+ <span title="no homepage available">[www]</span>
97
+ <% end %>
98
+
99
+ <% if spec["has_deps"] then %>
100
+ - depends on
101
+ <%= spec["dependencies"].map { |v| "<a href=\"##{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>.
102
+ <% end %>
103
+ </dt>
104
+ <dd>
105
+ <%=spec["summary"]%>
106
+ <% if spec["executables"] then %>
107
+ <br/>
108
+
109
+ <% if spec["only_one_executable"] then %>
110
+ Executable is
111
+ <% else %>
112
+ Executables are
113
+ <%end%>
114
+
115
+ <%= spec["executables"].map { |v| "<span class=\"context-item-name\">#{v["executable"]}</span>"}.join ', ' %>.
116
+
117
+ <%end%>
118
+ <br/>
119
+ <br/>
120
+ </dd>
121
+ <% end %>
122
+ </dl>
123
+
124
+ </div>
125
+ </div>
126
+ </div>
127
+ <div id="validator-badges">
128
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
129
+ </div>
130
+ </body>
131
+ </html>
132
+ DOC_TEMPLATE
133
+
134
+ # CSS is copy & paste from rdoc-style.css, RDoc V1.0.1 - 20041108
135
+ RDOC_CSS = <<-RDOC_CSS
136
+ body {
137
+ font-family: Verdana,Arial,Helvetica,sans-serif;
138
+ font-size: 90%;
139
+ margin: 0;
140
+ margin-left: 40px;
141
+ padding: 0;
142
+ background: white;
143
+ }
144
+
145
+ h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
146
+ h1 { font-size: 150%; }
147
+ h2,h3,h4 { margin-top: 1em; }
148
+
149
+ a { background: #eef; color: #039; text-decoration: none; }
150
+ a:hover { background: #039; color: #eef; }
151
+
152
+ /* Override the base stylesheets Anchor inside a table cell */
153
+ td > a {
154
+ background: transparent;
155
+ color: #039;
156
+ text-decoration: none;
157
+ }
158
+
159
+ /* and inside a section title */
160
+ .section-title > a {
161
+ background: transparent;
162
+ color: #eee;
163
+ text-decoration: none;
164
+ }
165
+
166
+ /* === Structural elements =================================== */
167
+
168
+ div#index {
169
+ margin: 0;
170
+ margin-left: -40px;
171
+ padding: 0;
172
+ font-size: 90%;
173
+ }
174
+
175
+
176
+ div#index a {
177
+ margin-left: 0.7em;
178
+ }
179
+
180
+ div#index .section-bar {
181
+ margin-left: 0px;
182
+ padding-left: 0.7em;
183
+ background: #ccc;
184
+ font-size: small;
185
+ }
186
+
187
+
188
+ div#classHeader, div#fileHeader {
189
+ width: auto;
190
+ color: white;
191
+ padding: 0.5em 1.5em 0.5em 1.5em;
192
+ margin: 0;
193
+ margin-left: -40px;
194
+ border-bottom: 3px solid #006;
195
+ }
196
+
197
+ div#classHeader a, div#fileHeader a {
198
+ background: inherit;
199
+ color: white;
200
+ }
201
+
202
+ div#classHeader td, div#fileHeader td {
203
+ background: inherit;
204
+ color: white;
205
+ }
206
+
207
+
208
+ div#fileHeader {
209
+ background: #057;
210
+ }
211
+
212
+ div#classHeader {
213
+ background: #048;
214
+ }
215
+
216
+
217
+ .class-name-in-header {
218
+ font-size: 180%;
219
+ font-weight: bold;
220
+ }
221
+
222
+
223
+ div#bodyContent {
224
+ padding: 0 1.5em 0 1.5em;
225
+ }
226
+
227
+ div#description {
228
+ padding: 0.5em 1.5em;
229
+ background: #efefef;
230
+ border: 1px dotted #999;
231
+ }
232
+
233
+ div#description h1,h2,h3,h4,h5,h6 {
234
+ color: #125;;
235
+ background: transparent;
236
+ }
237
+
238
+ div#validator-badges {
239
+ text-align: center;
240
+ }
241
+ div#validator-badges img { border: 0; }
242
+
243
+ div#copyright {
244
+ color: #333;
245
+ background: #efefef;
246
+ font: 0.75em sans-serif;
247
+ margin-top: 5em;
248
+ margin-bottom: 0;
249
+ padding: 0.5em 2em;
250
+ }
251
+
252
+
253
+ /* === Classes =================================== */
254
+
255
+ table.header-table {
256
+ color: white;
257
+ font-size: small;
258
+ }
259
+
260
+ .type-note {
261
+ font-size: small;
262
+ color: #DEDEDE;
263
+ }
264
+
265
+ .xxsection-bar {
266
+ background: #eee;
267
+ color: #333;
268
+ padding: 3px;
269
+ }
270
+
271
+ .section-bar {
272
+ color: #333;
273
+ border-bottom: 1px solid #999;
274
+ margin-left: -20px;
275
+ }
276
+
277
+
278
+ .section-title {
279
+ background: #79a;
280
+ color: #eee;
281
+ padding: 3px;
282
+ margin-top: 2em;
283
+ margin-left: -30px;
284
+ border: 1px solid #999;
285
+ }
286
+
287
+ .top-aligned-row { vertical-align: top }
288
+ .bottom-aligned-row { vertical-align: bottom }
289
+
290
+ /* --- Context section classes ----------------------- */
291
+
292
+ .context-row { }
293
+ .context-item-name { font-family: monospace; font-weight: bold; color: black; }
294
+ .context-item-value { font-size: small; color: #448; }
295
+ .context-item-desc { color: #333; padding-left: 2em; }
296
+
297
+ /* --- Method classes -------------------------- */
298
+ .method-detail {
299
+ background: #efefef;
300
+ padding: 0;
301
+ margin-top: 0.5em;
302
+ margin-bottom: 1em;
303
+ border: 1px dotted #ccc;
304
+ }
305
+ .method-heading {
306
+ color: black;
307
+ background: #ccc;
308
+ border-bottom: 1px solid #666;
309
+ padding: 0.2em 0.5em 0 0.5em;
310
+ }
311
+ .method-signature { color: black; background: inherit; }
312
+ .method-name { font-weight: bold; }
313
+ .method-args { font-style: italic; }
314
+ .method-description { padding: 0 0.5em 0 0.5em; }
315
+
316
+ /* --- Source code sections -------------------- */
317
+
318
+ a.source-toggle { font-size: 90%; }
319
+ div.method-source-code {
320
+ background: #262626;
321
+ color: #ffdead;
322
+ margin: 1em;
323
+ padding: 0.5em;
324
+ border: 1px dashed #999;
325
+ overflow: hidden;
326
+ }
327
+
328
+ div.method-source-code pre { color: #ffdead; overflow: hidden; }
329
+
330
+ /* --- Ruby keyword styles --------------------- */
331
+
332
+ .standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
333
+
334
+ .ruby-constant { color: #7fffd4; background: transparent; }
335
+ .ruby-keyword { color: #00ffff; background: transparent; }
336
+ .ruby-ivar { color: #eedd82; background: transparent; }
337
+ .ruby-operator { color: #00ffee; background: transparent; }
338
+ .ruby-identifier { color: #ffdead; background: transparent; }
339
+ .ruby-node { color: #ffa07a; background: transparent; }
340
+ .ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
341
+ .ruby-regexp { color: #ffa07a; background: transparent; }
342
+ .ruby-value { color: #7fffd4; background: transparent; }
343
+ RDOC_CSS
344
+
345
+ RDOC_NO_DOCUMENTATION = <<-'NO_DOC'
346
+ <?xml version="1.0" encoding="iso-8859-1"?>
347
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
348
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
349
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
350
+ <head>
351
+ <title>Found documentation</title>
352
+ <link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
353
+ </head>
354
+ <body>
355
+ <div id="fileHeader">
356
+ <%= SEARCH %>
357
+ <h1>No documentation found</h1>
358
+ </div>
359
+
360
+ <div id="bodyContent">
361
+ <div id="contextContent">
362
+ <div id="description">
363
+ <p>No gems matched <%= h query.inspect %></p>
364
+
365
+ <p>
366
+ Back to <a href="/">complete gem index</a>
367
+ </p>
368
+
369
+ </div>
370
+ </div>
371
+ </div>
372
+ <div id="validator-badges">
373
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
374
+ </div>
375
+ </body>
376
+ </html>
377
+ NO_DOC
378
+
379
+ RDOC_SEARCH_TEMPLATE = <<-'RDOC_SEARCH'
380
+ <?xml version="1.0" encoding="iso-8859-1"?>
381
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
382
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
383
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
384
+ <head>
385
+ <title>Found documentation</title>
386
+ <link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
387
+ </head>
388
+ <body>
389
+ <div id="fileHeader">
390
+ <%= SEARCH %>
391
+ <h1>Found documentation</h1>
392
+ </div>
393
+ <!-- banner header -->
394
+
395
+ <div id="bodyContent">
396
+ <div id="contextContent">
397
+ <div id="description">
398
+ <h1>Summary</h1>
399
+ <p><%=doc_items.length%> documentation topics found.</p>
400
+ <h1>Topics</h1>
401
+
402
+ <dl>
403
+ <% doc_items.each do |doc_item| %>
404
+ <dt>
405
+ <b><%=doc_item[:name]%></b>
406
+ <a href="<%=doc_item[:url]%>">[rdoc]</a>
407
+ </dt>
408
+ <dd>
409
+ <%=doc_item[:summary]%>
410
+ <br/>
411
+ <br/>
412
+ </dd>
413
+ <% end %>
414
+ </dl>
415
+
416
+ <p>
417
+ Back to <a href="/">complete gem index</a>
418
+ </p>
419
+
420
+ </div>
421
+ </div>
422
+ </div>
423
+ <div id="validator-badges">
424
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
425
+ </div>
426
+ </body>
427
+ </html>
428
+ RDOC_SEARCH
429
+
430
+ def self.run(options)
431
+ new(options[:gemdir], options[:port], options[:daemon],
432
+ options[:launch], options[:addresses]).run
433
+ end
434
+
435
+ ##
436
+ # Only the first directory in gem_dirs is used for serving gems
437
+
438
+ def initialize(gem_dirs, port, daemon, launch = nil, addresses = nil)
439
+ Socket.do_not_reverse_lookup = true
440
+
441
+ @gem_dirs = Array gem_dirs
442
+ @port = port
443
+ @daemon = daemon
444
+ @launch = launch
445
+ @addresses = addresses
446
+ logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL
447
+ @server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger
448
+
449
+ @spec_dirs = @gem_dirs.map do |gem_dir|
450
+ spec_dir = File.join gem_dir, 'specifications'
451
+
452
+ unless File.directory? spec_dir then
453
+ raise ArgumentError, "#{gem_dir} does not appear to be a gem repository"
454
+ end
455
+
456
+ spec_dir
457
+ end
458
+
459
+ @source_index = LibGems::SourceIndex.from_gems_in(*@spec_dirs)
460
+ end
461
+
462
+ def Marshal(req, res)
463
+ @source_index.refresh!
464
+
465
+ add_date res
466
+
467
+ index = Marshal.dump @source_index
468
+
469
+ if req.request_method == 'HEAD' then
470
+ res['content-length'] = index.length
471
+ return
472
+ end
473
+
474
+ if req.path =~ /Z$/ then
475
+ res['content-type'] = 'application/x-deflate'
476
+ index = LibGems.deflate index
477
+ else
478
+ res['content-type'] = 'application/octet-stream'
479
+ end
480
+
481
+ res.body << index
482
+ end
483
+
484
+ def add_date res
485
+ res['date'] = @spec_dirs.map do |spec_dir|
486
+ File.stat(spec_dir).mtime
487
+ end.max
488
+ end
489
+
490
+ def latest_specs(req, res)
491
+ @source_index.refresh!
492
+
493
+ res['content-type'] = 'application/x-gzip'
494
+
495
+ add_date res
496
+
497
+ specs = @source_index.latest_specs.sort.map do |spec|
498
+ platform = spec.original_platform
499
+ platform = LibGems::Platform::RUBY if platform.nil?
500
+ [spec.name, spec.version, platform]
501
+ end
502
+
503
+ specs = Marshal.dump specs
504
+
505
+ if req.path =~ /\.gz$/ then
506
+ specs = LibGems.gzip specs
507
+ res['content-type'] = 'application/x-gzip'
508
+ else
509
+ res['content-type'] = 'application/octet-stream'
510
+ end
511
+
512
+ if req.request_method == 'HEAD' then
513
+ res['content-length'] = specs.length
514
+ else
515
+ res.body << specs
516
+ end
517
+ end
518
+
519
+ ##
520
+ # Creates server sockets based on the addresses option. If no addresses
521
+ # were given a server socket for all interfaces is created.
522
+
523
+ def listen addresses = @addresses
524
+ addresses = [nil] unless addresses
525
+
526
+ listeners = 0
527
+
528
+ addresses.each do |address|
529
+ begin
530
+ @server.listen address, @port
531
+ @server.listeners[listeners..-1].each do |listener|
532
+ host, port = listener.addr.values_at 2, 1
533
+ host = "[#{host}]" if host =~ /:/ # we don't reverse lookup
534
+ say "Server started at http://#{host}:#{port}"
535
+ end
536
+
537
+ listeners = @server.listeners.length
538
+ rescue SystemCallError
539
+ next
540
+ end
541
+ end
542
+
543
+ if @server.listeners.empty? then
544
+ say "Unable to start a server."
545
+ say "Check for running servers or your --bind and --port arguments"
546
+ terminate_interaction 1
547
+ end
548
+ end
549
+
550
+ def quick(req, res)
551
+ @source_index.refresh!
552
+
553
+ res['content-type'] = 'text/plain'
554
+ add_date res
555
+
556
+ case req.request_uri.path
557
+ when '/quick/index' then
558
+ res.body << @source_index.map { |name,| name }.sort.join("\n")
559
+ when '/quick/index.rz' then
560
+ index = @source_index.map { |name,| name }.sort.join("\n")
561
+ res['content-type'] = 'application/x-deflate'
562
+ res.body << LibGems.deflate(index)
563
+ when '/quick/latest_index' then
564
+ index = @source_index.latest_specs.map { |spec| spec.full_name }
565
+ res.body << index.sort.join("\n")
566
+ when '/quick/latest_index.rz' then
567
+ index = @source_index.latest_specs.map { |spec| spec.full_name }
568
+ res['content-type'] = 'application/x-deflate'
569
+ res.body << LibGems.deflate(index.sort.join("\n"))
570
+ when %r|^/quick/(Marshal.#{Regexp.escape LibGems.marshal_version}/)?(.*?)-([0-9.]+)(-.*?)?\.gemspec\.rz$| then
571
+ dep = LibGems::Dependency.new $2, $3
572
+ specs = @source_index.search dep
573
+ marshal_format = $1
574
+
575
+ selector = [$2, $3, $4].map { |s| s.inspect }.join ' '
576
+
577
+ platform = if $4 then
578
+ LibGems::Platform.new $4.sub(/^-/, '')
579
+ else
580
+ LibGems::Platform::RUBY
581
+ end
582
+
583
+ specs = specs.select { |s| s.platform == platform }
584
+
585
+ if specs.empty? then
586
+ res.status = 404
587
+ res.body = "No gems found matching #{selector}"
588
+ elsif specs.length > 1 then
589
+ res.status = 500
590
+ res.body = "Multiple gems found matching #{selector}"
591
+ elsif marshal_format then
592
+ res['content-type'] = 'application/x-deflate'
593
+ res.body << LibGems.deflate(Marshal.dump(specs.first))
594
+ else # deprecated YAML format
595
+ res['content-type'] = 'application/x-deflate'
596
+ res.body << LibGems.deflate(specs.first.to_yaml)
597
+ end
598
+ else
599
+ raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
600
+ end
601
+ end
602
+
603
+ def root(req, res)
604
+ @source_index.refresh!
605
+ add_date res
606
+
607
+ raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless
608
+ req.path == '/'
609
+
610
+ specs = []
611
+ total_file_count = 0
612
+
613
+ @source_index.each do |path, spec|
614
+ total_file_count += spec.files.size
615
+ deps = spec.dependencies.map do |dep|
616
+ { "name" => dep.name,
617
+ "type" => dep.type,
618
+ "version" => dep.requirement.to_s, }
619
+ end
620
+
621
+ deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] }
622
+ deps.last["is_last"] = true unless deps.empty?
623
+
624
+ # executables
625
+ executables = spec.executables.sort.collect { |exec| {"executable" => exec} }
626
+ executables = nil if executables.empty?
627
+ executables.last["is_last"] = true if executables
628
+
629
+ specs << {
630
+ "authors" => spec.authors.sort.join(", "),
631
+ "date" => spec.date.to_s,
632
+ "dependencies" => deps,
633
+ "doc_path" => "/doc_root/#{spec.full_name}/rdoc/index.html",
634
+ "executables" => executables,
635
+ "only_one_executable" => (executables && executables.size == 1),
636
+ "full_name" => spec.full_name,
637
+ "has_deps" => !deps.empty?,
638
+ "homepage" => spec.homepage,
639
+ "name" => spec.name,
640
+ "rdoc_installed" => LibGems::DocManager.new(spec).rdoc_installed?,
641
+ "summary" => spec.summary,
642
+ "version" => spec.version.to_s,
643
+ }
644
+ end
645
+
646
+ specs << {
647
+ "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others",
648
+ "dependencies" => [],
649
+ "doc_path" => "/doc_root/rubygems-#{LibGems::GEM_VERSION}/rdoc/index.html",
650
+ "executables" => [{"executable" => 'gem', "is_last" => true}],
651
+ "only_one_executable" => true,
652
+ "full_name" => "libgems-#{LibGems::VERSION}",
653
+ "has_deps" => false,
654
+ "homepage" => "http://docs.rubygems.org/",
655
+ "name" => 'rubygems',
656
+ "rdoc_installed" => true,
657
+ "summary" => "#{LibGems::NAME} itself",
658
+ "version" => LibGems::VERSION,
659
+ }
660
+
661
+ specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] }
662
+ specs.last["is_last"] = true
663
+
664
+ # tag all specs with first_name_entry
665
+ last_spec = nil
666
+ specs.each do |spec|
667
+ is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase)
668
+ spec["first_name_entry"] = is_first
669
+ last_spec = spec
670
+ end
671
+
672
+ # create page from template
673
+ template = ERB.new(DOC_TEMPLATE)
674
+ res['content-type'] = 'text/html'
675
+
676
+ values = { "gem_count" => specs.size.to_s, "specs" => specs,
677
+ "total_file_count" => total_file_count.to_s }
678
+
679
+ result = template.result binding
680
+ res.body = result
681
+ end
682
+
683
+ ##
684
+ # Can be used for quick navigation to the rdoc documentation. You can then
685
+ # define a search shortcut for your browser. E.g. in Firefox connect
686
+ # 'shortcut:rdoc' to http://localhost:8808/rdoc?q=%s template. Then you can
687
+ # directly open the ActionPack documentation by typing 'rdoc actionp'. If
688
+ # there are multiple hits for the search term, they are presented as a list
689
+ # with links.
690
+ #
691
+ # Search algorithm aims for an intuitive search:
692
+ # 1. first try to find the gems and documentation folders which name
693
+ # starts with the search term
694
+ # 2. search for entries, that *contain* the search term
695
+ # 3. show all the gems
696
+ #
697
+ # If there is only one search hit, user is immediately redirected to the
698
+ # documentation for the particular gem, otherwise a list with results is
699
+ # shown.
700
+ #
701
+ # === Additional trick - install documentation for ruby core
702
+ #
703
+ # Note: please adjust paths accordingly use for example 'locate yaml.rb' and
704
+ # 'gem environment' to identify directories, that are specific for your
705
+ # local installation
706
+ #
707
+ # 1. install ruby sources
708
+ # cd /usr/src
709
+ # sudo apt-get source ruby
710
+ #
711
+ # 2. generate documentation
712
+ # rdoc -o /usr/lib/ruby/gems/1.8/doc/core/rdoc \
713
+ # /usr/lib/ruby/1.8 ruby1.8-1.8.7.72
714
+ #
715
+ # By typing 'rdoc core' you can now access the core documentation
716
+
717
+ def rdoc(req, res)
718
+ query = req.query['q']
719
+ show_rdoc_for_pattern("#{query}*", res) && return
720
+ show_rdoc_for_pattern("*#{query}*", res) && return
721
+
722
+ template = ERB.new RDOC_NO_DOCUMENTATION
723
+
724
+ res['content-type'] = 'text/html'
725
+ res.body = template.result binding
726
+ end
727
+
728
+ ##
729
+ # Returns true and prepares http response, if rdoc for the requested gem
730
+ # name pattern was found.
731
+ #
732
+ # The search is based on the file system content, not on the gems metadata.
733
+ # This allows additional documentation folders like 'core' for the ruby core
734
+ # documentation - just put it underneath the main doc folder.
735
+
736
+ def show_rdoc_for_pattern(pattern, res)
737
+ found_gems = Dir.glob("{#{@gem_dirs.join ','}}/doc/#{pattern}").select {|path|
738
+ File.exist? File.join(path, 'rdoc/index.html')
739
+ }
740
+ case found_gems.length
741
+ when 0
742
+ return false
743
+ when 1
744
+ new_path = File.basename(found_gems[0])
745
+ res.status = 302
746
+ res['Location'] = "/doc_root/#{new_path}/rdoc/index.html"
747
+ return true
748
+ else
749
+ doc_items = []
750
+ found_gems.each do |file_name|
751
+ base_name = File.basename(file_name)
752
+ doc_items << {
753
+ :name => base_name,
754
+ :url => "/doc_root/#{base_name}/rdoc/index.html",
755
+ :summary => ''
756
+ }
757
+ end
758
+
759
+ template = ERB.new(RDOC_SEARCH_TEMPLATE)
760
+ res['content-type'] = 'text/html'
761
+ result = template.result binding
762
+ res.body = result
763
+ return true
764
+ end
765
+ end
766
+
767
+ def run
768
+ listen
769
+
770
+ WEBrick::Daemon.start if @daemon
771
+
772
+ @server.mount_proc "/yaml", method(:yaml)
773
+ @server.mount_proc "/yaml.Z", method(:yaml)
774
+
775
+ @server.mount_proc "/Marshal.#{LibGems.marshal_version}", method(:Marshal)
776
+ @server.mount_proc "/Marshal.#{LibGems.marshal_version}.Z", method(:Marshal)
777
+
778
+ @server.mount_proc "/specs.#{LibGems.marshal_version}", method(:specs)
779
+ @server.mount_proc "/specs.#{LibGems.marshal_version}.gz", method(:specs)
780
+
781
+ @server.mount_proc "/latest_specs.#{LibGems.marshal_version}",
782
+ method(:latest_specs)
783
+ @server.mount_proc "/latest_specs.#{LibGems.marshal_version}.gz",
784
+ method(:latest_specs)
785
+
786
+ @server.mount_proc "/quick/", method(:quick)
787
+
788
+ @server.mount_proc("/gem-server-rdoc-style.css") do |req, res|
789
+ res['content-type'] = 'text/css'
790
+ add_date res
791
+ res.body << RDOC_CSS
792
+ end
793
+
794
+ @server.mount_proc "/", method(:root)
795
+
796
+ @server.mount_proc "/rdoc", method(:rdoc)
797
+
798
+ paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" }
799
+ paths.each do |mount_point, mount_dir|
800
+ @server.mount(mount_point, WEBrick::HTTPServlet::FileHandler,
801
+ File.join(@gem_dirs.first, mount_dir), true)
802
+ end
803
+
804
+ trap("INT") { @server.shutdown; exit! }
805
+ trap("TERM") { @server.shutdown; exit! }
806
+
807
+ launch if @launch
808
+
809
+ @server.start
810
+ end
811
+
812
+ def specs(req, res)
813
+ @source_index.refresh!
814
+
815
+ add_date res
816
+
817
+ specs = @source_index.sort.map do |_, spec|
818
+ platform = spec.original_platform
819
+ platform = LibGems::Platform::RUBY if platform.nil?
820
+ [spec.name, spec.version, platform]
821
+ end
822
+
823
+ specs = Marshal.dump specs
824
+
825
+ if req.path =~ /\.gz$/ then
826
+ specs = LibGems.gzip specs
827
+ res['content-type'] = 'application/x-gzip'
828
+ else
829
+ res['content-type'] = 'application/octet-stream'
830
+ end
831
+
832
+ if req.request_method == 'HEAD' then
833
+ res['content-length'] = specs.length
834
+ else
835
+ res.body << specs
836
+ end
837
+ end
838
+
839
+ def yaml(req, res)
840
+ @source_index.refresh!
841
+
842
+ add_date res
843
+
844
+ index = @source_index.to_yaml
845
+
846
+ if req.path =~ /Z$/ then
847
+ res['content-type'] = 'application/x-deflate'
848
+ index = LibGems.deflate index
849
+ else
850
+ res['content-type'] = 'text/plain'
851
+ end
852
+
853
+ if req.request_method == 'HEAD' then
854
+ res['content-length'] = index.length
855
+ return
856
+ end
857
+
858
+ res.body << index
859
+ end
860
+
861
+ def launch
862
+ listeners = @server.listeners.map{|l| l.addr[2] }
863
+
864
+ host = listeners.any?{|l| l == '0.0.0.0'} ? 'localhost' : listeners.first
865
+
866
+ say "Launching browser to http://#{host}:#{@port}"
867
+
868
+ system("#{@launch} http://#{host}:#{@port}")
869
+ end
870
+
871
+ end
872
+