rubygems-server 0.1.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.
- checksums.yaml +7 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +50 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/rubygems/commands/server_command.rb +85 -0
- data/lib/rubygems/server/version.rb +7 -0
- data/lib/rubygems/server.rb +882 -0
- data/lib/rubygems_plugin.rb +4 -0
- data/rubygems-server.gemspec +32 -0
- metadata +70 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: edae593cc5b009ece9ad798a753242bf5fc501b78feedf34261e86f41605f8b7
|
4
|
+
data.tar.gz: c42e795f5252111d62d0a595c5e057ccb287953ca262a9ead9c4149b812ee4a7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 72b1647b369e1d0a4cfaa22adf9b6c6b2d19c566944bf324e9d726ed234db81b8a7f2957580013fbc5fd2e46ad68e30778822bcd9d83deb1a53d3a2acffdefe7
|
7
|
+
data.tar.gz: b70f06a91f6263a15eb64bf3844850497fd6e4482733c44fcd6e820909686206285d8b863f16846fed0ec80f33bad0315fe31064e725b499b0eaecf2eafca02c
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2021 Hiroshi SHIBATA
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Gem::Server
|
2
|
+
|
3
|
+
Gem::Server and allows users to serve gems for consumption by `gem --remote-install`.
|
4
|
+
|
5
|
+
gem_server starts an HTTP server on the given port and serves the following:
|
6
|
+
|
7
|
+
* "/" - Browsing of gem spec files for installed gems
|
8
|
+
* "/specs.#{Gem.marshal_version}.gz" - specs name/version/platform index
|
9
|
+
* "/latest_specs.#{Gem.marshal_version}.gz" - latest specs
|
10
|
+
name/version/platform index
|
11
|
+
* "/quick/" - Individual gemspecs
|
12
|
+
* "/gems" - Direct access to download the installable gems
|
13
|
+
* "/rdoc?q=" - Search for installed rdoc documentation
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'rubygems-server'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle install
|
26
|
+
|
27
|
+
Or install it yourself as:
|
28
|
+
|
29
|
+
$ gem install rubygems-server
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
gem_server = Gem::Server.new Gem.dir, 8089, false
|
35
|
+
gem_server.run
|
36
|
+
```
|
37
|
+
|
38
|
+
## Development
|
39
|
+
|
40
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
41
|
+
|
42
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
43
|
+
|
44
|
+
## Contributing
|
45
|
+
|
46
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/rubygems/rubygems-server.
|
47
|
+
|
48
|
+
## License
|
49
|
+
|
50
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "rubygems/server"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require "irb"
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'rubygems/command'
|
3
|
+
require_relative '../server'
|
4
|
+
|
5
|
+
class Gem::Commands::ServerCommand < Gem::Command
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
super 'server', 'Documentation and gem repository HTTP server',
|
9
|
+
:port => 8808, :gemdir => [], :daemon => false
|
10
|
+
|
11
|
+
OptionParser.accept :Port do |port|
|
12
|
+
if port =~ /\A\d+\z/
|
13
|
+
port = Integer port
|
14
|
+
raise OptionParser::InvalidArgument, "#{port}: not a port number" if
|
15
|
+
port > 65535
|
16
|
+
|
17
|
+
port
|
18
|
+
else
|
19
|
+
begin
|
20
|
+
Socket.getservbyname port
|
21
|
+
rescue SocketError
|
22
|
+
raise OptionParser::InvalidArgument, "#{port}: no such named service"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
add_option '-p', '--port=PORT', :Port,
|
28
|
+
'port to listen on' do |port, options|
|
29
|
+
options[:port] = port
|
30
|
+
end
|
31
|
+
|
32
|
+
add_option '-d', '--dir=GEMDIR',
|
33
|
+
'directories from which to serve gems',
|
34
|
+
'multiple directories may be provided' do |gemdir, options|
|
35
|
+
options[:gemdir] << File.expand_path(gemdir)
|
36
|
+
end
|
37
|
+
|
38
|
+
add_option '--[no-]daemon', 'run as a daemon' do |daemon, options|
|
39
|
+
options[:daemon] = daemon
|
40
|
+
end
|
41
|
+
|
42
|
+
add_option '-b', '--bind=HOST,HOST',
|
43
|
+
'addresses to bind', Array do |address, options|
|
44
|
+
options[:addresses] ||= []
|
45
|
+
options[:addresses].push(*address)
|
46
|
+
end
|
47
|
+
|
48
|
+
add_option '-l', '--launch[=COMMAND]',
|
49
|
+
'launches a browser window',
|
50
|
+
"COMMAND defaults to 'start' on Windows",
|
51
|
+
"and 'open' on all other platforms" do |launch, options|
|
52
|
+
launch ||= Gem.win_platform? ? 'start' : 'open'
|
53
|
+
options[:launch] = launch
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def defaults_str # :nodoc:
|
58
|
+
"--port 8808 --dir #{Gem.dir} --no-daemon"
|
59
|
+
end
|
60
|
+
|
61
|
+
def description # :nodoc:
|
62
|
+
<<-EOF
|
63
|
+
The server command starts up a web server that hosts the RDoc for your
|
64
|
+
installed gems and can operate as a server for installation of gems on other
|
65
|
+
machines.
|
66
|
+
|
67
|
+
The cache files for installed gems must exist to use the server as a source
|
68
|
+
for gem installation.
|
69
|
+
|
70
|
+
To install gems from a running server, use `gem install GEMNAME --source
|
71
|
+
http://gem_server_host:8808`
|
72
|
+
|
73
|
+
You can set up a shortcut to gem server documentation using the URL:
|
74
|
+
|
75
|
+
http://localhost:8808/rdoc?q=%s - Firefox
|
76
|
+
http://localhost:8808/rdoc?q=* - LaunchBar
|
77
|
+
|
78
|
+
EOF
|
79
|
+
end
|
80
|
+
|
81
|
+
def execute
|
82
|
+
options[:gemdir] = Gem.path if options[:gemdir].empty?
|
83
|
+
Gem::Server.run options
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,882 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'zlib'
|
3
|
+
require 'erb'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'rubygems/rdoc'
|
8
|
+
|
9
|
+
##
|
10
|
+
# Gem::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.#{Gem.marshal_version}.gz" - specs name/version/platform index
|
16
|
+
# * "/latest_specs.#{Gem.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
|
+
#
|
22
|
+
# == Usage
|
23
|
+
#
|
24
|
+
# gem_server = Gem::Server.new Gem.dir, 8089, false
|
25
|
+
# gem_server.run
|
26
|
+
#
|
27
|
+
#--
|
28
|
+
# TODO Refactor into a real WEBrick servlet to remove code duplication.
|
29
|
+
|
30
|
+
class Gem::Server
|
31
|
+
attr_reader :spec_dirs
|
32
|
+
|
33
|
+
include ERB::Util
|
34
|
+
include Gem::UserInteraction
|
35
|
+
|
36
|
+
SEARCH = <<-ERB.freeze
|
37
|
+
<form class="headerSearch" name="headerSearchForm" method="get" action="/rdoc">
|
38
|
+
<div id="search" style="float:right">
|
39
|
+
<label for="q">Filter/Search</label>
|
40
|
+
<input id="q" type="text" style="width:10em" name="q">
|
41
|
+
<button type="submit" style="display:none"></button>
|
42
|
+
</div>
|
43
|
+
</form>
|
44
|
+
ERB
|
45
|
+
|
46
|
+
DOC_TEMPLATE = <<-'ERB'.freeze
|
47
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
48
|
+
<!DOCTYPE html
|
49
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
50
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
51
|
+
|
52
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
53
|
+
<head>
|
54
|
+
<title>RubyGems Documentation Index</title>
|
55
|
+
<link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
|
56
|
+
</head>
|
57
|
+
<body>
|
58
|
+
<div id="fileHeader">
|
59
|
+
<%= SEARCH %>
|
60
|
+
<h1>RubyGems Documentation Index</h1>
|
61
|
+
</div>
|
62
|
+
<!-- banner header -->
|
63
|
+
|
64
|
+
<div id="bodyContent">
|
65
|
+
<div id="contextContent">
|
66
|
+
<div id="description">
|
67
|
+
<h1>Summary</h1>
|
68
|
+
<p>There are <%=values["gem_count"]%> gems installed:</p>
|
69
|
+
<p>
|
70
|
+
<%= values["specs"].map { |v| "<a href=\"##{u v["name"]}\">#{h v["name"]}</a>" }.join ', ' %>.
|
71
|
+
<h1>Gems</h1>
|
72
|
+
|
73
|
+
<dl>
|
74
|
+
<% values["specs"].each do |spec| %>
|
75
|
+
<dt>
|
76
|
+
<% if spec["first_name_entry"] then %>
|
77
|
+
<a name="<%=h spec["name"]%>"></a>
|
78
|
+
<% end %>
|
79
|
+
|
80
|
+
<b><%=h spec["name"]%> <%=h spec["version"]%></b>
|
81
|
+
|
82
|
+
<% if spec["ri_installed"] || spec["rdoc_installed"] then %>
|
83
|
+
<a href="<%=spec["doc_path"]%>">[rdoc]</a>
|
84
|
+
<% else %>
|
85
|
+
<span title="rdoc not installed">[rdoc]</span>
|
86
|
+
<% end %>
|
87
|
+
|
88
|
+
<% if spec["homepage"] then %>
|
89
|
+
<a href="<%=uri_encode spec["homepage"]%>" title="<%=h spec["homepage"]%>">[www]</a>
|
90
|
+
<% else %>
|
91
|
+
<span title="no homepage available">[www]</span>
|
92
|
+
<% end %>
|
93
|
+
|
94
|
+
<% if spec["has_deps"] then %>
|
95
|
+
- depends on
|
96
|
+
<%= spec["dependencies"].map { |v| "<a href=\"##{u v["name"]}\">#{h v["name"]}</a>" }.join ', ' %>.
|
97
|
+
<% end %>
|
98
|
+
</dt>
|
99
|
+
<dd>
|
100
|
+
<%=spec["summary"]%>
|
101
|
+
<% if spec["executables"] then %>
|
102
|
+
<br/>
|
103
|
+
|
104
|
+
<% if spec["only_one_executable"] then %>
|
105
|
+
Executable is
|
106
|
+
<% else %>
|
107
|
+
Executables are
|
108
|
+
<%end%>
|
109
|
+
|
110
|
+
<%= spec["executables"].map { |v| "<span class=\"context-item-name\">#{h v["executable"]}</span>"}.join ', ' %>.
|
111
|
+
|
112
|
+
<%end%>
|
113
|
+
<br/>
|
114
|
+
<br/>
|
115
|
+
</dd>
|
116
|
+
<% end %>
|
117
|
+
</dl>
|
118
|
+
|
119
|
+
</div>
|
120
|
+
</div>
|
121
|
+
</div>
|
122
|
+
<div id="validator-badges">
|
123
|
+
<p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
|
124
|
+
</div>
|
125
|
+
</body>
|
126
|
+
</html>
|
127
|
+
ERB
|
128
|
+
|
129
|
+
# CSS is copy & paste from rdoc-style.css, RDoc V1.0.1 - 20041108
|
130
|
+
RDOC_CSS = <<-CSS.freeze
|
131
|
+
body {
|
132
|
+
font-family: Verdana,Arial,Helvetica,sans-serif;
|
133
|
+
font-size: 90%;
|
134
|
+
margin: 0;
|
135
|
+
margin-left: 40px;
|
136
|
+
padding: 0;
|
137
|
+
background: white;
|
138
|
+
}
|
139
|
+
|
140
|
+
h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
|
141
|
+
h1 { font-size: 150%; }
|
142
|
+
h2,h3,h4 { margin-top: 1em; }
|
143
|
+
|
144
|
+
a { background: #eef; color: #039; text-decoration: none; }
|
145
|
+
a:hover { background: #039; color: #eef; }
|
146
|
+
|
147
|
+
/* Override the base stylesheets Anchor inside a table cell */
|
148
|
+
td > a {
|
149
|
+
background: transparent;
|
150
|
+
color: #039;
|
151
|
+
text-decoration: none;
|
152
|
+
}
|
153
|
+
|
154
|
+
/* and inside a section title */
|
155
|
+
.section-title > a {
|
156
|
+
background: transparent;
|
157
|
+
color: #eee;
|
158
|
+
text-decoration: none;
|
159
|
+
}
|
160
|
+
|
161
|
+
/* === Structural elements =================================== */
|
162
|
+
|
163
|
+
div#index {
|
164
|
+
margin: 0;
|
165
|
+
margin-left: -40px;
|
166
|
+
padding: 0;
|
167
|
+
font-size: 90%;
|
168
|
+
}
|
169
|
+
|
170
|
+
|
171
|
+
div#index a {
|
172
|
+
margin-left: 0.7em;
|
173
|
+
}
|
174
|
+
|
175
|
+
div#index .section-bar {
|
176
|
+
margin-left: 0px;
|
177
|
+
padding-left: 0.7em;
|
178
|
+
background: #ccc;
|
179
|
+
font-size: small;
|
180
|
+
}
|
181
|
+
|
182
|
+
|
183
|
+
div#classHeader, div#fileHeader {
|
184
|
+
width: auto;
|
185
|
+
color: white;
|
186
|
+
padding: 0.5em 1.5em 0.5em 1.5em;
|
187
|
+
margin: 0;
|
188
|
+
margin-left: -40px;
|
189
|
+
border-bottom: 3px solid #006;
|
190
|
+
}
|
191
|
+
|
192
|
+
div#classHeader a, div#fileHeader a {
|
193
|
+
background: inherit;
|
194
|
+
color: white;
|
195
|
+
}
|
196
|
+
|
197
|
+
div#classHeader td, div#fileHeader td {
|
198
|
+
background: inherit;
|
199
|
+
color: white;
|
200
|
+
}
|
201
|
+
|
202
|
+
|
203
|
+
div#fileHeader {
|
204
|
+
background: #057;
|
205
|
+
}
|
206
|
+
|
207
|
+
div#classHeader {
|
208
|
+
background: #048;
|
209
|
+
}
|
210
|
+
|
211
|
+
|
212
|
+
.class-name-in-header {
|
213
|
+
font-size: 180%;
|
214
|
+
font-weight: bold;
|
215
|
+
}
|
216
|
+
|
217
|
+
|
218
|
+
div#bodyContent {
|
219
|
+
padding: 0 1.5em 0 1.5em;
|
220
|
+
}
|
221
|
+
|
222
|
+
div#description {
|
223
|
+
padding: 0.5em 1.5em;
|
224
|
+
background: #efefef;
|
225
|
+
border: 1px dotted #999;
|
226
|
+
}
|
227
|
+
|
228
|
+
div#description h1,h2,h3,h4,h5,h6 {
|
229
|
+
color: #125;;
|
230
|
+
background: transparent;
|
231
|
+
}
|
232
|
+
|
233
|
+
div#validator-badges {
|
234
|
+
text-align: center;
|
235
|
+
}
|
236
|
+
div#validator-badges img { border: 0; }
|
237
|
+
|
238
|
+
div#copyright {
|
239
|
+
color: #333;
|
240
|
+
background: #efefef;
|
241
|
+
font: 0.75em sans-serif;
|
242
|
+
margin-top: 5em;
|
243
|
+
margin-bottom: 0;
|
244
|
+
padding: 0.5em 2em;
|
245
|
+
}
|
246
|
+
|
247
|
+
|
248
|
+
/* === Classes =================================== */
|
249
|
+
|
250
|
+
table.header-table {
|
251
|
+
color: white;
|
252
|
+
font-size: small;
|
253
|
+
}
|
254
|
+
|
255
|
+
.type-note {
|
256
|
+
font-size: small;
|
257
|
+
color: #DEDEDE;
|
258
|
+
}
|
259
|
+
|
260
|
+
.xxsection-bar {
|
261
|
+
background: #eee;
|
262
|
+
color: #333;
|
263
|
+
padding: 3px;
|
264
|
+
}
|
265
|
+
|
266
|
+
.section-bar {
|
267
|
+
color: #333;
|
268
|
+
border-bottom: 1px solid #999;
|
269
|
+
margin-left: -20px;
|
270
|
+
}
|
271
|
+
|
272
|
+
|
273
|
+
.section-title {
|
274
|
+
background: #79a;
|
275
|
+
color: #eee;
|
276
|
+
padding: 3px;
|
277
|
+
margin-top: 2em;
|
278
|
+
margin-left: -30px;
|
279
|
+
border: 1px solid #999;
|
280
|
+
}
|
281
|
+
|
282
|
+
.top-aligned-row { vertical-align: top }
|
283
|
+
.bottom-aligned-row { vertical-align: bottom }
|
284
|
+
|
285
|
+
/* --- Context section classes ----------------------- */
|
286
|
+
|
287
|
+
.context-row { }
|
288
|
+
.context-item-name { font-family: monospace; font-weight: bold; color: black; }
|
289
|
+
.context-item-value { font-size: small; color: #448; }
|
290
|
+
.context-item-desc { color: #333; padding-left: 2em; }
|
291
|
+
|
292
|
+
/* --- Method classes -------------------------- */
|
293
|
+
.method-detail {
|
294
|
+
background: #efefef;
|
295
|
+
padding: 0;
|
296
|
+
margin-top: 0.5em;
|
297
|
+
margin-bottom: 1em;
|
298
|
+
border: 1px dotted #ccc;
|
299
|
+
}
|
300
|
+
.method-heading {
|
301
|
+
color: black;
|
302
|
+
background: #ccc;
|
303
|
+
border-bottom: 1px solid #666;
|
304
|
+
padding: 0.2em 0.5em 0 0.5em;
|
305
|
+
}
|
306
|
+
.method-signature { color: black; background: inherit; }
|
307
|
+
.method-name { font-weight: bold; }
|
308
|
+
.method-args { font-style: italic; }
|
309
|
+
.method-description { padding: 0 0.5em 0 0.5em; }
|
310
|
+
|
311
|
+
/* --- Source code sections -------------------- */
|
312
|
+
|
313
|
+
a.source-toggle { font-size: 90%; }
|
314
|
+
div.method-source-code {
|
315
|
+
background: #262626;
|
316
|
+
color: #ffdead;
|
317
|
+
margin: 1em;
|
318
|
+
padding: 0.5em;
|
319
|
+
border: 1px dashed #999;
|
320
|
+
overflow: hidden;
|
321
|
+
}
|
322
|
+
|
323
|
+
div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
324
|
+
|
325
|
+
/* --- Ruby keyword styles --------------------- */
|
326
|
+
|
327
|
+
.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
|
328
|
+
|
329
|
+
.ruby-constant { color: #7fffd4; background: transparent; }
|
330
|
+
.ruby-keyword { color: #00ffff; background: transparent; }
|
331
|
+
.ruby-ivar { color: #eedd82; background: transparent; }
|
332
|
+
.ruby-operator { color: #00ffee; background: transparent; }
|
333
|
+
.ruby-identifier { color: #ffdead; background: transparent; }
|
334
|
+
.ruby-node { color: #ffa07a; background: transparent; }
|
335
|
+
.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
|
336
|
+
.ruby-regexp { color: #ffa07a; background: transparent; }
|
337
|
+
.ruby-value { color: #7fffd4; background: transparent; }
|
338
|
+
CSS
|
339
|
+
|
340
|
+
RDOC_NO_DOCUMENTATION = <<-'ERB'.freeze
|
341
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
342
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
343
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
344
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
345
|
+
<head>
|
346
|
+
<title>Found documentation</title>
|
347
|
+
<link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
|
348
|
+
</head>
|
349
|
+
<body>
|
350
|
+
<div id="fileHeader">
|
351
|
+
<%= SEARCH %>
|
352
|
+
<h1>No documentation found</h1>
|
353
|
+
</div>
|
354
|
+
|
355
|
+
<div id="bodyContent">
|
356
|
+
<div id="contextContent">
|
357
|
+
<div id="description">
|
358
|
+
<p>No gems matched <%= h query.inspect %></p>
|
359
|
+
|
360
|
+
<p>
|
361
|
+
Back to <a href="/">complete gem index</a>
|
362
|
+
</p>
|
363
|
+
|
364
|
+
</div>
|
365
|
+
</div>
|
366
|
+
</div>
|
367
|
+
<div id="validator-badges">
|
368
|
+
<p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
|
369
|
+
</div>
|
370
|
+
</body>
|
371
|
+
</html>
|
372
|
+
ERB
|
373
|
+
|
374
|
+
RDOC_SEARCH_TEMPLATE = <<-'ERB'.freeze
|
375
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
376
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
377
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
378
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
379
|
+
<head>
|
380
|
+
<title>Found documentation</title>
|
381
|
+
<link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
|
382
|
+
</head>
|
383
|
+
<body>
|
384
|
+
<div id="fileHeader">
|
385
|
+
<%= SEARCH %>
|
386
|
+
<h1>Found documentation</h1>
|
387
|
+
</div>
|
388
|
+
<!-- banner header -->
|
389
|
+
|
390
|
+
<div id="bodyContent">
|
391
|
+
<div id="contextContent">
|
392
|
+
<div id="description">
|
393
|
+
<h1>Summary</h1>
|
394
|
+
<p><%=doc_items.length%> documentation topics found.</p>
|
395
|
+
<h1>Topics</h1>
|
396
|
+
|
397
|
+
<dl>
|
398
|
+
<% doc_items.each do |doc_item| %>
|
399
|
+
<dt>
|
400
|
+
<b><%=doc_item[:name]%></b>
|
401
|
+
<a href="<%=u doc_item[:url]%>">[rdoc]</a>
|
402
|
+
</dt>
|
403
|
+
<dd>
|
404
|
+
<%=h doc_item[:summary]%>
|
405
|
+
<br/>
|
406
|
+
<br/>
|
407
|
+
</dd>
|
408
|
+
<% end %>
|
409
|
+
</dl>
|
410
|
+
|
411
|
+
<p>
|
412
|
+
Back to <a href="/">complete gem index</a>
|
413
|
+
</p>
|
414
|
+
|
415
|
+
</div>
|
416
|
+
</div>
|
417
|
+
</div>
|
418
|
+
<div id="validator-badges">
|
419
|
+
<p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
|
420
|
+
</div>
|
421
|
+
</body>
|
422
|
+
</html>
|
423
|
+
ERB
|
424
|
+
|
425
|
+
def self.run(options)
|
426
|
+
new(options[:gemdir], options[:port], options[:daemon],
|
427
|
+
options[:launch], options[:addresses]).run
|
428
|
+
end
|
429
|
+
|
430
|
+
def initialize(gem_dirs, port, daemon, launch = nil, addresses = nil)
|
431
|
+
begin
|
432
|
+
require 'webrick'
|
433
|
+
rescue LoadError
|
434
|
+
abort "webrick is not found. You may need to `gem install webrick` to install webrick."
|
435
|
+
end
|
436
|
+
|
437
|
+
Gem::RDoc.load_rdoc
|
438
|
+
Socket.do_not_reverse_lookup = true
|
439
|
+
|
440
|
+
@gem_dirs = Array gem_dirs
|
441
|
+
@port = port
|
442
|
+
@daemon = daemon
|
443
|
+
@launch = launch
|
444
|
+
@addresses = addresses
|
445
|
+
|
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 {|gem_dir| File.join gem_dir, 'specifications' }
|
450
|
+
@spec_dirs.reject! {|spec_dir| !File.directory? spec_dir }
|
451
|
+
|
452
|
+
reset_gems
|
453
|
+
|
454
|
+
@have_rdoc_4_plus = nil
|
455
|
+
end
|
456
|
+
|
457
|
+
def add_date(res)
|
458
|
+
res['date'] = @spec_dirs.map do |spec_dir|
|
459
|
+
File.stat(spec_dir).mtime
|
460
|
+
end.max
|
461
|
+
end
|
462
|
+
|
463
|
+
def uri_encode(str)
|
464
|
+
str.gsub(URI::UNSAFE) do |match|
|
465
|
+
match.each_byte.map {|c| sprintf('%%%02X', c.ord) }.join
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
def doc_root(gem_name)
|
470
|
+
if have_rdoc_4_plus?
|
471
|
+
"/doc_root/#{u gem_name}/"
|
472
|
+
else
|
473
|
+
"/doc_root/#{u gem_name}/rdoc/index.html"
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
def have_rdoc_4_plus?
|
478
|
+
@have_rdoc_4_plus ||=
|
479
|
+
Gem::Requirement.new('>= 4.0.0.preview2').satisfied_by? Gem::RDoc.rdoc_version
|
480
|
+
end
|
481
|
+
|
482
|
+
def latest_specs(req, res)
|
483
|
+
reset_gems
|
484
|
+
|
485
|
+
res['content-type'] = 'application/x-gzip'
|
486
|
+
|
487
|
+
add_date res
|
488
|
+
|
489
|
+
latest_specs = Gem::Specification.latest_specs
|
490
|
+
|
491
|
+
specs = latest_specs.sort.map do |spec|
|
492
|
+
platform = spec.original_platform || Gem::Platform::RUBY
|
493
|
+
[spec.name, spec.version, platform]
|
494
|
+
end
|
495
|
+
|
496
|
+
specs = Marshal.dump specs
|
497
|
+
|
498
|
+
if req.path =~ /\.gz$/
|
499
|
+
specs = Gem::Util.gzip specs
|
500
|
+
res['content-type'] = 'application/x-gzip'
|
501
|
+
else
|
502
|
+
res['content-type'] = 'application/octet-stream'
|
503
|
+
end
|
504
|
+
|
505
|
+
if req.request_method == 'HEAD'
|
506
|
+
res['content-length'] = specs.length
|
507
|
+
else
|
508
|
+
res.body << specs
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
##
|
513
|
+
# Creates server sockets based on the addresses option. If no addresses
|
514
|
+
# were given a server socket for all interfaces is created.
|
515
|
+
|
516
|
+
def listen(addresses = @addresses)
|
517
|
+
addresses = [nil] unless addresses
|
518
|
+
|
519
|
+
listeners = 0
|
520
|
+
|
521
|
+
addresses.each do |address|
|
522
|
+
begin
|
523
|
+
@server.listen address, @port
|
524
|
+
@server.listeners[listeners..-1].each do |listener|
|
525
|
+
host, port = listener.addr.values_at 2, 1
|
526
|
+
host = "[#{host}]" if host =~ /:/ # we don't reverse lookup
|
527
|
+
say "Server started at http://#{host}:#{port}"
|
528
|
+
end
|
529
|
+
|
530
|
+
listeners = @server.listeners.length
|
531
|
+
rescue SystemCallError
|
532
|
+
next
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
if @server.listeners.empty?
|
537
|
+
say "Unable to start a server."
|
538
|
+
say "Check for running servers or your --bind and --port arguments"
|
539
|
+
terminate_interaction 1
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
def prerelease_specs(req, res)
|
544
|
+
reset_gems
|
545
|
+
|
546
|
+
res['content-type'] = 'application/x-gzip'
|
547
|
+
|
548
|
+
add_date res
|
549
|
+
|
550
|
+
specs = Gem::Specification.select do |spec|
|
551
|
+
spec.version.prerelease?
|
552
|
+
end.sort.map do |spec|
|
553
|
+
platform = spec.original_platform || Gem::Platform::RUBY
|
554
|
+
[spec.name, spec.version, platform]
|
555
|
+
end
|
556
|
+
|
557
|
+
specs = Marshal.dump specs
|
558
|
+
|
559
|
+
if req.path =~ /\.gz$/
|
560
|
+
specs = Gem::Util.gzip specs
|
561
|
+
res['content-type'] = 'application/x-gzip'
|
562
|
+
else
|
563
|
+
res['content-type'] = 'application/octet-stream'
|
564
|
+
end
|
565
|
+
|
566
|
+
if req.request_method == 'HEAD'
|
567
|
+
res['content-length'] = specs.length
|
568
|
+
else
|
569
|
+
res.body << specs
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
def quick(req, res)
|
574
|
+
reset_gems
|
575
|
+
|
576
|
+
res['content-type'] = 'text/plain'
|
577
|
+
add_date res
|
578
|
+
|
579
|
+
case req.request_uri.path
|
580
|
+
when %r{^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)\.gemspec\.rz$} then
|
581
|
+
marshal_format, full_name = $1, $2
|
582
|
+
specs = Gem::Specification.find_all_by_full_name(full_name)
|
583
|
+
|
584
|
+
selector = full_name.inspect
|
585
|
+
|
586
|
+
if specs.empty?
|
587
|
+
res.status = 404
|
588
|
+
res.body = "No gems found matching #{selector}"
|
589
|
+
elsif specs.length > 1
|
590
|
+
res.status = 500
|
591
|
+
res.body = "Multiple gems found matching #{selector}"
|
592
|
+
elsif marshal_format
|
593
|
+
res['content-type'] = 'application/x-deflate'
|
594
|
+
res.body << Gem.deflate(Marshal.dump(specs.first))
|
595
|
+
end
|
596
|
+
else
|
597
|
+
raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
def root(req, res)
|
602
|
+
reset_gems
|
603
|
+
|
604
|
+
add_date res
|
605
|
+
|
606
|
+
raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless
|
607
|
+
req.path == '/'
|
608
|
+
|
609
|
+
specs = []
|
610
|
+
total_file_count = 0
|
611
|
+
|
612
|
+
Gem::Specification.each do |spec|
|
613
|
+
total_file_count += spec.files.size
|
614
|
+
deps = spec.dependencies.map do |dep|
|
615
|
+
{
|
616
|
+
"name" => dep.name,
|
617
|
+
"type" => dep.type,
|
618
|
+
"version" => dep.requirement.to_s,
|
619
|
+
}
|
620
|
+
end
|
621
|
+
|
622
|
+
deps = deps.sort_by {|dep| [dep["name"].downcase, dep["version"]] }
|
623
|
+
deps.last["is_last"] = true unless deps.empty?
|
624
|
+
|
625
|
+
# executables
|
626
|
+
executables = spec.executables.sort.collect {|exec| {"executable" => exec} }
|
627
|
+
executables = nil if executables.empty?
|
628
|
+
executables.last["is_last"] = true if executables
|
629
|
+
|
630
|
+
# Pre-process spec homepage for safety reasons
|
631
|
+
begin
|
632
|
+
homepage_uri = URI.parse(spec.homepage)
|
633
|
+
if [URI::HTTP, URI::HTTPS].member? homepage_uri.class
|
634
|
+
homepage_uri = spec.homepage
|
635
|
+
else
|
636
|
+
homepage_uri = "."
|
637
|
+
end
|
638
|
+
rescue URI::InvalidURIError
|
639
|
+
homepage_uri = "."
|
640
|
+
end
|
641
|
+
|
642
|
+
specs << {
|
643
|
+
"authors" => spec.authors.sort.join(", "),
|
644
|
+
"date" => spec.date.to_s,
|
645
|
+
"dependencies" => deps,
|
646
|
+
"doc_path" => doc_root(spec.full_name),
|
647
|
+
"executables" => executables,
|
648
|
+
"only_one_executable" => (executables && executables.size == 1),
|
649
|
+
"full_name" => spec.full_name,
|
650
|
+
"has_deps" => !deps.empty?,
|
651
|
+
"homepage" => homepage_uri,
|
652
|
+
"name" => spec.name,
|
653
|
+
"rdoc_installed" => Gem::RDoc.new(spec).rdoc_installed?,
|
654
|
+
"ri_installed" => Gem::RDoc.new(spec).ri_installed?,
|
655
|
+
"summary" => spec.summary,
|
656
|
+
"version" => spec.version.to_s,
|
657
|
+
}
|
658
|
+
end
|
659
|
+
|
660
|
+
specs << {
|
661
|
+
"authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others",
|
662
|
+
"dependencies" => [],
|
663
|
+
"doc_path" => doc_root("rubygems-#{Gem::VERSION}"),
|
664
|
+
"executables" => [{"executable" => 'gem', "is_last" => true}],
|
665
|
+
"only_one_executable" => true,
|
666
|
+
"full_name" => "rubygems-#{Gem::VERSION}",
|
667
|
+
"has_deps" => false,
|
668
|
+
"homepage" => "https://guides.rubygems.org/",
|
669
|
+
"name" => 'rubygems',
|
670
|
+
"ri_installed" => true,
|
671
|
+
"summary" => "RubyGems itself",
|
672
|
+
"version" => Gem::VERSION,
|
673
|
+
}
|
674
|
+
|
675
|
+
specs = specs.sort_by {|spec| [spec["name"].downcase, spec["version"]] }
|
676
|
+
specs.last["is_last"] = true
|
677
|
+
|
678
|
+
# tag all specs with first_name_entry
|
679
|
+
last_spec = nil
|
680
|
+
specs.each do |spec|
|
681
|
+
is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase)
|
682
|
+
spec["first_name_entry"] = is_first
|
683
|
+
last_spec = spec
|
684
|
+
end
|
685
|
+
|
686
|
+
# create page from template
|
687
|
+
template = ERB.new(DOC_TEMPLATE)
|
688
|
+
res['content-type'] = 'text/html'
|
689
|
+
|
690
|
+
values = { "gem_count" => specs.size.to_s, "specs" => specs,
|
691
|
+
"total_file_count" => total_file_count.to_s }
|
692
|
+
|
693
|
+
# suppress 1.9.3dev warning about unused variable
|
694
|
+
values = values
|
695
|
+
|
696
|
+
result = template.result binding
|
697
|
+
res.body = result
|
698
|
+
end
|
699
|
+
|
700
|
+
##
|
701
|
+
# Can be used for quick navigation to the rdoc documentation. You can then
|
702
|
+
# define a search shortcut for your browser. E.g. in Firefox connect
|
703
|
+
# 'shortcut:rdoc' to http://localhost:8808/rdoc?q=%s template. Then you can
|
704
|
+
# directly open the ActionPack documentation by typing 'rdoc actionp'. If
|
705
|
+
# there are multiple hits for the search term, they are presented as a list
|
706
|
+
# with links.
|
707
|
+
#
|
708
|
+
# Search algorithm aims for an intuitive search:
|
709
|
+
# 1. first try to find the gems and documentation folders which name
|
710
|
+
# starts with the search term
|
711
|
+
# 2. search for entries, that *contain* the search term
|
712
|
+
# 3. show all the gems
|
713
|
+
#
|
714
|
+
# If there is only one search hit, user is immediately redirected to the
|
715
|
+
# documentation for the particular gem, otherwise a list with results is
|
716
|
+
# shown.
|
717
|
+
#
|
718
|
+
# === Additional trick - install documentation for Ruby core
|
719
|
+
#
|
720
|
+
# Note: please adjust paths accordingly use for example 'locate yaml.rb' and
|
721
|
+
# 'gem environment' to identify directories, that are specific for your
|
722
|
+
# local installation
|
723
|
+
#
|
724
|
+
# 1. install Ruby sources
|
725
|
+
# cd /usr/src
|
726
|
+
# sudo apt-get source ruby
|
727
|
+
#
|
728
|
+
# 2. generate documentation
|
729
|
+
# rdoc -o /usr/lib/ruby/gems/1.8/doc/core/rdoc \
|
730
|
+
# /usr/lib/ruby/1.8 ruby1.8-1.8.7.72
|
731
|
+
#
|
732
|
+
# By typing 'rdoc core' you can now access the core documentation
|
733
|
+
|
734
|
+
def rdoc(req, res)
|
735
|
+
query = req.query['q']
|
736
|
+
show_rdoc_for_pattern("#{query}*", res) && return
|
737
|
+
show_rdoc_for_pattern("*#{query}*", res) && return
|
738
|
+
|
739
|
+
template = ERB.new RDOC_NO_DOCUMENTATION
|
740
|
+
|
741
|
+
res['content-type'] = 'text/html'
|
742
|
+
res.body = template.result binding
|
743
|
+
end
|
744
|
+
|
745
|
+
##
|
746
|
+
# Updates the server to use the latest installed gems.
|
747
|
+
|
748
|
+
def reset_gems # :nodoc:
|
749
|
+
Gem::Specification.dirs = @gem_dirs
|
750
|
+
end
|
751
|
+
|
752
|
+
##
|
753
|
+
# Returns true and prepares http response, if rdoc for the requested gem
|
754
|
+
# name pattern was found.
|
755
|
+
#
|
756
|
+
# The search is based on the file system content, not on the gems metadata.
|
757
|
+
# This allows additional documentation folders like 'core' for the Ruby core
|
758
|
+
# documentation - just put it underneath the main doc folder.
|
759
|
+
|
760
|
+
def show_rdoc_for_pattern(pattern, res)
|
761
|
+
found_gems = Dir.glob("{#{@gem_dirs.join ','}}/doc/#{pattern}").select do |path|
|
762
|
+
File.exist? File.join(path, 'rdoc/index.html')
|
763
|
+
end
|
764
|
+
case found_gems.length
|
765
|
+
when 0
|
766
|
+
return false
|
767
|
+
when 1
|
768
|
+
new_path = File.basename(found_gems[0])
|
769
|
+
res.status = 302
|
770
|
+
res['Location'] = doc_root new_path
|
771
|
+
return true
|
772
|
+
else
|
773
|
+
doc_items = []
|
774
|
+
found_gems.each do |file_name|
|
775
|
+
base_name = File.basename(file_name)
|
776
|
+
doc_items << {
|
777
|
+
:name => base_name,
|
778
|
+
:url => doc_root(new_path),
|
779
|
+
:summary => '',
|
780
|
+
}
|
781
|
+
end
|
782
|
+
|
783
|
+
template = ERB.new(RDOC_SEARCH_TEMPLATE)
|
784
|
+
res['content-type'] = 'text/html'
|
785
|
+
result = template.result binding
|
786
|
+
res.body = result
|
787
|
+
return true
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
def run
|
792
|
+
listen
|
793
|
+
|
794
|
+
WEBrick::Daemon.start if @daemon
|
795
|
+
|
796
|
+
@server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs)
|
797
|
+
@server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs)
|
798
|
+
|
799
|
+
@server.mount_proc "/latest_specs.#{Gem.marshal_version}",
|
800
|
+
method(:latest_specs)
|
801
|
+
@server.mount_proc "/latest_specs.#{Gem.marshal_version}.gz",
|
802
|
+
method(:latest_specs)
|
803
|
+
|
804
|
+
@server.mount_proc "/prerelease_specs.#{Gem.marshal_version}",
|
805
|
+
method(:prerelease_specs)
|
806
|
+
@server.mount_proc "/prerelease_specs.#{Gem.marshal_version}.gz",
|
807
|
+
method(:prerelease_specs)
|
808
|
+
|
809
|
+
@server.mount_proc "/quick/", method(:quick)
|
810
|
+
|
811
|
+
@server.mount_proc("/gem-server-rdoc-style.css") do |req, res|
|
812
|
+
res['content-type'] = 'text/css'
|
813
|
+
add_date res
|
814
|
+
res.body << RDOC_CSS
|
815
|
+
end
|
816
|
+
|
817
|
+
@server.mount_proc "/", method(:root)
|
818
|
+
|
819
|
+
@server.mount_proc "/rdoc", method(:rdoc)
|
820
|
+
|
821
|
+
file_handlers = {
|
822
|
+
'/gems' => '/cache/',
|
823
|
+
}
|
824
|
+
|
825
|
+
if have_rdoc_4_plus?
|
826
|
+
@server.mount '/doc_root', RDoc::Servlet, '/doc_root'
|
827
|
+
else
|
828
|
+
file_handlers['/doc_root'] = '/doc/'
|
829
|
+
end
|
830
|
+
|
831
|
+
@gem_dirs.each do |gem_dir|
|
832
|
+
file_handlers.each do |mount_point, mount_dir|
|
833
|
+
@server.mount(mount_point, WEBrick::HTTPServlet::FileHandler,
|
834
|
+
File.join(gem_dir, mount_dir), true)
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
trap("INT") { @server.shutdown; exit! }
|
839
|
+
trap("TERM") { @server.shutdown; exit! }
|
840
|
+
|
841
|
+
launch if @launch
|
842
|
+
|
843
|
+
@server.start
|
844
|
+
end
|
845
|
+
|
846
|
+
def specs(req, res)
|
847
|
+
reset_gems
|
848
|
+
|
849
|
+
add_date res
|
850
|
+
|
851
|
+
specs = Gem::Specification.sort_by(&:sort_obj).map do |spec|
|
852
|
+
platform = spec.original_platform || Gem::Platform::RUBY
|
853
|
+
[spec.name, spec.version, platform]
|
854
|
+
end
|
855
|
+
|
856
|
+
specs = Marshal.dump specs
|
857
|
+
|
858
|
+
if req.path =~ /\.gz$/
|
859
|
+
specs = Gem::Util.gzip specs
|
860
|
+
res['content-type'] = 'application/x-gzip'
|
861
|
+
else
|
862
|
+
res['content-type'] = 'application/octet-stream'
|
863
|
+
end
|
864
|
+
|
865
|
+
if req.request_method == 'HEAD'
|
866
|
+
res['content-length'] = specs.length
|
867
|
+
else
|
868
|
+
res.body << specs
|
869
|
+
end
|
870
|
+
end
|
871
|
+
|
872
|
+
def launch
|
873
|
+
listeners = @server.listeners.map{|l| l.addr[2] }
|
874
|
+
|
875
|
+
# TODO: 0.0.0.0 == any, not localhost.
|
876
|
+
host = listeners.any?{|l| l == '0.0.0.0' } ? 'localhost' : listeners.first
|
877
|
+
|
878
|
+
say "Launching browser to http://#{host}:#{@port}"
|
879
|
+
|
880
|
+
system("#{@launch} http://#{host}:#{@port}")
|
881
|
+
end
|
882
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/rubygems/server/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "rubygems-server"
|
7
|
+
spec.version = Rubygems::Server::VERSION
|
8
|
+
spec.authors = ["Hiroshi SHIBATA"]
|
9
|
+
spec.email = ["hsbt@ruby-lang.org"]
|
10
|
+
|
11
|
+
spec.summary = "Gem::Server and allows users to serve gems for consumption by `gem --remote-install`."
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = "https://github.com/rubygems/rubygems-server"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
16
|
+
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
19
|
+
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
23
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
24
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
25
|
+
end
|
26
|
+
end
|
27
|
+
spec.bindir = "exe"
|
28
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ["lib"]
|
30
|
+
|
31
|
+
spec.add_dependency "webrick"
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubygems-server
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hiroshi SHIBATA
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-10-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: webrick
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Gem::Server and allows users to serve gems for consumption by `gem --remote-install`.
|
28
|
+
email:
|
29
|
+
- hsbt@ruby-lang.org
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- Gemfile
|
35
|
+
- LICENSE.txt
|
36
|
+
- README.md
|
37
|
+
- Rakefile
|
38
|
+
- bin/console
|
39
|
+
- bin/setup
|
40
|
+
- lib/rubygems/commands/server_command.rb
|
41
|
+
- lib/rubygems/server.rb
|
42
|
+
- lib/rubygems/server/version.rb
|
43
|
+
- lib/rubygems_plugin.rb
|
44
|
+
- rubygems-server.gemspec
|
45
|
+
homepage: https://github.com/rubygems/rubygems-server
|
46
|
+
licenses:
|
47
|
+
- MIT
|
48
|
+
metadata:
|
49
|
+
homepage_uri: https://github.com/rubygems/rubygems-server
|
50
|
+
source_code_uri: https://github.com/rubygems/rubygems-server
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 2.6.0
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubygems_version: 3.3.0.dev
|
67
|
+
signing_key:
|
68
|
+
specification_version: 4
|
69
|
+
summary: Gem::Server and allows users to serve gems for consumption by `gem --remote-install`.
|
70
|
+
test_files: []
|