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.
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
+