patchbay 0.0.1.pre2 → 0.0.1.pre3
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/lib/patchbay.rb +150 -1
- metadata +59 -27
data/lib/patchbay.rb
CHANGED
@@ -25,15 +25,42 @@ EOT
|
|
25
25
|
</body></html>
|
26
26
|
EOT
|
27
27
|
|
28
|
+
private
|
29
|
+
# Checks if the `path_to_test` falls within the `root_path`.
|
30
|
+
# This can be used to test for directory-traversal attacks.
|
31
|
+
#
|
32
|
+
# == Parameters:
|
33
|
+
# path_to_test::
|
34
|
+
# A path which is to be checked against the root path.
|
35
|
+
#
|
36
|
+
# root_path::
|
37
|
+
# A path that `path_to_test` will be checked against.
|
38
|
+
#
|
39
|
+
# == Returns:
|
40
|
+
# `true` if `path_to_test` resides within `root_path`.
|
41
|
+
# `false` otherwise.
|
42
|
+
#
|
28
43
|
def path_is_subdir_of(path_to_test, root_path)
|
29
44
|
File.fnmatch(File.join(root_path, '**'), File.realpath(path_to_test))
|
30
45
|
end
|
31
46
|
|
32
|
-
|
47
|
+
# Parses URLs and matches them to handlers.
|
48
|
+
class Router
|
33
49
|
def initialize
|
34
50
|
@routes = []
|
35
51
|
end
|
36
52
|
|
53
|
+
private
|
54
|
+
|
55
|
+
# Check if the given route data matches the split-up URL.
|
56
|
+
#
|
57
|
+
# == Parameters:
|
58
|
+
# route::
|
59
|
+
# An object that responds to `handler`, `url_parts`, and `verb`.
|
60
|
+
#
|
61
|
+
# url_parts::
|
62
|
+
# An array of Strings corresponding to a split URL.
|
63
|
+
#
|
37
64
|
def matches?(route, url_parts)
|
38
65
|
if route.url_parts.size != url_parts.size
|
39
66
|
return false
|
@@ -57,6 +84,20 @@ EOT
|
|
57
84
|
return params
|
58
85
|
end
|
59
86
|
|
87
|
+
public
|
88
|
+
|
89
|
+
# Find a handler matching an HTTP request.
|
90
|
+
#
|
91
|
+
# == Parameters:
|
92
|
+
# verb::
|
93
|
+
# The HTTP verb (GET, POST etc)
|
94
|
+
# url::
|
95
|
+
# The requested URL
|
96
|
+
#
|
97
|
+
# == Returns:
|
98
|
+
# A 2-element array consisting of the handler
|
99
|
+
# followed by a hash of parameters extracted from the URL.
|
100
|
+
#
|
60
101
|
def match(verb, url)
|
61
102
|
parts = url.gsub(/^\/+/,'').split('/')
|
62
103
|
|
@@ -72,6 +113,16 @@ EOT
|
|
72
113
|
return [ nil, nil ]
|
73
114
|
end
|
74
115
|
|
116
|
+
# Add a route matching a certain URL pattern and verb.
|
117
|
+
#
|
118
|
+
# == Parameters:
|
119
|
+
# verb::
|
120
|
+
# The HTTP verb (GET, POST etc)
|
121
|
+
# route::
|
122
|
+
# The URL template to match.
|
123
|
+
# This may contain symbols of the form `:xxxx`;
|
124
|
+
# these represent variable parameters to be extracted from the URL
|
125
|
+
# when it is matched.
|
75
126
|
def add(verb, route, handler)
|
76
127
|
parts = route.gsub(/^\/+/,'').split('/')
|
77
128
|
routeObj = Route.new
|
@@ -82,6 +133,7 @@ EOT
|
|
82
133
|
end
|
83
134
|
end
|
84
135
|
|
136
|
+
protected
|
85
137
|
def self.router
|
86
138
|
@router ||= Router.new
|
87
139
|
@router
|
@@ -91,42 +143,107 @@ EOT
|
|
91
143
|
self.class.router
|
92
144
|
end
|
93
145
|
|
146
|
+
public
|
147
|
+
# Set up a handler for GET requests matching a given route.
|
148
|
+
#
|
149
|
+
# == Parameters:
|
150
|
+
# route:
|
151
|
+
# Route pattern to match (see Patchbay::Router::add)
|
152
|
+
# action:
|
153
|
+
# Block to be executed to fulfill the request
|
154
|
+
#
|
94
155
|
def self.get(route, &action)
|
95
156
|
router.add('GET', route, action)
|
96
157
|
end
|
97
158
|
|
159
|
+
# Set up a handler for PUT requests matching a given route.
|
160
|
+
#
|
161
|
+
# == Parameters:
|
162
|
+
# route:
|
163
|
+
# Route pattern to match (see Patchbay::Router::add)
|
164
|
+
# action:
|
165
|
+
# Block to be executed to fulfill the request
|
166
|
+
#
|
98
167
|
def self.put(route, &action)
|
99
168
|
router.add('PUT', route, action)
|
100
169
|
end
|
101
170
|
|
171
|
+
# Set up a handler for POST requests matching a given route.
|
172
|
+
#
|
173
|
+
# == Parameters:
|
174
|
+
# route:
|
175
|
+
# Route pattern to match (see Patchbay::Router::add)
|
176
|
+
# action:
|
177
|
+
# Block to be executed to fulfill the request
|
178
|
+
#
|
102
179
|
def self.post(route, &action)
|
103
180
|
router.add('POST', route, action)
|
104
181
|
end
|
105
182
|
|
183
|
+
# Set up a handler for DELETE requests matching a given route.
|
184
|
+
#
|
185
|
+
# == Parameters:
|
186
|
+
# route:
|
187
|
+
# Route pattern to match (see Patchbay::Router::add)
|
188
|
+
# action:
|
189
|
+
# Block to be executed to fulfill the request
|
190
|
+
#
|
106
191
|
def self.delete(route, &action)
|
107
192
|
router.add('DELETE', route, action)
|
108
193
|
end
|
109
194
|
|
195
|
+
# Get directory from which static files are being served
|
196
|
+
#
|
197
|
+
# == Returns:
|
198
|
+
# The absolute path to the directory from which static files
|
199
|
+
# may be served.
|
200
|
+
#
|
110
201
|
def self.files_dir
|
111
202
|
@files_dir
|
112
203
|
end
|
113
204
|
|
205
|
+
# Set directory from which static files may be served
|
206
|
+
#
|
207
|
+
# == Parameters:
|
208
|
+
# new_dir:
|
209
|
+
# Path (relative or absolute) from which static files may
|
210
|
+
# be served.
|
114
211
|
def self.files_dir=(new_dir)
|
115
212
|
@files_dir = File.realpath(new_dir)
|
116
213
|
end
|
117
214
|
|
215
|
+
# Convenience function for accessing files_dir without writing
|
216
|
+
# self.class.files_dir
|
217
|
+
#
|
118
218
|
def files_dir
|
119
219
|
self.class.files_dir
|
120
220
|
end
|
121
221
|
|
222
|
+
private
|
223
|
+
# Get the Rack environment corresponding to the current request.
|
224
|
+
#
|
225
|
+
# == Returns:
|
226
|
+
# The Rack environment corresponding to the ongoing request.
|
227
|
+
#
|
122
228
|
def environment
|
123
229
|
@environment
|
124
230
|
end
|
125
231
|
|
232
|
+
# Get the parameters parsed from the request URL.
|
233
|
+
#
|
234
|
+
# == Returns:
|
235
|
+
# A Hash-like object representing the passed parameters.
|
236
|
+
#
|
126
237
|
def params
|
127
238
|
@params
|
128
239
|
end
|
129
240
|
|
241
|
+
# Handle requests that match no route, when we have a public files directory.
|
242
|
+
# Sets up the response appropriately for that case.
|
243
|
+
#
|
244
|
+
# == Returns:
|
245
|
+
# None.
|
246
|
+
#
|
130
247
|
def handle_file
|
131
248
|
url_parts = environment['PATH_INFO'].split('/')
|
132
249
|
file_path = File.join(files_dir, url_parts)
|
@@ -144,11 +261,19 @@ EOT
|
|
144
261
|
end
|
145
262
|
end
|
146
263
|
|
264
|
+
# Set up the response to send a file once its absolute path is known.
|
265
|
+
#
|
147
266
|
def send_file(file_path)
|
148
267
|
mime_type = Rack::Mime.mime_type(File.extname(file_path))
|
149
268
|
@response = [200, { "Content-Type" => mime_type }, File.new(file_path)]
|
150
269
|
end
|
151
270
|
|
271
|
+
public
|
272
|
+
# Rack callable interface.
|
273
|
+
#
|
274
|
+
# == Returns:
|
275
|
+
# A 3-element array of [status, response_headers, body].
|
276
|
+
#
|
152
277
|
def call(env)
|
153
278
|
# find a handler for this request
|
154
279
|
handler, route_params = router.match(env['REQUEST_METHOD'], env['PATH_INFO'])
|
@@ -180,6 +305,16 @@ EOT
|
|
180
305
|
@response
|
181
306
|
end
|
182
307
|
|
308
|
+
private
|
309
|
+
# Set response content and type.
|
310
|
+
#
|
311
|
+
# == Parameters:
|
312
|
+
# options::
|
313
|
+
# A Hash of parameters.
|
314
|
+
# `:error => 404`: set response error code
|
315
|
+
# `:html => '<html>...</html>'`, `:json => '{...}'` etc: set response content and content type.
|
316
|
+
# Content type is guessed automatically.
|
317
|
+
#
|
183
318
|
def render(options={})
|
184
319
|
if @response
|
185
320
|
fail "can only render once per request"
|
@@ -197,20 +332,32 @@ EOT
|
|
197
332
|
end
|
198
333
|
end
|
199
334
|
|
335
|
+
# Set up response for 404 (route/page not found) error.
|
336
|
+
#
|
200
337
|
def handle_no_route
|
201
338
|
@response = [404, { "Content-Type" => "text/html" }, [NoRoute_Message]]
|
202
339
|
end
|
203
340
|
|
341
|
+
# Handle exceptions occurring during route-handler execution.
|
342
|
+
#
|
204
343
|
def handle_exception(e)
|
205
344
|
@response = [500, { "Content-Type" => "text/html" }, [Exception_Message]]
|
206
345
|
$stderr.puts e.inspect
|
207
346
|
e.backtrace.each { |bt| $stderr.puts bt }
|
208
347
|
end
|
209
348
|
|
349
|
+
# Set up response when permission was denied attempting to read a local file.
|
350
|
+
#
|
210
351
|
def handle_forbidden
|
211
352
|
@response = [403, { "Content-Type" => "text/html" }, [Forbidden_Message]]
|
212
353
|
end
|
213
354
|
|
355
|
+
public
|
356
|
+
# Start an appropriate server to run the application.
|
357
|
+
#
|
358
|
+
# == Parameters:
|
359
|
+
# options::
|
360
|
+
# Options hash to be passed to the Rack handler.
|
214
361
|
def run(options={})
|
215
362
|
handler = find_rack_handler
|
216
363
|
handler_name = handler.name.gsub(/.*::/,'')
|
@@ -219,6 +366,8 @@ EOT
|
|
219
366
|
end
|
220
367
|
|
221
368
|
private
|
369
|
+
# Search through installed Rack handlers for ones we like
|
370
|
+
#
|
222
371
|
def find_rack_handler
|
223
372
|
servers = %w/thin mongrel webrick/
|
224
373
|
servers.each do |server|
|
metadata
CHANGED
@@ -1,57 +1,89 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: patchbay
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 3457254002499246909
|
5
5
|
prerelease: 6
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
- pre
|
11
|
+
- 3
|
12
|
+
version: 0.0.1.pre3
|
6
13
|
platform: ruby
|
7
|
-
authors:
|
14
|
+
authors:
|
8
15
|
- Andrew Armenia
|
9
16
|
autorequire:
|
10
17
|
bindir: bin
|
11
18
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
19
|
+
|
20
|
+
date: 2011-09-13 00:00:00 -04:00
|
21
|
+
default_executable:
|
22
|
+
dependencies:
|
23
|
+
- !ruby/object:Gem::Dependency
|
15
24
|
name: rack
|
16
|
-
|
25
|
+
prerelease: false
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
17
27
|
none: false
|
18
|
-
requirements:
|
19
|
-
- -
|
20
|
-
- !ruby/object:Gem::Version
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
hash: 27
|
32
|
+
segments:
|
33
|
+
- 1
|
34
|
+
- 3
|
35
|
+
- 0
|
21
36
|
version: 1.3.0
|
22
37
|
type: :runtime
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
simplicity, minimalism, and easy integration
|
38
|
+
version_requirements: *id001
|
39
|
+
description: |
|
40
|
+
Patchbay is the web framework for non-web apps.
|
41
|
+
It's designed for simplicity, minimalism, and easy integration.
|
42
|
+
|
27
43
|
email: andrew@asquaredlabs.com
|
28
44
|
executables: []
|
45
|
+
|
29
46
|
extensions: []
|
47
|
+
|
30
48
|
extra_rdoc_files: []
|
31
|
-
|
49
|
+
|
50
|
+
files:
|
32
51
|
- lib/patchbay.rb
|
52
|
+
has_rdoc: true
|
33
53
|
homepage: http://rubygems.org/gems/patchbay
|
34
54
|
licenses: []
|
55
|
+
|
35
56
|
post_install_message:
|
36
57
|
rdoc_options: []
|
37
|
-
|
58
|
+
|
59
|
+
require_paths:
|
38
60
|
- lib
|
39
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
62
|
none: false
|
41
|
-
requirements:
|
42
|
-
- -
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
|
45
|
-
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
hash: 3
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
71
|
none: false
|
47
|
-
requirements:
|
48
|
-
- -
|
49
|
-
- !ruby/object:Gem::Version
|
72
|
+
requirements:
|
73
|
+
- - ">"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 25
|
76
|
+
segments:
|
77
|
+
- 1
|
78
|
+
- 3
|
79
|
+
- 1
|
50
80
|
version: 1.3.1
|
51
81
|
requirements: []
|
82
|
+
|
52
83
|
rubyforge_project:
|
53
|
-
rubygems_version: 1.
|
84
|
+
rubygems_version: 1.5.2
|
54
85
|
signing_key:
|
55
86
|
specification_version: 3
|
56
87
|
summary: Embed HTTP APIs in non-web apps easily
|
57
88
|
test_files: []
|
89
|
+
|