patchbay 0.0.1.pre2 → 0.0.1.pre3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/patchbay.rb +150 -1
  2. metadata +59 -27
@@ -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
- class Router
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
- version: 0.0.1.pre2
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
- date: 2011-09-13 00:00:00.000000000Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
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
- requirement: &14329380 !ruby/object:Gem::Requirement
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
- prerelease: false
24
- version_requirements: *14329380
25
- description: ! "Patchbay is the web framework for non-web apps. \nIt's designed for
26
- simplicity, minimalism, and easy integration.\n"
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
- files:
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
- require_paths:
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
- version: '0'
45
- required_rubygems_version: !ruby/object:Gem::Requirement
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.8.6
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
+