halcyon 0.3.22 → 0.4.0
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/Rakefile +3 -3
- data/bin/halcyon +11 -5
- data/lib/halcyon.rb +12 -6
- data/lib/halcyon/client/base.rb +26 -19
- data/lib/halcyon/client/exceptions.rb +1 -1
- data/lib/halcyon/exceptions.rb +1 -0
- data/lib/halcyon/server/auth/basic.rb +107 -0
- data/lib/halcyon/server/base.rb +86 -29
- data/lib/halcyon/server/exceptions.rb +1 -1
- data/lib/halcyon/server/router.rb +1 -1
- data/spec/halcyon/error_spec.rb +5 -5
- data/spec/halcyon/router_spec.rb +8 -8
- data/spec/halcyon/server_spec.rb +29 -17
- metadata +4 -6
- data/lib/halcyon/support/hashext.rb +0 -59
- data/spec/SPEC +0 -29
- data/spec/SPEC.html +0 -237
data/Rakefile
CHANGED
@@ -78,17 +78,17 @@ end
|
|
78
78
|
namespace 'spec' do
|
79
79
|
desc "generate spec"
|
80
80
|
task :gen do
|
81
|
-
sh "
|
81
|
+
sh "bacon -r~/lib/bacon/output -rlib/halcyon -rtest/spec_helper spec/**/* -s > spec/SPEC"
|
82
82
|
end
|
83
83
|
|
84
84
|
desc "run rspec"
|
85
85
|
task :run do
|
86
|
-
sh "
|
86
|
+
sh "bacon -r~/lib/bacon/output -rlib/halcyon -rspec/spec_helper spec/**/* -o CTestUnit"
|
87
87
|
end
|
88
88
|
|
89
89
|
desc "run rspec verbosely"
|
90
90
|
task :verb do
|
91
|
-
sh "
|
91
|
+
sh "bacon -r~/lib/bacon/output -rlib/halcyon -rspec/spec_helper spec/**/* -o CSpecDox"
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
data/bin/halcyon
CHANGED
@@ -146,17 +146,22 @@ if !File.exists?("#{options[:app]}.rb")
|
|
146
146
|
abort "Halcyon did not find the app #{options[:app]}. Check your path and try again."
|
147
147
|
end
|
148
148
|
|
149
|
+
require options[:app]
|
150
|
+
appname = File.basename(options[:app]).capitalize.gsub(/_([a-z])/){|m|m[1].chr.capitalize}
|
149
151
|
begin
|
150
|
-
|
151
|
-
app = Object.const_get(File.basename(options[:app]).capitalize.gsub(/_([a-z])/){|m|m[1].chr.capitalize})
|
152
|
+
app = Object.const_get(appname)
|
152
153
|
rescue NameError => e
|
153
|
-
abort "Unable to load #{
|
154
|
+
abort "Unable to load #{appname}. Please ensure your server is so named."
|
154
155
|
end
|
155
156
|
|
156
157
|
#--
|
157
158
|
# prepare server
|
158
159
|
#++
|
159
160
|
begin
|
161
|
+
# attempt to require the server
|
162
|
+
begin; require options[:server].capitalize; rescue LoadError; end
|
163
|
+
|
164
|
+
# get the appropriate Rack Handler
|
160
165
|
server = Rack::Handler.const_get(options[:server].capitalize)
|
161
166
|
rescue NameError
|
162
167
|
servers = {
|
@@ -164,7 +169,8 @@ rescue NameError
|
|
164
169
|
'fastcgi' => 'FastCGI',
|
165
170
|
'lsws' => 'LSWS',
|
166
171
|
'mongrel' => 'Mongrel',
|
167
|
-
'webrick' => 'WEBrick'
|
172
|
+
'webrick' => 'WEBrick',
|
173
|
+
'thin' => 'Thin'
|
168
174
|
}
|
169
175
|
abort "Unsupported server (missing Rack Handler). Did you mean to specify #{options[:server]}?" unless servers.key? options[:server]
|
170
176
|
server = Rack::Handler.const_get(servers[options[:server]])
|
@@ -196,4 +202,4 @@ end
|
|
196
202
|
# start server
|
197
203
|
#++
|
198
204
|
|
199
|
-
server.run app, :Port => options[:port]
|
205
|
+
server.run app, :Port => Integer(options[:port])
|
data/lib/halcyon.rb
CHANGED
@@ -10,18 +10,14 @@ $:.unshift File.dirname(__FILE__)
|
|
10
10
|
# dependencies
|
11
11
|
#++
|
12
12
|
|
13
|
-
%w(
|
14
|
-
|
15
|
-
class Hash
|
16
|
-
include HashExt::Keys
|
17
|
-
end
|
13
|
+
%w(rubygems merb/core_ext).each {|dep|require dep}
|
18
14
|
|
19
15
|
#--
|
20
16
|
# module
|
21
17
|
#++
|
22
18
|
|
23
19
|
module Halcyon
|
24
|
-
VERSION = [0,
|
20
|
+
VERSION = [0,4,0]
|
25
21
|
def self.version
|
26
22
|
VERSION.join('.')
|
27
23
|
end
|
@@ -43,6 +39,16 @@ module Halcyon
|
|
43
39
|
abort "READ THE DAMNED RDOCS!"
|
44
40
|
end
|
45
41
|
|
42
|
+
#--
|
43
|
+
# Module Autoloading
|
44
|
+
#++
|
45
|
+
|
46
|
+
class Server
|
47
|
+
module Auth
|
48
|
+
autoload :Basic, 'halcyon/server/auth/basic'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
46
52
|
end
|
47
53
|
|
48
54
|
%w(halcyon/exceptions).each {|dep|require dep}
|
data/lib/halcyon/client/base.rb
CHANGED
@@ -176,37 +176,29 @@ module Halcyon
|
|
176
176
|
#++
|
177
177
|
|
178
178
|
# Performs a GET request on the URI specified.
|
179
|
-
def get(uri)
|
179
|
+
def get(uri, headers={})
|
180
180
|
req = Net::HTTP::Get.new(uri)
|
181
|
-
req
|
182
|
-
req["User-Agent"] = USER_AGENT
|
183
|
-
request(req)
|
181
|
+
request(req, headers)
|
184
182
|
end
|
185
183
|
|
186
184
|
# Performs a POST request on the URI specified.
|
187
|
-
def post(uri, data)
|
185
|
+
def post(uri, data, headers={})
|
188
186
|
req = Net::HTTP::Post.new(uri)
|
189
|
-
req["Content-Type"] = CONTENT_TYPE
|
190
|
-
req["User-Agent"] = USER_AGENT
|
191
187
|
req.body = format_body(data)
|
192
|
-
request(req)
|
188
|
+
request(req, headers)
|
193
189
|
end
|
194
190
|
|
195
191
|
# Performs a DELETE request on the URI specified.
|
196
|
-
def delete(uri)
|
192
|
+
def delete(uri, headers={})
|
197
193
|
req = Net::HTTP::Delete.new(uri)
|
198
|
-
req
|
199
|
-
req["User-Agent"] = USER_AGENT
|
200
|
-
request(req)
|
194
|
+
request(req, headers)
|
201
195
|
end
|
202
196
|
|
203
197
|
# Performs a PUT request on the URI specified.
|
204
|
-
def put(uri, data)
|
198
|
+
def put(uri, data, headers={})
|
205
199
|
req = Net::HTTP::Put.new(uri)
|
206
|
-
req["Content-Type"] = CONTENT_TYPE
|
207
|
-
req["User-Agent"] = USER_AGENT
|
208
200
|
req.body = format_body(data)
|
209
|
-
request(req)
|
201
|
+
request(req, headers)
|
210
202
|
end
|
211
203
|
|
212
204
|
private
|
@@ -223,13 +215,26 @@ module Halcyon
|
|
223
215
|
# (defined in Halcyon::Exceptions) which all inherit from
|
224
216
|
# +Halcyon::Exceptions::Base+. It is up to the client to handle these
|
225
217
|
# exceptions specifically.
|
226
|
-
def request(req)
|
218
|
+
def request(req, headers={})
|
219
|
+
# define essential headers for Halcyon::Server's picky requirements
|
220
|
+
req["Content-Type"] = CONTENT_TYPE
|
221
|
+
req["User-Agent"] = USER_AGENT
|
222
|
+
|
223
|
+
# apply provided headers
|
224
|
+
headers.each do |pair|
|
225
|
+
header, value = pair
|
226
|
+
req[header] = value
|
227
|
+
end
|
228
|
+
|
229
|
+
# provide hook for modifying the headers
|
230
|
+
req = headers(req) if respond_to? :headers
|
231
|
+
|
227
232
|
# prepare and send HTTP request
|
228
233
|
res = Net::HTTP.start(@uri.host, @uri.port) {|http|http.request(req)}
|
229
234
|
|
230
235
|
# parse response
|
231
236
|
body = JSON.parse(res.body)
|
232
|
-
body.symbolize_keys!
|
237
|
+
body.symbolize_keys!
|
233
238
|
|
234
239
|
# handle non-successes
|
235
240
|
raise Halcyon::Client::Base::Exceptions.lookup(body[:status]).new unless res.kind_of? Net::HTTPSuccess
|
@@ -245,7 +250,9 @@ module Halcyon
|
|
245
250
|
# format according to Net::HTTP for sending through as a Hash.
|
246
251
|
def format_body(data)
|
247
252
|
data = {:body => data} unless data.is_a? Hash
|
248
|
-
data.
|
253
|
+
data.symbolize_keys!
|
254
|
+
# uses the Merb Hash#to_params method defined in merb/core_ext.
|
255
|
+
data.to_params
|
249
256
|
end
|
250
257
|
|
251
258
|
end
|
data/lib/halcyon/exceptions.rb
CHANGED
@@ -0,0 +1,107 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Created by Matt Todd on 2008-01-16.
|
4
|
+
# Copyright (c) 2008. All rights reserved.
|
5
|
+
#++
|
6
|
+
|
7
|
+
#--
|
8
|
+
# module
|
9
|
+
#++
|
10
|
+
|
11
|
+
module Halcyon
|
12
|
+
class Server
|
13
|
+
module Auth
|
14
|
+
|
15
|
+
# = Introduction
|
16
|
+
#
|
17
|
+
# The Auth::Basic class provides an alternative to the Server::Base
|
18
|
+
# class for creating servers with HTTP Basic Authentication built in.
|
19
|
+
#
|
20
|
+
# == Usage
|
21
|
+
#
|
22
|
+
# In order to provide for HTTP Basic Authentication in your server,
|
23
|
+
# it would first need to inherit from this class instead of Server::Base
|
24
|
+
# and then provide a method to check for the existence of the credentials
|
25
|
+
# and respond accordingly. This looks like the following:
|
26
|
+
#
|
27
|
+
# class AuthenticatedApp < Halcyon::Server::Auth::Basic
|
28
|
+
# def basic_authorization(username, password)
|
29
|
+
# [username, password] == ['rupert', 'secret']
|
30
|
+
# end
|
31
|
+
# # write normal Halcyon server app here
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# The credentials passed to the +basic_authorization+ method are pulled
|
35
|
+
# from the appropriate Authorization header value and parsed from the
|
36
|
+
# base64 values. If no Authorization header value is passed, an exception
|
37
|
+
# is thrown resulting in the appropriate response to the client.
|
38
|
+
class Basic < Server::Base
|
39
|
+
|
40
|
+
AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION']
|
41
|
+
|
42
|
+
# Determines the appropriate HTTP Authorization header to refer to when
|
43
|
+
# plucking out the header for processing.
|
44
|
+
def authorization_key
|
45
|
+
@authorization_key ||= AUTHORIZATION_KEYS.detect{|k|@env.has_key?(k)}
|
46
|
+
end
|
47
|
+
|
48
|
+
alias :_run :run
|
49
|
+
|
50
|
+
# Ensures that the HTTP Authentication header is included, the Basic
|
51
|
+
# scheme is being used, and the credentials pass the +basic_auth+
|
52
|
+
# test. If any of these fail, an Unauthorized exception is raised
|
53
|
+
# (except for non-Basic schemes), otherwise the +route+ is +run+
|
54
|
+
# normally.
|
55
|
+
#
|
56
|
+
# See the documentation for the +basic_auth+ class method for details
|
57
|
+
# concerning the credentials and action inclusion/exclusion.
|
58
|
+
def run(route)
|
59
|
+
# test credentials if the action is one specified to be tested
|
60
|
+
if ((@@auth[:except].nil? && @@auth[:only].nil?) || # the default is to test if no restrictions
|
61
|
+
(!@@auth[:only].nil? && @@auth[:only].include?(route[:action].to_sym)) || # but if the action is in the :only directive, test
|
62
|
+
(!@@auth[:except].nil? && !@@auth[:except].include?(route[:action].to_sym))) # or if the action is not in the :except directive, test
|
63
|
+
|
64
|
+
# make sure there's an authorization header
|
65
|
+
raise Base::Exceptions::Unauthorized.new unless !authorization_key.nil?
|
66
|
+
|
67
|
+
# make sure the request is via the Basic protocol
|
68
|
+
scheme = @env[authorization_key].split.first.downcase.to_sym
|
69
|
+
raise Base::Exceptions::BadRequest.new unless scheme == :basic
|
70
|
+
|
71
|
+
# make sure the credentials pass the test
|
72
|
+
credentials = @env[authorization_key].split.last.unpack("m*").first.split(':', 2)
|
73
|
+
raise Base::Exceptions::Unauthorized.new unless @@auth[:method].call(*credentials)
|
74
|
+
end
|
75
|
+
|
76
|
+
# success, so run the route normally
|
77
|
+
_run(route)
|
78
|
+
rescue Halcyon::Exceptions::Base => e
|
79
|
+
@logger.warn "#{uri} => #{e.error}"
|
80
|
+
# handles all content error exceptions
|
81
|
+
@res.status = e.status
|
82
|
+
{:status => e.status, :body => e.error}
|
83
|
+
end
|
84
|
+
|
85
|
+
# Provides a way to define a test as well as set limits on what is
|
86
|
+
# tested for Basic Authorization. This method should be called in the
|
87
|
+
# definition of the server. A simple example would look like:
|
88
|
+
#
|
89
|
+
# class Servr < Halcyon::Server::Auth::Basic
|
90
|
+
# basic_auth :only => [:grant] do |user, pass|
|
91
|
+
# # test credentials
|
92
|
+
# end
|
93
|
+
# # routes and actions follow...
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# Two acceptable options include <tt>:only</tt> and <tt>:except</tt>.
|
97
|
+
def self.basic_auth(options={}, &proc)
|
98
|
+
instance_eval do
|
99
|
+
@@auth = options.merge(:method => proc)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/halcyon/server/base.rb
CHANGED
@@ -17,10 +17,11 @@ module Halcyon
|
|
17
17
|
class Server
|
18
18
|
|
19
19
|
DEFAULT_OPTIONS = {
|
20
|
+
:root => Dir.pwd,
|
20
21
|
:environment => 'none',
|
21
22
|
:port => 9267,
|
22
23
|
:host => 'localhost',
|
23
|
-
:server => 'mongrel',
|
24
|
+
:server => Gem.searcher.find('thin').nil? ? 'mongrel' : 'thin',
|
24
25
|
:pid_file => '/var/run/halcyon.{server}.{app}.{port}.pid',
|
25
26
|
:log_file => '/var/log/halcyon.{app}.log',
|
26
27
|
:log_level => 'info',
|
@@ -66,8 +67,8 @@ module Halcyon
|
|
66
67
|
# end
|
67
68
|
#
|
68
69
|
# Two routes are (effectively) defined here, the first being to watch for
|
69
|
-
# all requests in the format
|
70
|
-
# stored and transmitted as the appropriate keys in the params hash.
|
70
|
+
# all requests in the format <tt>/hello/:name</tt> where the word pattern
|
71
|
+
# is stored and transmitted as the appropriate keys in the params hash.
|
71
72
|
#
|
72
73
|
# Once we've got our inputs specified, we can start to handle requests:
|
73
74
|
#
|
@@ -76,18 +77,18 @@ module Halcyon
|
|
76
77
|
# r.match('/hello/:name').to(:action => 'greet')
|
77
78
|
# {:action => 'not_found'} # default route
|
78
79
|
# end
|
79
|
-
# def greet
|
80
|
+
# def greet; {:status=>200, :body=>"Hi #{params[:name]}"}; end
|
80
81
|
# end
|
81
82
|
#
|
82
83
|
# You will notice that we only define the method +greet+ and that it
|
83
84
|
# returns a Hash object containing a +status+ code and +body+ content.
|
84
85
|
# This is the most basic way to send data, but if all you're doing is
|
85
86
|
# replying that the request was successful and you have data to return,
|
86
|
-
# the method +ok+ (an alias of
|
87
|
-
# as its sole parameter is sufficient.
|
87
|
+
# the method +ok+ (an alias of <tt>standard_response</tt>) with the +body+
|
88
|
+
# param as its sole parameter is sufficient.
|
88
89
|
#
|
89
90
|
#
|
90
|
-
# def greet
|
91
|
+
# def greet; ok("Hi #{params[:name]}"); end
|
91
92
|
#
|
92
93
|
# You'll also notice that there's no method called +not_found+; this is
|
93
94
|
# because it is already defined and behaves almost exactly like the +ok+
|
@@ -99,9 +100,13 @@ module Halcyon
|
|
99
100
|
# route actually matches, so it doesn't need any of the extra path to match
|
100
101
|
# against.
|
101
102
|
#
|
103
|
+
# Lastly, the use of +params+ inside the method is simply a method call
|
104
|
+
# to a hash of the parameters gleaned from the route, such as +:name+ or
|
105
|
+
# any other variables passed to it.
|
106
|
+
#
|
102
107
|
# == The Filesystem
|
103
108
|
#
|
104
|
-
# It's important to note that the +halcyon+ commandline tool expects
|
109
|
+
# It's important to note that the +halcyon+ commandline tool expects to
|
105
110
|
# find your server inheriting +Halcyon::Server::Base+ with the same exact
|
106
111
|
# name as its filename, though with special rules.
|
107
112
|
#
|
@@ -109,7 +114,8 @@ module Halcyon
|
|
109
114
|
# that your server's class name be +AppServer+ as it capitalizes each word
|
110
115
|
# and removes all underscores, etc.
|
111
116
|
#
|
112
|
-
# Keep this in mind when naming your class and your file
|
117
|
+
# Keep this in mind when naming your class and your file, though this
|
118
|
+
# restriction is only temporary.
|
113
119
|
#
|
114
120
|
# NOTE: This really isn't a necessary step if you write your own deployment
|
115
121
|
# script instead of using the +halcyon+ commandline tool (as it is simply
|
@@ -175,11 +181,11 @@ module Halcyon
|
|
175
181
|
# Most of your requests will have all the data it needs inside of the
|
176
182
|
# +params+ you receive for your action, but for POST and PUT requests
|
177
183
|
# (you are being RESTful, right?) you will need to retrieve your data
|
178
|
-
# from the
|
184
|
+
# from the method +post+. Here's how:
|
179
185
|
#
|
180
|
-
#
|
186
|
+
# post[:key] => "value"
|
181
187
|
#
|
182
|
-
# As you can see, keys specifically are
|
188
|
+
# As you can see, keys specifically are symbols and values as well. What
|
183
189
|
# this means is that your POST data that you send to the server needs to
|
184
190
|
# be careful to provide a flat Hash (if anything other than a Hash is
|
185
191
|
# passed, it is packed up into a hash similar to +{:body=>data}+) or at
|
@@ -188,7 +194,7 @@ module Halcyon
|
|
188
194
|
# (though this could change). Here's how you would reconstruct your
|
189
195
|
# special hash:
|
190
196
|
#
|
191
|
-
# value = JSON.parse(
|
197
|
+
# value = JSON.parse(post[:key])
|
192
198
|
#
|
193
199
|
# That will take care of reconstructing your Hash.
|
194
200
|
#
|
@@ -253,7 +259,7 @@ module Halcyon
|
|
253
259
|
# that a proper JSON response may be rendered by +call+.
|
254
260
|
#
|
255
261
|
# With this in mind, it is preferred that, for any errors that should
|
256
|
-
# result in a given HTTP Response code other than
|
262
|
+
# result in a given HTTP Response code other than 2xx, an appropriate
|
257
263
|
# exception should be thrown which is then handled by this method's
|
258
264
|
# rescue clause.
|
259
265
|
#
|
@@ -309,32 +315,37 @@ module Halcyon
|
|
309
315
|
# requests.
|
310
316
|
#
|
311
317
|
# It is preferred for these methods to throw Exceptions::Base exceptions
|
312
|
-
# (or one of its inheriters) instead of handling them manually.
|
318
|
+
# (or one of its inheriters) instead of handling them manually. This
|
319
|
+
# ensures that the actual action is not run when in fact it shouldn't,
|
320
|
+
# otherwise you could be allowing unauthenticated users privileged
|
321
|
+
# information or allowing them to perform destructive actions.
|
313
322
|
def run(route)
|
314
323
|
# make sure the request meets our expectations
|
315
324
|
acceptable_request! unless $debug || $test
|
316
325
|
|
317
326
|
# pull params
|
318
|
-
params = route.reject{|key, val| [:action, :module].include? key}
|
319
|
-
params.merge!(query_params)
|
327
|
+
@params = route.reject{|key, val| [:action, :module].include? key}
|
328
|
+
@params.merge!(query_params)
|
320
329
|
|
321
330
|
# pre call hook
|
322
|
-
before_call
|
331
|
+
before_call if respond_to? :before_call
|
323
332
|
|
324
333
|
# handle module actions differently than non-module actions
|
325
334
|
if route[:module].nil?
|
326
335
|
# call action
|
327
|
-
res = send(route[:action]
|
336
|
+
res = send(route[:action])
|
328
337
|
else
|
329
338
|
# call module action
|
330
339
|
mod = self.dup
|
331
340
|
mod.instance_eval(&(@@modules[route[:module].to_sym]))
|
332
|
-
res = mod.send(route[:action]
|
341
|
+
res = mod.send(route[:action])
|
333
342
|
end
|
334
343
|
|
335
344
|
# after call hook
|
336
345
|
after_call if respond_to? :after_call
|
337
346
|
|
347
|
+
@params = {}
|
348
|
+
|
338
349
|
res
|
339
350
|
rescue Halcyon::Exceptions::Base => e
|
340
351
|
@logger.warn "#{uri} => #{e.error}"
|
@@ -399,8 +410,13 @@ module Halcyon
|
|
399
410
|
@pid = File.new(@config[:pid_file].gsub('{n}', server_cluster_number), "w", 0644)
|
400
411
|
@pid << "#{$$}\n"; @pid.close
|
401
412
|
|
402
|
-
# log existence
|
413
|
+
# log existence
|
403
414
|
@logger.info "PID file created. PID is #{$$}."
|
415
|
+
|
416
|
+
# call startup callback if defined
|
417
|
+
startup if respond_to? :startup
|
418
|
+
|
419
|
+
# log ready state
|
404
420
|
@logger.info "Started. Awaiting connectivity. Listening on #{@config[:port]}..."
|
405
421
|
|
406
422
|
# trap signals to die (when killed by the user) gracefully
|
@@ -424,7 +440,13 @@ module Halcyon
|
|
424
440
|
|
425
441
|
# Closes the logger and deletes the PID file.
|
426
442
|
def clean_up
|
443
|
+
# don't try to clean up what's cleaned up already
|
427
444
|
return if defined? @cleaned_up
|
445
|
+
|
446
|
+
# run shutdown hook if defined
|
447
|
+
shutdown if respond_to? :shutdown
|
448
|
+
|
449
|
+
# close logger, delete PID file, flag clean state
|
428
450
|
@logger.close
|
429
451
|
File.delete(@pid.path) if File.exist?(@pid.path)
|
430
452
|
@cleaned_up = true
|
@@ -528,12 +550,12 @@ module Halcyon
|
|
528
550
|
#
|
529
551
|
# The accepted level values are as follows:
|
530
552
|
#
|
531
|
-
#
|
532
|
-
#
|
533
|
-
#
|
534
|
-
#
|
535
|
-
#
|
536
|
-
#
|
553
|
+
# * debug
|
554
|
+
# * info
|
555
|
+
# * warn
|
556
|
+
# * error
|
557
|
+
# * fatal
|
558
|
+
# * unknown
|
537
559
|
#
|
538
560
|
# These are the exact way you can refer to the logger level you'd like to
|
539
561
|
# log at from all points of option specification (listed above in order
|
@@ -694,9 +716,14 @@ module Halcyon
|
|
694
716
|
raise Exceptions::NotFound.new(404, body)
|
695
717
|
end
|
696
718
|
|
719
|
+
# Returns the params of the current request, set in the +run+ method.
|
720
|
+
def params
|
721
|
+
@params
|
722
|
+
end
|
723
|
+
|
697
724
|
# Returns the params following the ? in a given URL as a hash
|
698
725
|
def query_params
|
699
|
-
@env['QUERY_STRING'].split(/&/).inject({}){|h,kp| k,v = kp.split(/=/); h[k] = v; h}
|
726
|
+
@env['QUERY_STRING'].split(/&/).inject({}){|h,kp| k,v = kp.split(/=/); h[k] = v; h}.symbolize_keys!
|
700
727
|
end
|
701
728
|
|
702
729
|
# Returns the URI requested
|
@@ -706,11 +733,41 @@ module Halcyon
|
|
706
733
|
URI.parse(@env['REQUEST_URI'] || @env['PATH_INFO']).path
|
707
734
|
end
|
708
735
|
|
709
|
-
# Returns the Request Method as a lowercase symbol
|
736
|
+
# Returns the Request Method as a lowercase symbol.
|
737
|
+
#
|
738
|
+
# One useful situation for this method would be similar to this:
|
739
|
+
#
|
740
|
+
# case method
|
741
|
+
# when :get
|
742
|
+
# # perform reading operations
|
743
|
+
# when :post
|
744
|
+
# # perform updating operations
|
745
|
+
# when :put
|
746
|
+
# # perform creating operations
|
747
|
+
# when :delete
|
748
|
+
# # perform deleting options
|
749
|
+
# end
|
750
|
+
#
|
751
|
+
# It can also be used in many other cases, like throwing an exception if
|
752
|
+
# an action is called with an unexpected method.
|
710
753
|
def method
|
711
754
|
@env['REQUEST_METHOD'].downcase.to_sym
|
712
755
|
end
|
713
756
|
|
757
|
+
# Returns the POST data hash, making the keys symbols first.
|
758
|
+
#
|
759
|
+
# Use like <tt>post[:post_param]</tt>.
|
760
|
+
def post
|
761
|
+
@req.POST.symbolize_keys!
|
762
|
+
end
|
763
|
+
|
764
|
+
# Returns the GET data hash, making the keys symbols first.
|
765
|
+
#
|
766
|
+
# Use like <tt>get[:get_param]</tt>.
|
767
|
+
def get
|
768
|
+
@req.GET.symbolize_keys!
|
769
|
+
end
|
770
|
+
|
714
771
|
end
|
715
772
|
|
716
773
|
end
|
@@ -8,7 +8,7 @@
|
|
8
8
|
#++
|
9
9
|
|
10
10
|
begin
|
11
|
-
%w(rubygems merb/core_ext merb/router).each {|dep|require dep}
|
11
|
+
%w(rubygems merb/core_ext merb/router uri).each {|dep|require dep}
|
12
12
|
rescue LoadError => e
|
13
13
|
abort "Merb must be installed for Routing to function. Please install Merb."
|
14
14
|
end
|
data/spec/halcyon/error_spec.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
|
1
|
+
describe "Halcyon::Server Errors" do
|
2
2
|
|
3
|
-
before
|
3
|
+
before do
|
4
4
|
@app = Specr.new :port => 4000
|
5
5
|
end
|
6
6
|
|
7
|
-
|
7
|
+
it "should provide shorthand methods for errors which should throw an appropriate exception" do
|
8
8
|
begin
|
9
9
|
@app.not_found
|
10
10
|
rescue Halcyon::Exceptions::Base => e
|
@@ -20,7 +20,7 @@ context "Halcyon::Server Errors" do
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
it "supports numerous standard HTTP request error exceptions with lookup by status code" do
|
24
24
|
begin
|
25
25
|
Halcyon::Server::Base::Exceptions::NotFound.new
|
26
26
|
rescue Halcyon::Exceptions::Base => e
|
@@ -44,7 +44,7 @@ context "Halcyon::Server Errors" do
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
|
47
|
+
it "should have a short inheritence chain to make catching generically simple" do
|
48
48
|
begin
|
49
49
|
Halcyon::Server::Base::Exceptions::NotFound.new
|
50
50
|
rescue Halcon::Exceptions::Base => e
|
data/spec/halcyon/router_spec.rb
CHANGED
@@ -1,31 +1,31 @@
|
|
1
|
-
|
1
|
+
describe "Halcyon::Server::Router" do
|
2
2
|
|
3
|
-
before
|
3
|
+
before do
|
4
4
|
@app = Specr.new :port => 4000
|
5
5
|
end
|
6
6
|
|
7
|
-
|
7
|
+
it "should prepares routes correctly when written correctly" do
|
8
8
|
# routes have been defined for Specr
|
9
|
-
Halcyon::Server::Router.routes.
|
9
|
+
Halcyon::Server::Router.routes.should.not == []
|
10
10
|
Halcyon::Server::Router.routes.length.should > 0
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
it "should match URIs to the correct route" do
|
14
14
|
Halcyon::Server::Router.route(Rack::MockRequest.env_for('/'))[:action].should == 'index'
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
it "should use the default route if no matching route is found" do
|
18
18
|
Halcyon::Server::Router.route(Rack::MockRequest.env_for('/erroneous/path'))[:action].should == 'not_found'
|
19
19
|
Halcyon::Server::Router.route(Rack::MockRequest.env_for("/random/#{rand}"))[:action].should == 'not_found'
|
20
20
|
end
|
21
21
|
|
22
|
-
|
22
|
+
it "should map params in routes to parameters" do
|
23
23
|
response = Halcyon::Server::Router.route(Rack::MockRequest.env_for('/hello/Matt'))
|
24
24
|
response[:action].should == 'greeter'
|
25
25
|
response[:name].should == 'Matt'
|
26
26
|
end
|
27
27
|
|
28
|
-
|
28
|
+
it "should supply arbitrary routing param values included as a param even if not in the URI" do
|
29
29
|
Halcyon::Server::Router.route(Rack::MockRequest.env_for('/'))[:arbitrary].should == 'random'
|
30
30
|
end
|
31
31
|
|
data/spec/halcyon/server_spec.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
-
|
1
|
+
describe "Halcyon::Server" do
|
2
2
|
|
3
|
-
before
|
3
|
+
before do
|
4
4
|
@app = Specr.new :port => 4000
|
5
5
|
end
|
6
6
|
|
7
|
-
|
7
|
+
it "should dispatch methods according to their respective routes" do
|
8
8
|
Rack::MockRequest.new(@app).get("/hello/Matt")
|
9
9
|
last_line = File.new(@app.instance_variable_get("@config")[:log_file]).readlines.last
|
10
10
|
last_line.should =~ /INFO \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] \(\d+\) Specr#test :: \[200\] .* => greeter \(.+\)/
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
it "should provide various shorthand methods for simple responses but take custom response values" do
|
14
14
|
response = {:status => 200, :body => 'OK'}
|
15
15
|
@app.ok.should == response
|
16
16
|
@app.success.should == response
|
@@ -20,44 +20,44 @@ context "Halcyon::Server" do
|
|
20
20
|
@app.ok(['OK', 'Sure Thang', 'Correcto']).should == {:status => 200, :body => ['OK', 'Sure Thang', 'Correcto']}
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
it "should handle requests and respond with JSON" do
|
24
24
|
body = JSON.parse(Rack::MockRequest.new(@app).get("/").body)
|
25
25
|
body['status'].should == 200
|
26
26
|
body['body'].should == "Found"
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
it "should handle requests with param values in the URL" do
|
30
30
|
body = JSON.parse(Rack::MockRequest.new(@app).get("/hello/Matt").body)
|
31
31
|
body['status'].should == 200
|
32
32
|
body['body'].should == "Hello Matt"
|
33
33
|
end
|
34
34
|
|
35
|
-
|
35
|
+
it "should route unmatchable requests to the default route and return JSON with appropriate status" do
|
36
36
|
body = JSON.parse(Rack::MockRequest.new(@app).get("/garbage/request/url").body)
|
37
37
|
body['status'].should == 404
|
38
38
|
body['body'].should == "Not Found"
|
39
39
|
end
|
40
40
|
|
41
|
-
|
41
|
+
it "should log activity" do
|
42
42
|
prev_line = File.new(@app.instance_variable_get("@config")[:log_file]).readlines.last
|
43
43
|
Rack::MockRequest.new(@app).get("/url/that/will/not/be/found/#{rand}")
|
44
44
|
last_line = File.new(@app.instance_variable_get("@config")[:log_file]).readlines.last
|
45
45
|
last_line.should =~ /INFO \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] \(\d+\) Specr#test :: \[404\] .* => not_found \(.+\)/
|
46
|
-
prev_line.
|
46
|
+
prev_line.should.not == last_line
|
47
47
|
end
|
48
48
|
|
49
|
-
|
49
|
+
it "should create a PID file while running with the correct process ID" do
|
50
50
|
pid_file = @app.instance_variable_get("@config")[:pid_file]
|
51
|
-
File.exist?(pid_file).should
|
51
|
+
File.exist?(pid_file).should.be.true?
|
52
52
|
File.open(pid_file){|file|file.read.should == "#{$$}\n"}
|
53
53
|
end
|
54
54
|
|
55
|
-
|
55
|
+
it "should parse URI query params correctly" do
|
56
56
|
Rack::MockRequest.new(@app).get("/?query=value&lang=en-US")
|
57
|
-
@app.query_params.should == {
|
57
|
+
@app.query_params.should == {:query => 'value', :lang => 'en-US'}
|
58
58
|
end
|
59
59
|
|
60
|
-
|
60
|
+
it "should parse the URI correctly" do
|
61
61
|
Rack::MockRequest.new(@app).get("http://localhost:4000/slaughterhouse/5")
|
62
62
|
@app.uri.should == '/slaughterhouse/5'
|
63
63
|
|
@@ -68,7 +68,7 @@ context "Halcyon::Server" do
|
|
68
68
|
@app.uri.should == '/'
|
69
69
|
end
|
70
70
|
|
71
|
-
|
71
|
+
it "should provide a quick way to find out what method the request was performed using" do
|
72
72
|
Rack::MockRequest.new(@app).get("/#{rand}")
|
73
73
|
@app.method.should == :get
|
74
74
|
|
@@ -82,12 +82,24 @@ context "Halcyon::Server" do
|
|
82
82
|
@app.method.should == :delete
|
83
83
|
end
|
84
84
|
|
85
|
-
|
85
|
+
it "should provide convenient access to GET and POST data" do
|
86
|
+
Rack::MockRequest.new(@app).get("/#{rand}?foo=bar")
|
87
|
+
@app.get[:foo].should == 'bar'
|
88
|
+
|
89
|
+
Rack::MockRequest.new(@app).post("/#{rand}", :input => {:foo => 'bar'}.to_params)
|
90
|
+
@app.post[:foo].should == 'bar'
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should deny all unacceptable requests" do
|
86
94
|
conf = @app.instance_variable_get("@config")
|
87
95
|
conf[:acceptable_requests] = Halcyon::Server::ACCEPTABLE_REQUESTS
|
88
96
|
|
89
|
-
Rack::MockRequest.new(@app).
|
97
|
+
Rack::MockRequest.new(@app).get("/#{rand}")
|
90
98
|
@app.acceptable_request! rescue Halcyon::Exceptions::Base
|
91
99
|
end
|
92
100
|
|
101
|
+
it "should record the correct environment details" do
|
102
|
+
@app.instance_eval { @config[:root].should == Dir.pwd }
|
103
|
+
end
|
104
|
+
|
93
105
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: halcyon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Todd
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-02-10 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -54,8 +54,6 @@ files:
|
|
54
54
|
- spec/halcyon/error_spec.rb
|
55
55
|
- spec/halcyon/router_spec.rb
|
56
56
|
- spec/halcyon/server_spec.rb
|
57
|
-
- spec/SPEC
|
58
|
-
- spec/SPEC.html
|
59
57
|
- spec/spec_helper.rb
|
60
58
|
- lib/halcyon
|
61
59
|
- lib/halcyon/client
|
@@ -65,12 +63,12 @@ files:
|
|
65
63
|
- lib/halcyon/client.rb
|
66
64
|
- lib/halcyon/exceptions.rb
|
67
65
|
- lib/halcyon/server
|
66
|
+
- lib/halcyon/server/auth
|
67
|
+
- lib/halcyon/server/auth/basic.rb
|
68
68
|
- lib/halcyon/server/base.rb
|
69
69
|
- lib/halcyon/server/exceptions.rb
|
70
70
|
- lib/halcyon/server/router.rb
|
71
71
|
- lib/halcyon/server.rb
|
72
|
-
- lib/halcyon/support
|
73
|
-
- lib/halcyon/support/hashext.rb
|
74
72
|
- lib/halcyon.rb
|
75
73
|
has_rdoc: true
|
76
74
|
homepage: http://halcyon.rubyforge.org
|
@@ -1,59 +0,0 @@
|
|
1
|
-
# Pulled out from http://dev.rubyonrails.org/browser/trunk/activesupport/lib/active_support/core_ext/hash/keys.rb
|
2
|
-
module HashExt #:nodoc:
|
3
|
-
module Keys #:nodoc:
|
4
|
-
# Return a new hash with all keys converted to strings.
|
5
|
-
def stringify_keys
|
6
|
-
inject({}) do |options, (key, value)|
|
7
|
-
options[key.to_s] = value
|
8
|
-
options
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
# Destructively convert all keys to strings.
|
13
|
-
def stringify_keys!
|
14
|
-
keys.each do |key|
|
15
|
-
unless key.class.to_s == "String" # weird hack to make the tests run when string_ext_test.rb is also running
|
16
|
-
self[key.to_s] = self[key]
|
17
|
-
delete(key)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
# Return a new hash with all keys converted to symbols.
|
24
|
-
def symbolize_keys
|
25
|
-
inject({}) do |options, (key, value)|
|
26
|
-
value = value.symbolize_keys if value.respond_to? :symbolize_keys # recursive # MT 2007-12-04
|
27
|
-
options[key.to_sym || key] = value
|
28
|
-
options
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Destructively convert all keys to symbols.
|
33
|
-
def symbolize_keys!
|
34
|
-
keys.each do |key|
|
35
|
-
unless key.is_a?(Symbol) || (new_key = key.to_sym).nil?
|
36
|
-
self[new_key] = self[key]
|
37
|
-
self[new_key].symbolize_keys! if self[new_key].respond_to? :symbolize_keys!
|
38
|
-
delete(key)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
self
|
42
|
-
end
|
43
|
-
|
44
|
-
alias_method :to_options, :symbolize_keys
|
45
|
-
alias_method :to_options!, :symbolize_keys!
|
46
|
-
|
47
|
-
# Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
|
48
|
-
# Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbol
|
49
|
-
# as keys, this will fail.
|
50
|
-
# examples:
|
51
|
-
# { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
|
52
|
-
# { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): years, name"
|
53
|
-
# { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
|
54
|
-
def assert_valid_keys(*valid_keys)
|
55
|
-
unknown_keys = keys - [valid_keys].flatten
|
56
|
-
raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
data/spec/SPEC
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
|
2
|
-
Halcyon::Server Errors
|
3
|
-
- should provide shorthand methods for errors which should throw an appropriate exception
|
4
|
-
- supports numerous standard HTTP request error exceptions with lookup by status code
|
5
|
-
- should have a short inheritence chain to make catching generically simple
|
6
|
-
|
7
|
-
Halcyon::Server::Router
|
8
|
-
- should prepares routes correctly when written correctly
|
9
|
-
- should match URIs to the correct route
|
10
|
-
- should use the default route if no matching route is found
|
11
|
-
- should map params in routes to parameters
|
12
|
-
- should supply arbitrary routing param values included as a param even if not in the URI
|
13
|
-
|
14
|
-
Halcyon::Server
|
15
|
-
- should dispatch methods according to their respective routes
|
16
|
-
- should provide various shorthand methods for simple responses but take custom response values
|
17
|
-
- should handle requests and respond with JSON
|
18
|
-
- should handle requests with param values in the URL
|
19
|
-
- should route unmatchable requests to the default route and return JSON with appropriate status
|
20
|
-
- should log activity
|
21
|
-
- should create a PID file while running with the correct process ID
|
22
|
-
- should parse URI query params correctly
|
23
|
-
- should parse the URI correctly
|
24
|
-
- should provide a quick way to find out what method the request was performed using
|
25
|
-
- should deny all unacceptable requests
|
26
|
-
|
27
|
-
Finished in 0.211129 seconds
|
28
|
-
|
29
|
-
19 examples, 0 failures
|
data/spec/SPEC.html
DELETED
@@ -1,237 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<!DOCTYPE html
|
3
|
-
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
4
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
5
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
6
|
-
<head>
|
7
|
-
<title>RSpec results</title>
|
8
|
-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
9
|
-
<meta http-equiv="Expires" content="-1" />
|
10
|
-
<meta http-equiv="Pragma" content="no-cache" />
|
11
|
-
<style type="text/css">
|
12
|
-
body {
|
13
|
-
margin: 0;
|
14
|
-
padding: 0;
|
15
|
-
background: #fff;
|
16
|
-
font-size: 80%;
|
17
|
-
}
|
18
|
-
</style>
|
19
|
-
</head>
|
20
|
-
<body>
|
21
|
-
<div class="rspec-report">
|
22
|
-
<script type="text/javascript">
|
23
|
-
// <![CDATA[
|
24
|
-
function moveProgressBar(percentDone) {
|
25
|
-
document.getElementById("rspec-header").style.width = percentDone +"%";
|
26
|
-
}
|
27
|
-
function makeRed(element_id) {
|
28
|
-
document.getElementById(element_id).style.background = '#C40D0D';
|
29
|
-
document.getElementById(element_id).style.color = '#FFFFFF';
|
30
|
-
}
|
31
|
-
|
32
|
-
function makeYellow(element_id) {
|
33
|
-
if (element_id == "rspec-header" && document.getElementById(element_id).style.background != '#C40D0D')
|
34
|
-
{
|
35
|
-
document.getElementById(element_id).style.background = '#FAF834';
|
36
|
-
document.getElementById(element_id).style.color = '#000000';
|
37
|
-
}
|
38
|
-
else
|
39
|
-
{
|
40
|
-
document.getElementById(element_id).style.background = '#FAF834';
|
41
|
-
document.getElementById(element_id).style.color = '#000000';
|
42
|
-
}
|
43
|
-
}
|
44
|
-
|
45
|
-
// ]]>
|
46
|
-
</script>
|
47
|
-
<style type="text/css">
|
48
|
-
#rspec-header {
|
49
|
-
background: #65C400; color: #fff;
|
50
|
-
}
|
51
|
-
|
52
|
-
.rspec-report h1 {
|
53
|
-
margin: 0px 10px 0px 10px;
|
54
|
-
padding: 10px;
|
55
|
-
font-family: "Lucida Grande", Helvetica, sans-serif;
|
56
|
-
font-size: 1.8em;
|
57
|
-
}
|
58
|
-
|
59
|
-
#summary {
|
60
|
-
margin: 0; padding: 5px 10px;
|
61
|
-
font-family: "Lucida Grande", Helvetica, sans-serif;
|
62
|
-
text-align: right;
|
63
|
-
position: absolute;
|
64
|
-
top: 0px;
|
65
|
-
right: 0px;
|
66
|
-
}
|
67
|
-
|
68
|
-
#summary p {
|
69
|
-
margin: 0 0 0 2px;
|
70
|
-
}
|
71
|
-
|
72
|
-
#summary #totals {
|
73
|
-
font-size: 1.2em;
|
74
|
-
}
|
75
|
-
|
76
|
-
.example_group {
|
77
|
-
margin: 0 10px 5px;
|
78
|
-
background: #fff;
|
79
|
-
}
|
80
|
-
|
81
|
-
dl {
|
82
|
-
margin: 0; padding: 0 0 5px;
|
83
|
-
font: normal 11px "Lucida Grande", Helvetica, sans-serif;
|
84
|
-
}
|
85
|
-
|
86
|
-
dt {
|
87
|
-
padding: 3px;
|
88
|
-
background: #65C400;
|
89
|
-
color: #fff;
|
90
|
-
font-weight: bold;
|
91
|
-
}
|
92
|
-
|
93
|
-
dd {
|
94
|
-
margin: 5px 0 5px 5px;
|
95
|
-
padding: 3px 3px 3px 18px;
|
96
|
-
}
|
97
|
-
|
98
|
-
dd.spec.passed {
|
99
|
-
border-left: 5px solid #65C400;
|
100
|
-
border-bottom: 1px solid #65C400;
|
101
|
-
background: #DBFFB4; color: #3D7700;
|
102
|
-
}
|
103
|
-
|
104
|
-
dd.spec.failed {
|
105
|
-
border-left: 5px solid #C20000;
|
106
|
-
border-bottom: 1px solid #C20000;
|
107
|
-
color: #C20000; background: #FFFBD3;
|
108
|
-
}
|
109
|
-
|
110
|
-
dd.spec.not_implemented {
|
111
|
-
border-left: 5px solid #FAF834;
|
112
|
-
border-bottom: 1px solid #FAF834;
|
113
|
-
background: #FCFB98; color: #131313;
|
114
|
-
}
|
115
|
-
|
116
|
-
dd.spec.pending_fixed {
|
117
|
-
border-left: 5px solid #0000C2;
|
118
|
-
border-bottom: 1px solid #0000C2;
|
119
|
-
color: #0000C2; background: #D3FBFF;
|
120
|
-
}
|
121
|
-
|
122
|
-
.backtrace {
|
123
|
-
color: #000;
|
124
|
-
font-size: 12px;
|
125
|
-
}
|
126
|
-
|
127
|
-
a {
|
128
|
-
color: #BE5C00;
|
129
|
-
}
|
130
|
-
|
131
|
-
/* Ruby code, style similar to vibrant ink */
|
132
|
-
.ruby {
|
133
|
-
font-size: 12px;
|
134
|
-
font-family: monospace;
|
135
|
-
color: white;
|
136
|
-
background-color: black;
|
137
|
-
padding: 0.1em 0 0.2em 0;
|
138
|
-
}
|
139
|
-
|
140
|
-
.ruby .keyword { color: #FF6600; }
|
141
|
-
.ruby .constant { color: #339999; }
|
142
|
-
.ruby .attribute { color: white; }
|
143
|
-
.ruby .global { color: white; }
|
144
|
-
.ruby .module { color: white; }
|
145
|
-
.ruby .class { color: white; }
|
146
|
-
.ruby .string { color: #66FF00; }
|
147
|
-
.ruby .ident { color: white; }
|
148
|
-
.ruby .method { color: #FFCC00; }
|
149
|
-
.ruby .number { color: white; }
|
150
|
-
.ruby .char { color: white; }
|
151
|
-
.ruby .comment { color: #9933CC; }
|
152
|
-
.ruby .symbol { color: white; }
|
153
|
-
.ruby .regex { color: #44B4CC; }
|
154
|
-
.ruby .punct { color: white; }
|
155
|
-
.ruby .escape { color: white; }
|
156
|
-
.ruby .interp { color: white; }
|
157
|
-
.ruby .expr { color: white; }
|
158
|
-
|
159
|
-
.ruby .offending { background-color: gray; }
|
160
|
-
.ruby .linenum {
|
161
|
-
width: 75px;
|
162
|
-
padding: 0.1em 1em 0.2em 0;
|
163
|
-
color: #000000;
|
164
|
-
background-color: #FFFBD3;
|
165
|
-
}
|
166
|
-
|
167
|
-
</style>
|
168
|
-
|
169
|
-
<div id="rspec-header">
|
170
|
-
<h1>RSpec Results</h1>
|
171
|
-
|
172
|
-
<div id="summary">
|
173
|
-
<p id="totals"> </p>
|
174
|
-
<p id="duration"> </p>
|
175
|
-
</div>
|
176
|
-
</div>
|
177
|
-
|
178
|
-
<div class="results">
|
179
|
-
<div class="example_group">
|
180
|
-
<dl>
|
181
|
-
<dt id="example_group_1">Halcyon::Server Errors</dt>
|
182
|
-
<script type="text/javascript">moveProgressBar('5.2');</script>
|
183
|
-
<dd class="spec passed"><span class="passed_spec_name">should provide shorthand methods for errors which should throw an appropriate exception</span></dd>
|
184
|
-
<script type="text/javascript">moveProgressBar('10.5');</script>
|
185
|
-
<dd class="spec passed"><span class="passed_spec_name">supports numerous standard HTTP request error exceptions with lookup by status code</span></dd>
|
186
|
-
<script type="text/javascript">moveProgressBar('15.7');</script>
|
187
|
-
<dd class="spec passed"><span class="passed_spec_name">should have a short inheritence chain to make catching generically simple</span></dd>
|
188
|
-
</dl>
|
189
|
-
</div>
|
190
|
-
<div class="example_group">
|
191
|
-
<dl>
|
192
|
-
<dt id="example_group_2">Halcyon::Server::Router</dt>
|
193
|
-
<script type="text/javascript">moveProgressBar('21.0');</script>
|
194
|
-
<dd class="spec passed"><span class="passed_spec_name">should prepares routes correctly when written correctly</span></dd>
|
195
|
-
<script type="text/javascript">moveProgressBar('26.3');</script>
|
196
|
-
<dd class="spec passed"><span class="passed_spec_name">should match URIs to the correct route</span></dd>
|
197
|
-
<script type="text/javascript">moveProgressBar('31.5');</script>
|
198
|
-
<dd class="spec passed"><span class="passed_spec_name">should use the default route if no matching route is found</span></dd>
|
199
|
-
<script type="text/javascript">moveProgressBar('36.8');</script>
|
200
|
-
<dd class="spec passed"><span class="passed_spec_name">should map params in routes to parameters</span></dd>
|
201
|
-
<script type="text/javascript">moveProgressBar('42.1');</script>
|
202
|
-
<dd class="spec passed"><span class="passed_spec_name">should supply arbitrary routing param values included as a param even if not in the URI</span></dd>
|
203
|
-
</dl>
|
204
|
-
</div>
|
205
|
-
<div class="example_group">
|
206
|
-
<dl>
|
207
|
-
<dt id="example_group_3">Halcyon::Server</dt>
|
208
|
-
<script type="text/javascript">moveProgressBar('47.3');</script>
|
209
|
-
<dd class="spec passed"><span class="passed_spec_name">should dispatch methods according to their respective routes</span></dd>
|
210
|
-
<script type="text/javascript">moveProgressBar('52.6');</script>
|
211
|
-
<dd class="spec passed"><span class="passed_spec_name">should provide various shorthand methods for simple responses but take custom response values</span></dd>
|
212
|
-
<script type="text/javascript">moveProgressBar('57.8');</script>
|
213
|
-
<dd class="spec passed"><span class="passed_spec_name">should handle requests and respond with JSON</span></dd>
|
214
|
-
<script type="text/javascript">moveProgressBar('63.1');</script>
|
215
|
-
<dd class="spec passed"><span class="passed_spec_name">should handle requests with param values in the URL</span></dd>
|
216
|
-
<script type="text/javascript">moveProgressBar('68.4');</script>
|
217
|
-
<dd class="spec passed"><span class="passed_spec_name">should route unmatchable requests to the default route and return JSON with appropriate status</span></dd>
|
218
|
-
<script type="text/javascript">moveProgressBar('73.6');</script>
|
219
|
-
<dd class="spec passed"><span class="passed_spec_name">should log activity</span></dd>
|
220
|
-
<script type="text/javascript">moveProgressBar('78.9');</script>
|
221
|
-
<dd class="spec passed"><span class="passed_spec_name">should create a PID file while running with the correct process ID</span></dd>
|
222
|
-
<script type="text/javascript">moveProgressBar('84.2');</script>
|
223
|
-
<dd class="spec passed"><span class="passed_spec_name">should parse URI query params correctly</span></dd>
|
224
|
-
<script type="text/javascript">moveProgressBar('89.4');</script>
|
225
|
-
<dd class="spec passed"><span class="passed_spec_name">should parse the URI correctly</span></dd>
|
226
|
-
<script type="text/javascript">moveProgressBar('94.7');</script>
|
227
|
-
<dd class="spec passed"><span class="passed_spec_name">should provide a quick way to find out what method the request was performed using</span></dd>
|
228
|
-
<script type="text/javascript">moveProgressBar('100.0');</script>
|
229
|
-
<dd class="spec passed"><span class="passed_spec_name">should deny all unacceptable requests</span></dd>
|
230
|
-
</dl>
|
231
|
-
</div>
|
232
|
-
<script type="text/javascript">document.getElementById('duration').innerHTML = "Finished in <strong>0.211129 seconds</strong>";</script>
|
233
|
-
<script type="text/javascript">document.getElementById('totals').innerHTML = "19 examples, 0 failures";</script>
|
234
|
-
</div>
|
235
|
-
</div>
|
236
|
-
</body>
|
237
|
-
</html>
|