dispatcher 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|