halcyon 0.3.22 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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>
|