dispatcher 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.
- data/LICENSE.txt +0 -0
- data/README.txt +33 -0
- data/SyntaxCheck/lib/dispatcher.rb +0 -0
- data/SyntaxCheck/lib/dispatcher/cgi.rb +0 -0
- data/SyntaxCheck/lib/dispatcher/cli.rb +0 -0
- data/TODO.txt +0 -0
- data/lib/dispatch.rb +31 -0
- data/lib/dispatcher.rb +306 -0
- data/lib/dispatcher/cgi.rb +74 -0
- data/lib/dispatcher/cli.rb +76 -0
- metadata +60 -0
data/LICENSE.txt
ADDED
File without changes
|
data/README.txt
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
= Dispatcher
|
2
|
+
|
3
|
+
The +Dispatcher+ module defines a simple and consistent interface between Ruby and most webserver
|
4
|
+
configurations. This library provides a very restrictive set of features, and as such is generally
|
5
|
+
not meant to be directly used by web-application authors, but instead targets implementors of
|
6
|
+
frameworks and web-libraries.
|
7
|
+
|
8
|
+
== Basic Usage
|
9
|
+
|
10
|
+
The following is a very basic example of relaying a "Hello World" type response back to the
|
11
|
+
webserver. Notice that we rely on the +autodetection+ facilities of +Dispatcher+ here, by not
|
12
|
+
defining or configuring which webserver interface the script is to utilize.
|
13
|
+
|
14
|
+
require 'dispatcher'
|
15
|
+
|
16
|
+
Dispatcher.dispatch do |request|
|
17
|
+
header 'Status', '200 OK'
|
18
|
+
header 'Content-Type', 'text/plain'
|
19
|
+
|
20
|
+
print 'Hello World'
|
21
|
+
end
|
22
|
+
|
23
|
+
However, in some instances, we may wish to use a webserver that cannot be necessairly
|
24
|
+
+autodetected+, such as a standalone server like +Mongrel+.
|
25
|
+
|
26
|
+
require 'dispatcher/mongrel'
|
27
|
+
|
28
|
+
Dispatcher.dispatch(:port => 8081) do |request|
|
29
|
+
header 'Status', '200 OK'
|
30
|
+
header 'Content-Type', 'text/plain'
|
31
|
+
|
32
|
+
print 'Hello World'
|
33
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
data/TODO.txt
ADDED
File without changes
|
data/lib/dispatch.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'dispatcher/cgi'
|
2
|
+
|
3
|
+
Dispatcher.dispatch do |request|
|
4
|
+
header 'Status', '200 NOT OK'
|
5
|
+
header 'Content-Type', 'text/plain'
|
6
|
+
|
7
|
+
print "#{request.uri}\n\n"
|
8
|
+
|
9
|
+
print request.inspect.gsub(' @', "\n@").gsub('={', "={\n ").gsub('",', "\",\n ").gsub('"}', "\"\n}")
|
10
|
+
=begin
|
11
|
+
print %{<?xml version="1.0" encoding="UTF-8"?>
|
12
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
13
|
+
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
14
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
|
15
|
+
<head>
|
16
|
+
<title>Roar</title>
|
17
|
+
</head>
|
18
|
+
<body>}
|
19
|
+
print "<h2>#{Dispatcher.autodetect}</h2>"
|
20
|
+
print "<h3>Working Directory</h3>\n"
|
21
|
+
print Dir.getwd
|
22
|
+
print "<h3>Environment Variables</h3>\n"
|
23
|
+
print "<table style=\"font-size: 0.75em;\"><tbody>\n"
|
24
|
+
ENV.each do |key, value|
|
25
|
+
print "<tr><td style=\"text-align:right;\"><b>#{key}</b>:</td><td>#{value}</td></tr>\n"
|
26
|
+
end
|
27
|
+
print "</tbody></table>\n"
|
28
|
+
print %{ </body>
|
29
|
+
</html>}
|
30
|
+
=end
|
31
|
+
end
|
data/lib/dispatcher.rb
ADDED
@@ -0,0 +1,306 @@
|
|
1
|
+
module Dispatcher
|
2
|
+
CR = 13.chr
|
3
|
+
LF = 10.chr
|
4
|
+
CRLF = CR + LF
|
5
|
+
|
6
|
+
# Dispatches the specified responder block, given the passed parameters.
|
7
|
+
#
|
8
|
+
# If a subclass of Dispatcher is passed, it will be used as the dispatcher interface. Otherwise,
|
9
|
+
# the last-defined interface will be used. Failing that, the functian will appempt to autodetect
|
10
|
+
# an appropriate interface.
|
11
|
+
#
|
12
|
+
# Additionally, any number of named parameters (:param => value) may be passed, and will be
|
13
|
+
# considered configuration values for the given dispatcher interface.
|
14
|
+
def self::dispatch(*params, &responder)
|
15
|
+
dispatcher = self.default_dispatcher
|
16
|
+
config = Hash.new
|
17
|
+
|
18
|
+
params.each do |param|
|
19
|
+
dispatcher = param if param.kind_of? CoreDispatcher
|
20
|
+
config = param if param.kind_of? Hash
|
21
|
+
end
|
22
|
+
|
23
|
+
# Autodetect if don't have a valid dispatcher
|
24
|
+
dispatcher = self.autodetect if not dispatcher
|
25
|
+
|
26
|
+
# create our Dispatcher, and start listening for requests
|
27
|
+
dispatcher = dispatcher.new(responder, config)
|
28
|
+
dispatcher.listen
|
29
|
+
end
|
30
|
+
|
31
|
+
# Attempts to autodetects an appropriate dispatcher interface depending on the current
|
32
|
+
# configuration. If an appropriate interface is found, it will be loaded, and the appropriate
|
33
|
+
# Dispatcher subclass will be returned. (CoreDispatcher by default)
|
34
|
+
def self.autodetect
|
35
|
+
# Check for CGI
|
36
|
+
if ENV.has_key? 'GATEWAY_INTERFACE' and ENV['GATEWAY_INTERFACE'][0,4].upcase == 'CGI/'
|
37
|
+
require 'dispatcher/cgi'
|
38
|
+
return CGIDispatcher
|
39
|
+
|
40
|
+
# Check for CLI
|
41
|
+
elsif ENV.has_key? 'PROMPT'
|
42
|
+
require 'dispatcher/cli'
|
43
|
+
return CLIDispatcher
|
44
|
+
|
45
|
+
# By default, we turn to the CoreDispatcher
|
46
|
+
else
|
47
|
+
return CoreDispatcher
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# A core Dispatcher interface.
|
52
|
+
#
|
53
|
+
# This is intended as a 'virtual' class that all other dispatcher interfaces inherit from.
|
54
|
+
class CoreDispatcher
|
55
|
+
# Creates a new
|
56
|
+
def initialize(responder, *config)
|
57
|
+
@responder = responder
|
58
|
+
@config = config
|
59
|
+
end
|
60
|
+
|
61
|
+
# Start listening for incoming requests.
|
62
|
+
def listen
|
63
|
+
err = 'You are attempting to use the default Dispatcher interface. '
|
64
|
+
err += 'Most likely the library was unable to autodetect an appropriate interface. '
|
65
|
+
err += 'Try specifying one manually via a require \'request/<interface>\'.'
|
66
|
+
|
67
|
+
raise TypeError, err, caller
|
68
|
+
end
|
69
|
+
|
70
|
+
# Super-simple handler.
|
71
|
+
#
|
72
|
+
# Just outputs any STDOUT content. Buffering should be done at the Responder level.
|
73
|
+
def handle(request)
|
74
|
+
response = Response.new(@responder)
|
75
|
+
response.render(request)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Set the default_dispatcher to the last class that inherits CoreDispatcher.
|
79
|
+
def self.inherited(subclass)
|
80
|
+
Dispatcher.default_dispatcher = subclass
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# A HTTP request.
|
85
|
+
class Request
|
86
|
+
# Any HTTP headers passed along with the request
|
87
|
+
attr_accessor :headers
|
88
|
+
|
89
|
+
# The body of the request, if given.
|
90
|
+
attr_accessor :body
|
91
|
+
|
92
|
+
# A full set of CGI environment values
|
93
|
+
attr_accessor :values
|
94
|
+
|
95
|
+
# Every environment variable passed from the server
|
96
|
+
attr_accessor :env
|
97
|
+
|
98
|
+
# Generates a new request.
|
99
|
+
#
|
100
|
+
# Any values passed will be set to the appropriate attributes.
|
101
|
+
def initialize(*vals)
|
102
|
+
@headers = HTTPHeaderHash.new
|
103
|
+
@env = Hash.new
|
104
|
+
@values = Hash.new
|
105
|
+
|
106
|
+
vals.each do |key, value|
|
107
|
+
instance_variable_set(key, value)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# The current request method.
|
112
|
+
def method
|
113
|
+
@values['REQUEST_METHOD']
|
114
|
+
end
|
115
|
+
|
116
|
+
# The requested path
|
117
|
+
def path
|
118
|
+
@values['PATH_INFO']
|
119
|
+
end
|
120
|
+
|
121
|
+
# The fully qualified URI of the request
|
122
|
+
def uri
|
123
|
+
# protocol
|
124
|
+
result = 'http'
|
125
|
+
if @values['HTTPS'] == 'on'
|
126
|
+
result += 's'
|
127
|
+
end
|
128
|
+
|
129
|
+
# server/port
|
130
|
+
result += "://#{@values['HTTP_HOST']}"
|
131
|
+
|
132
|
+
# path
|
133
|
+
result += @values['PATH_INFO']
|
134
|
+
|
135
|
+
# query
|
136
|
+
if @values['QUERY_STRING'] != ''
|
137
|
+
result += "?#{@values['QUERY_STRING']}"
|
138
|
+
end
|
139
|
+
|
140
|
+
return result
|
141
|
+
end
|
142
|
+
alias_method :url, :uri
|
143
|
+
end
|
144
|
+
|
145
|
+
# A response object embodies a single HTTP response to the Dispatcher.
|
146
|
+
class Response
|
147
|
+
attr_reader :body, :headers
|
148
|
+
attr_accessor :body_start
|
149
|
+
|
150
|
+
# Defines a new response
|
151
|
+
#
|
152
|
+
# The responder is a Proc object, that when run, output the HTTP response, via $stdout and any
|
153
|
+
# helper functions defined in this class, such as header().
|
154
|
+
#
|
155
|
+
# If buffered is true, the responder method will be completely processed before any output is
|
156
|
+
# rendered or returned.
|
157
|
+
def initialize(responder, buffered = false)
|
158
|
+
@headers = HTTPHeaderHash.new
|
159
|
+
@body = ''
|
160
|
+
|
161
|
+
@body_start = false
|
162
|
+
@buffered = buffered
|
163
|
+
|
164
|
+
(class <<self; self; end).send(:define_method, :responder, responder)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Sets a header for the response.
|
168
|
+
#
|
169
|
+
# If the response is currently unbuffered, the header will be rendered once $stdout is written
|
170
|
+
# to, otherwise it will be rendered at the end of buffering (or the end of the responder).
|
171
|
+
#
|
172
|
+
# Please note that if a header is sent after content is already rendered, an IOError will be
|
173
|
+
# thrown.
|
174
|
+
def header(field, value)
|
175
|
+
if @body_start
|
176
|
+
raise IOError, 'Attempt to output headers after the response body has started.', caller
|
177
|
+
end
|
178
|
+
|
179
|
+
@headers[field] = value
|
180
|
+
end
|
181
|
+
|
182
|
+
# Buffers a block's output to $stdout.
|
183
|
+
#
|
184
|
+
# The method will return the buffered output if passback is true.
|
185
|
+
def buffer(passback = false)
|
186
|
+
old_out = $stdout
|
187
|
+
$stdout = $stdout.clone
|
188
|
+
class <<$stdout
|
189
|
+
attr_accessor :buffer
|
190
|
+
|
191
|
+
def write(value)
|
192
|
+
@buffer << value
|
193
|
+
end
|
194
|
+
end
|
195
|
+
$stdout.buffer = ''
|
196
|
+
|
197
|
+
yield
|
198
|
+
|
199
|
+
result = $stdout.buffer
|
200
|
+
$stdout = old_out
|
201
|
+
|
202
|
+
if passback
|
203
|
+
return result
|
204
|
+
|
205
|
+
else
|
206
|
+
# if this is the first render of the body, we need to print out any headers
|
207
|
+
if not @body_start
|
208
|
+
@body_start = true
|
209
|
+
|
210
|
+
@headers.each do |field, value|
|
211
|
+
print field + ': ' + value + CRLF
|
212
|
+
end
|
213
|
+
print CRLF
|
214
|
+
end
|
215
|
+
|
216
|
+
print result
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Renders the request
|
221
|
+
#
|
222
|
+
# If the response was designated as a buffered response at creation, the headers and body may
|
223
|
+
# be retrieved from the object (no output will be rendered by this method).
|
224
|
+
#
|
225
|
+
# If the response is not buffered, the body and header attributes must be ignored.
|
226
|
+
def render(request)
|
227
|
+
if @buffered
|
228
|
+
@body = buffer(true) do
|
229
|
+
responder(request)
|
230
|
+
end
|
231
|
+
else
|
232
|
+
# intercept the first chunk of output so we can detect header usage
|
233
|
+
old_out = $stdout
|
234
|
+
$stdout = $stdout.clone
|
235
|
+
class <<$stdout
|
236
|
+
attr_accessor :response
|
237
|
+
|
238
|
+
def write(val)
|
239
|
+
if not @ignore and not @response.body_start
|
240
|
+
@response.body_start = true
|
241
|
+
|
242
|
+
@response.headers.each do |field, value|
|
243
|
+
print field + ': ' + value + CRLF
|
244
|
+
end
|
245
|
+
|
246
|
+
print CRLF
|
247
|
+
end
|
248
|
+
|
249
|
+
super(val)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
$stdout.response = self
|
253
|
+
|
254
|
+
responder(request)
|
255
|
+
print ''
|
256
|
+
|
257
|
+
$stdout = old_out
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# Defines a Hash of HTTP headers.
|
263
|
+
#
|
264
|
+
# Keys are case-insensitive, and converted to 'pretty' keys
|
265
|
+
# e.g. 'CoNtEnT-TYPE' would be 'Content-Type'
|
266
|
+
#
|
267
|
+
# This class <b>does not</b> parse for valid HTTP header fields, so you may recieve
|
268
|
+
# errors from your webserver if a malformed header is passed.
|
269
|
+
class HTTPHeaderHash < Hash
|
270
|
+
def initialize(*values)
|
271
|
+
values.each do |field, value|
|
272
|
+
self[field] = value
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# Converts the given key string into a 'pretty' HTTP header field
|
277
|
+
def self.pretty_field(field)
|
278
|
+
chunks = field.gsub('_', '-').split('-')
|
279
|
+
|
280
|
+
chunks.collect! do |val|
|
281
|
+
val.capitalize
|
282
|
+
end
|
283
|
+
|
284
|
+
return chunks.join('-')
|
285
|
+
end
|
286
|
+
|
287
|
+
# Sets the key to value.
|
288
|
+
def []=(field, value)
|
289
|
+
super(HTTPHeaderHash.pretty_field(field.to_s), value.to_s)
|
290
|
+
end
|
291
|
+
|
292
|
+
# Returns the value stored for key.
|
293
|
+
def [](field)
|
294
|
+
super(HTTPHeaderHash.pretty_field(field.to_s))
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Typically, this returns the last-defined subclass of CoreDispatcher.
|
299
|
+
def self.default_dispatcher
|
300
|
+
@default_dispatcher
|
301
|
+
end
|
302
|
+
# This is automatically set when a class inherits from CoreDispatcher.
|
303
|
+
def self.default_dispatcher=(dispatcher)
|
304
|
+
@default_dispatcher = dispatcher
|
305
|
+
end
|
306
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'dispatcher'
|
2
|
+
|
3
|
+
|
4
|
+
module Dispatcher
|
5
|
+
# CGI Dispatcher
|
6
|
+
#
|
7
|
+
# Manages dispatching to CGI interfaces.
|
8
|
+
class CGIDispatcher < CoreDispatcher
|
9
|
+
# Immediately processes the request for the values given in ENV.
|
10
|
+
def listen
|
11
|
+
# set up our request
|
12
|
+
request = generate_request
|
13
|
+
|
14
|
+
# and handle it
|
15
|
+
handle(request)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Generates a reques object from the current values of ENV
|
19
|
+
def generate_request
|
20
|
+
request = Request.new
|
21
|
+
|
22
|
+
# Copy information out of the environment
|
23
|
+
ENV.each do |key, value|
|
24
|
+
request.env[key] = value
|
25
|
+
|
26
|
+
if key[0,5].upcase == 'HTTP_'
|
27
|
+
request.headers[key[5, key.length - 5]] = value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Set up the CGI value Hash
|
32
|
+
request.values = build_cgi_environment(request.env)
|
33
|
+
|
34
|
+
return request
|
35
|
+
end
|
36
|
+
|
37
|
+
#:nodoc:
|
38
|
+
# List of environment values used in the CGI environment.
|
39
|
+
# We use Hash keys to avoid O(n^2) type behavior.
|
40
|
+
CGI_VARS = {
|
41
|
+
'AUTH_TYPE' => true,
|
42
|
+
'CONTENT_LENGTH' => true,
|
43
|
+
'CONTENT_TYPE' => true,
|
44
|
+
'GATEWAY_INTERFACE' => true,
|
45
|
+
'HTTPS' => true, # non-spec
|
46
|
+
'PATH_INFO' => true,
|
47
|
+
'PATH_TRANSLATED' => true,
|
48
|
+
'QUERY_STRING' => true,
|
49
|
+
'REMOTE_ADDR' => true,
|
50
|
+
'REMOTE_HOST' => true,
|
51
|
+
'REMOTE_IDENT' => true,
|
52
|
+
'REMOTE_USER' => true,
|
53
|
+
'REQUEST_METHOD' => true,
|
54
|
+
'SCRIPT_NAME' => true,
|
55
|
+
'SERVER_NAME' => true,
|
56
|
+
'SERVER_PORT' => true,
|
57
|
+
'SERVER_PROTOCOL' => true,
|
58
|
+
'SERVER_SOFTWARE' => true,
|
59
|
+
}
|
60
|
+
|
61
|
+
# Generates a hash of a CGI environment from values in the given hash.
|
62
|
+
def build_cgi_environment(env)
|
63
|
+
cgi_env = Hash.new
|
64
|
+
|
65
|
+
env.each do |key, value|
|
66
|
+
if key[0,5].upcase == 'HTTP_' or CGI_VARS[key.upcase]
|
67
|
+
cgi_env[key] = value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
return cgi_env
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'dispatcher'
|
2
|
+
require 'getoptlong'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module Dispatcher
|
6
|
+
# Command line Dispatcher interface.
|
7
|
+
#
|
8
|
+
# The CLI interface allows for easy command line debugging of Responders.
|
9
|
+
#
|
10
|
+
# Usage: script.rb [URI] [switches]
|
11
|
+
class CLIDispatcher < CoreDispatcher
|
12
|
+
OPTS = GetoptLong.new(
|
13
|
+
['--help', '-h', GetoptLong::NO_ARGUMENT],
|
14
|
+
['--clean', '-c', GetoptLong::NO_ARGUMENT]
|
15
|
+
)
|
16
|
+
|
17
|
+
# Sets up the request, and handles the response.
|
18
|
+
def listen
|
19
|
+
# read the options into a Hash
|
20
|
+
options = Hash.new
|
21
|
+
OPTS.quiet = true # shut it up
|
22
|
+
|
23
|
+
begin
|
24
|
+
OPTS.each do |opt, value|
|
25
|
+
options[opt] = value
|
26
|
+
end
|
27
|
+
# catch invalid option exceptions
|
28
|
+
rescue GetoptLong::InvalidOption => err
|
29
|
+
puts err.to_s + ' (-h will show valid options)'
|
30
|
+
end
|
31
|
+
|
32
|
+
# help?
|
33
|
+
if options.has_key? '--help'
|
34
|
+
display_help
|
35
|
+
|
36
|
+
# otherwise we're going to handle a request
|
37
|
+
else
|
38
|
+
# set up our request
|
39
|
+
request = Request.new
|
40
|
+
|
41
|
+
# and handle it
|
42
|
+
if options.has_key? '--clean'
|
43
|
+
handle_cleaned(request)
|
44
|
+
else
|
45
|
+
handle(request)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Displays a nicely formatted copy of the response
|
51
|
+
def handle_cleaned(request)
|
52
|
+
response = Response.new(@responder, true)
|
53
|
+
response.render(request)
|
54
|
+
body = response.body
|
55
|
+
|
56
|
+
# strip tags
|
57
|
+
body.gsub!(/<\/?[^>]*>/, "")
|
58
|
+
|
59
|
+
print body
|
60
|
+
end
|
61
|
+
|
62
|
+
# Displays the command line help and usage details
|
63
|
+
def display_help
|
64
|
+
print %{
|
65
|
+
Usage: #{File.basename($0)} [URI] [options]
|
66
|
+
|
67
|
+
Options:
|
68
|
+
-c/--clean Cleans up any HTML output for easy screen reading
|
69
|
+
-h/--help Help. You're looking at it
|
70
|
+
|
71
|
+
Further Information:
|
72
|
+
http://dispatcher.rubyforge.org
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: dispatcher
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2006-10-08 00:00:00 -05:00
|
8
|
+
summary: A lightweight HTTP dispatch interface between Ruby and most webserver configurations.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: imacleod@gmail.com
|
12
|
+
homepage: http://dispatcher.rubyforge.org/
|
13
|
+
rubyforge_project: dispatcher
|
14
|
+
description: Dispatcher provides a simple and consistent interface between Ruby and most webserver configurations. It is typically used in conjunction with a request-building system, such as the Responder gem.
|
15
|
+
autorequire: dispatcher
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Ian MacLeod
|
31
|
+
files:
|
32
|
+
- lib/dispatch.rb
|
33
|
+
- lib/dispatcher.rb
|
34
|
+
- lib/dispatcher/cgi.rb
|
35
|
+
- lib/dispatcher/cli.rb
|
36
|
+
- SyntaxCheck/lib/dispatcher.rb
|
37
|
+
- SyntaxCheck/lib/dispatcher/cgi.rb
|
38
|
+
- SyntaxCheck/lib/dispatcher/cli.rb
|
39
|
+
- LICENSE.txt
|
40
|
+
- README.txt
|
41
|
+
- TODO.txt
|
42
|
+
test_files: []
|
43
|
+
|
44
|
+
rdoc_options:
|
45
|
+
- --title
|
46
|
+
- Dispatcher
|
47
|
+
- --main
|
48
|
+
- README.txt
|
49
|
+
extra_rdoc_files:
|
50
|
+
- LICENSE.txt
|
51
|
+
- README.txt
|
52
|
+
- TODO.txt
|
53
|
+
executables: []
|
54
|
+
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
dependencies: []
|
60
|
+
|