halcyon 0.3.18 → 0.3.22

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 CHANGED
@@ -75,42 +75,39 @@ task :uninstall => [:clean] do
75
75
  sh %{sudo gem uninstall #{project[:name]}}
76
76
  end
77
77
 
78
- task 'run-spec' do
79
- require 'spec'
80
- $:.unshift(File.dirname(__FILE__))
81
- stdout = []
82
- class << stdout
83
- def print(*e) concat(e); Kernel.print(*e); end
84
- def puts(*e) concat(e); Kernel.puts(*e); end
85
- def flush; end
78
+ namespace 'spec' do
79
+ desc "generate spec"
80
+ task :gen do
81
+ sh "spec -c -rlib/halcyon -rspec/spec_helper spec/**/* --format s:spec/SPEC --format h:spec/SPEC.html"
86
82
  end
87
- stderr = []
88
- class << stderr
89
- alias print <<
90
- def print(*e) concat(e); Kernel.print(*e); end
91
- def puts(*e) concat(e); Kernel.puts(*e); end
92
- def flush; end
83
+
84
+ desc "run rspec"
85
+ task :run do
86
+ sh "spec -c -rlib/halcyon -rspec/spec_helper spec/**/*"
87
+ end
88
+
89
+ desc "run rspec verbosely"
90
+ task :verb do
91
+ sh "spec -c -rlib/halcyon -rspec/spec_helper spec/**/* --format s"
93
92
  end
94
- ::Spec::Runner::CommandLine.run(['spec'], stderr, stdout, false, true)
95
- exit_status = stdout.last.strip[/(\d+) failures?/, 1].to_i
96
- at_exit{
97
- exit(exit_status == 0 ? 0 : 1)
98
- }
99
- end
100
-
101
- desc "run rspec"
102
- task :spec do
103
- run = Rake::Task['run-spec']
104
- run.execute
105
93
  end
106
94
 
107
- task :default => :spec
108
-
109
95
  desc "Do predistribution stuff"
110
- task :predist => [:chmod, :changelog, :rdoc]
96
+ task :predist => [:chmod, :changelog, :manifest, :rdoc]
111
97
 
112
98
  def manifest
113
- `darcs query manifest`.split("\n").map { |f| f.gsub(/\A\.\//, '') }
99
+ require 'find'
100
+ paths = []
101
+ manifest = File.new('MANIFEST', 'w+')
102
+ Find.find('.') do |path|
103
+ path.gsub!(/\A\.\//, '')
104
+ next if path =~ /(\.svn|doc|pkg|^\.|MANIFEST)/
105
+ paths << path
106
+ end
107
+ paths.sort.each do |path|
108
+ manifest.puts path
109
+ end
110
+ manifest.close
114
111
  end
115
112
 
116
113
  desc "Make binaries executable"
@@ -119,9 +116,14 @@ task :chmod do
119
116
  Dir["test/cgi/test*"].each { |binary| File.chmod(0775, binary) }
120
117
  end
121
118
 
122
- desc "Generate a ChangeLog"
119
+ desc "Generate a MANIFEST"
120
+ task :manifest do
121
+ manifest
122
+ end
123
+
124
+ desc "Generate a CHANGELOG"
123
125
  task :changelog do
124
- sh "svn log >ChangeLog"
126
+ sh "svn log > CHANGELOG"
125
127
  end
126
128
 
127
129
  desc "Generate RDoc documentation"
@@ -146,3 +148,5 @@ desc "find . -name \"*.rb\" | xargs wc -l | grep total"
146
148
  task :loc do
147
149
  sh "find . -name \"*.rb\" | xargs wc -l | grep total"
148
150
  end
151
+
152
+ task :default => Rake::Task['spec:run']
data/bin/halcyon CHANGED
@@ -19,6 +19,7 @@
19
19
  #++
20
20
 
21
21
  $debug = false
22
+ $test = false
22
23
  options = Halcyon::Server::DEFAULT_OPTIONS
23
24
 
24
25
  #--
data/lib/halcyon.rb CHANGED
@@ -21,7 +21,7 @@ end
21
21
  #++
22
22
 
23
23
  module Halcyon
24
- VERSION = [0,3,18]
24
+ VERSION = [0,3,22]
25
25
  def self.version
26
26
  VERSION.join('.')
27
27
  end
@@ -40,7 +40,7 @@ module Halcyon
40
40
  # For documentation on using Halcyon, check out the Halcyon::Server::Base and
41
41
  # Halcyon::Client::Base classes which contain much more usage documentation.
42
42
  def introduction
43
- abort "READ THE DAMNED RDOCS FOO"
43
+ abort "READ THE DAMNED RDOCS!"
44
44
  end
45
45
 
46
46
  end
@@ -221,8 +221,8 @@ module Halcyon
221
221
  # If the server responds with any kind of failure (anything with a status
222
222
  # that isn't 200), Halcyon will in turn raise the respective exception
223
223
  # (defined in Halcyon::Exceptions) which all inherit from
224
- # +Halcyon::Client::Exceptions::Base+. It is up to the client to handle
225
- # these exceptions specifically.
224
+ # +Halcyon::Exceptions::Base+. It is up to the client to handle these
225
+ # exceptions specifically.
226
226
  def request(req)
227
227
  # prepare and send HTTP request
228
228
  res = Net::HTTP.start(@uri.host, @uri.port) {|http|http.request(req)}
@@ -236,7 +236,7 @@ module Halcyon
236
236
 
237
237
  # return response
238
238
  body
239
- rescue Halcyon::Client::Base::Exceptions::Base => e
239
+ rescue Halcyon::Exceptions::Base => e
240
240
  # log exception if logger is in place
241
241
  raise
242
242
  end
@@ -12,18 +12,6 @@ module Halcyon
12
12
  class Base
13
13
  module Exceptions #:nodoc:
14
14
 
15
- #--
16
- # Base Halcyon Exception
17
- #++
18
-
19
- class Base < StandardError #:nodoc:
20
- attr_accessor :status, :error
21
- def initialize(status, error)
22
- @status = status
23
- @error = error
24
- end
25
- end
26
-
27
15
  #--
28
16
  # Exception classes
29
17
  #++
@@ -31,7 +19,7 @@ module Halcyon
31
19
  Halcyon::Exceptions::HTTP_ERROR_CODES.to_a.each do |http_error|
32
20
  status, body = http_error
33
21
  class_eval(
34
- "class #{body.gsub(/( |\-)/,'')} < Base\n"+
22
+ "class #{body.gsub(/( |\-)/,'')} < Halcyon::Exceptions::Base\n"+
35
23
  " def initialize(s=#{status}, e='#{body}')\n"+
36
24
  " super s, e\n"+
37
25
  " end\n"+
@@ -9,6 +9,23 @@
9
9
 
10
10
  module Halcyon
11
11
  module Exceptions #:nodoc:
12
+
13
+ #--
14
+ # Base Halcyon Exception
15
+ #++
16
+
17
+ class Base < StandardError #:nodoc:
18
+ attr_accessor :status, :error
19
+ def initialize(status, error)
20
+ @status = status
21
+ @error = error
22
+ end
23
+ end
24
+
25
+ #--
26
+ # HTTP Error Codes and Errors
27
+ #++
28
+
12
29
  HTTP_ERROR_CODES = {
13
30
  400 => 'Bad Request',
14
31
  401 => 'Unauthorized',
@@ -219,9 +219,6 @@ module Halcyon
219
219
  # instance
220
220
  @env['halcyon.logger'] = @logger
221
221
 
222
- # set the acceptable remotes to include the remote IP if debugging is enabled
223
- @config[:acceptable_remotes] << @env["REMOTE_ADDR"] if $debug
224
-
225
222
  # pre run hook
226
223
  before_run(Time.now - @time_started) if respond_to? :before_run
227
224
 
@@ -289,6 +286,11 @@ module Halcyon
289
286
  # primarily only speaking with other apps on the same machine, though
290
287
  # your specific requirements may differ and change that.
291
288
  #
289
+ # When in debug mode or in testing mode, the request filtering test is
290
+ # not fired, so all requests from all User Agents and locations will
291
+ # succeed. This is important to know if you plan on testing this specific
292
+ # feature while in debugging or testing modes.
293
+ #
292
294
  # == Hooks, Callbacks, and Authentication
293
295
  #
294
296
  # There is no Authentication mechanism built in to Halcyon (for the time
@@ -310,10 +312,7 @@ module Halcyon
310
312
  # (or one of its inheriters) instead of handling them manually.
311
313
  def run(route)
312
314
  # make sure the request meets our expectations
313
- @config[:acceptable_requests].each do |req|
314
- raise Exceptions::Base.new(req[2], req[3]) unless @env[req[0]] =~ req[1]
315
- end
316
- raise Exceptions::Forbidden.new unless @config[:acceptable_remotes].member? @env["REMOTE_ADDR"]
315
+ acceptable_request! unless $debug || $test
317
316
 
318
317
  # pull params
319
318
  params = route.reject{|key, val| [:action, :module].include? key}
@@ -337,13 +336,21 @@ module Halcyon
337
336
  after_call if respond_to? :after_call
338
337
 
339
338
  res
340
- rescue Exceptions::Base => e
339
+ rescue Halcyon::Exceptions::Base => e
341
340
  @logger.warn "#{uri} => #{e.error}"
342
341
  # handles all content error exceptions
343
342
  @res.status = e.status
344
343
  {:status => e.status, :body => e.error}
345
344
  end
346
345
 
346
+ # Tests for acceptable requests if +$debug+ and +$test+ are not set.
347
+ def acceptable_request!
348
+ @config[:acceptable_requests].each do |req|
349
+ raise Halcyon::Exceptions::Base.new(req[2], req[3]) unless @env[req[0]] =~ req[1]
350
+ end
351
+ raise Exceptions::Forbidden.new unless @config[:acceptable_remotes].member? @env["REMOTE_ADDR"]
352
+ end
353
+
347
354
  #--
348
355
  # Initialization and setup
349
356
  #++
@@ -373,18 +380,20 @@ module Halcyon
373
380
  def initialize(options = {})
374
381
  # save configuration options
375
382
  @config = DEFAULT_OPTIONS.merge(options)
383
+ @config[:app] ||= self.class.to_s.downcase
376
384
 
377
385
  # apply name options to log_file and pid_file configs
378
386
  apply_log_and_pid_file_name_options
379
387
 
380
- # debug mode handling
388
+ # debug and test mode handling
381
389
  enable_debugging if $debug
390
+ enable_testing if $test
382
391
 
383
392
  # setup logging
384
- setup_logging unless $debug
393
+ setup_logging unless $debug || $test
385
394
 
386
395
  # setup request filtering
387
- setup_request_filters unless $debug
396
+ setup_request_filters unless $debug || $test
388
397
 
389
398
  # create PID file
390
399
  @pid = File.new(@config[:pid_file].gsub('{n}', server_cluster_number), "w", 0644)
@@ -397,8 +406,7 @@ module Halcyon
397
406
  # trap signals to die (when killed by the user) gracefully
398
407
  finalize = Proc.new do
399
408
  @logger.info "Shutting down #{$$}."
400
- @logger.close
401
- File.delete(@pid.path)
409
+ clean_up
402
410
  exit
403
411
  end
404
412
  # http://en.wikipedia.org/wiki/Signal_%28computing%29
@@ -414,6 +422,14 @@ module Halcyon
414
422
  end
415
423
  end
416
424
 
425
+ # Closes the logger and deletes the PID file.
426
+ def clean_up
427
+ return if defined? @cleaned_up
428
+ @logger.close
429
+ File.delete(@pid.path) if File.exist?(@pid.path)
430
+ @cleaned_up = true
431
+ end
432
+
417
433
  # Retreives the server cluster sequence number for the PID file.
418
434
  #
419
435
  # This is deprecated and will be removed soon, probably for the 0.4.0
@@ -448,23 +464,41 @@ module Halcyon
448
464
  # debugging.
449
465
  def enable_debugging
450
466
  $debug = true
467
+
468
+ # set the PID file name to /tmp/ unless PID file already exists
469
+ @config[:pid_file] = '/tmp/halcyon.{server}.{app}.{port}.pid' unless defined? @pid
470
+ apply_log_and_pid_file_name_options # reapply for {server}, {app}, and {port} to be set
471
+
451
472
  # setup logger to STDOUT and log entering debugging mode
452
473
  @logger = Logger.new(STDOUT)
453
474
  @logger.progname = "#{self.class}#debug"
454
475
  @logger.level = Logger::DEBUG
455
476
  @logger.formatter = @config[:log_format]
456
477
  @logger.info "Entering debugging mode..."
457
-
478
+ rescue Errno::EACCES
479
+ abort "Can't access #{@config[:pid_file]}, try 'sudo #{$0}'"
480
+ end
481
+
482
+ # This method is used to setup logging and the request handling methods
483
+ # for debugging.
484
+ def enable_testing
458
485
  # set the PID file name to /tmp/ unless PID file already exists
459
- @config[:pid_file] = '/tmp/halcyon.{server}.{app}.{port}.pid' unless @pid.is_a? File
486
+ @config[:pid_file] = '/tmp/halcyon.testing.{app}.{port}.pid' unless defined? @pid
487
+ @config[:log_file] = '/tmp/halcyon.testing.{app}.log'
460
488
  apply_log_and_pid_file_name_options # reapply for {server}, {app}, and {port} to be set
461
489
 
462
- # modify acceptable request's profiles
463
- @config[:acceptable_requests] = [
464
- ["HTTP_USER_AGENT", /.*/, 406, 'Not Acceptable'],
465
- ["HTTP_USER_AGENT", /.*/, 415, 'Unsupported Media Type'] # content type isn't set when navigating via browser
466
- ]
467
- @logger.debug "ACCEPTABLE_REQUESTS modified to accept all User Agents (browsers)"
490
+ # setup logger and log entering testing mode
491
+ @logger = Logger.new(@config[:log_file])
492
+ @logger.progname = "#{self.class}#test"
493
+ @logger.level = Logger::DEBUG
494
+ @logger.formatter = @config[:log_format]
495
+ @logger.info "Entering testing mode..."
496
+
497
+ # make sure we clean up after ourselves since we're in testing mode
498
+ at_exit {
499
+ clean_up
500
+ File.delete(@config[:log_file]) if File.exist?(@config[:log_file])
501
+ }
468
502
  rescue Errno::EACCES
469
503
  abort "Can't access #{@config[:pid_file]}, try 'sudo #{$0}'"
470
504
  end
@@ -669,7 +703,7 @@ module Halcyon
669
703
  def uri
670
704
  # special parsing is done to remove the protocol, host, and port that
671
705
  # some Handlers leave in there. (Fixes inconsistencies.)
672
- URI.parse(@env['REQUEST_URI']).path
706
+ URI.parse(@env['REQUEST_URI'] || @env['PATH_INFO']).path
673
707
  end
674
708
 
675
709
  # Returns the Request Method as a lowercase symbol
@@ -12,18 +12,6 @@ module Halcyon
12
12
  class Base
13
13
  module Exceptions #:nodoc:
14
14
 
15
- #--
16
- # Base Halcyon Exception
17
- #++
18
-
19
- class Base < StandardError #:nodoc:
20
- attr_accessor :status, :error
21
- def initialize(status, error)
22
- @status = status
23
- @error = error
24
- end
25
- end
26
-
27
15
  #--
28
16
  # Exception classes
29
17
  #++
@@ -31,7 +19,7 @@ module Halcyon
31
19
  Halcyon::Exceptions::HTTP_ERROR_CODES.to_a.each do |http_error|
32
20
  status, body = http_error
33
21
  class_eval(
34
- "class #{body.gsub(/( |\-)/,'')} < Base\n"+
22
+ "class #{body.gsub(/( |\-)/,'')} < Halcyon::Exceptions::Base\n"+
35
23
  " def initialize(s=#{status}, e='#{body}')\n"+
36
24
  " super s, e\n"+
37
25
  " end\n"+
@@ -76,7 +76,8 @@ module Halcyon
76
76
  # default route if no match is made.
77
77
  def self.route(env)
78
78
  # pull out the path requested (WEBrick keeps the host and port and protocol in REQUEST_URI)
79
- uri = URI.parse(env['REQUEST_URI']).path
79
+ # PATH_INFO is failover if REQUEST_URI is blank (like what Rack::MockRequest does)
80
+ uri = URI.parse(env['REQUEST_URI'] || env['PATH_INFO']).path
80
81
 
81
82
  # prepare request
82
83
  path = (uri ? uri.split('?').first : '').sub(/\/+/, '/')
@@ -89,7 +90,7 @@ module Halcyon
89
90
  # make sure a route is returned even if no match is found
90
91
  if route[0].nil?
91
92
  #return default route
92
- env['halcyon.logger'].debug "No route found. Using default."
93
+ env['halcyon.logger'].debug "No route found. Using default." if env['halcyon.logger'].is_a? Logger
93
94
  @@default_route
94
95
  else
95
96
  # params (including action and module if set) for the matching route
data/spec/SPEC ADDED
@@ -0,0 +1,29 @@
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 ADDED
@@ -0,0 +1,237 @@
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">&nbsp;</p>
174
+ <p id="duration">&nbsp;</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>
@@ -0,0 +1,55 @@
1
+ context "Halcyon::Server Errors" do
2
+
3
+ before(:each) do
4
+ @app = Specr.new :port => 4000
5
+ end
6
+
7
+ specify "should provide shorthand methods for errors which should throw an appropriate exception" do
8
+ begin
9
+ @app.not_found
10
+ rescue Halcyon::Exceptions::Base => e
11
+ e.status.should == 404
12
+ e.error.should == 'Not Found'
13
+ end
14
+
15
+ begin
16
+ @app.not_found('Missing')
17
+ rescue Halcyon::Exceptions::Base => e
18
+ e.status.should == 404
19
+ e.error.should == 'Missing'
20
+ end
21
+ end
22
+
23
+ specify "supports numerous standard HTTP request error exceptions with lookup by status code" do
24
+ begin
25
+ Halcyon::Server::Base::Exceptions::NotFound.new
26
+ rescue Halcyon::Exceptions::Base => e
27
+ e.status.should == 404
28
+ e.error.should == 'Not Found'
29
+ end
30
+
31
+ Halcyon::Exceptions::HTTP_ERROR_CODES.each do |code, error|
32
+ begin
33
+ Halcyon::Server::Base::Exceptions.const_get(error.gsub(/( |\-)/,'')).new
34
+ rescue Halcyon::Exceptions::Base => e
35
+ e.status.should == code
36
+ e.error.should == error
37
+ end
38
+ begin
39
+ Halcyon::Server::Base::Exceptions.lookup(code).new
40
+ rescue Halcyon::Exceptions::Base => e
41
+ e.status.should == code
42
+ e.error.should == error
43
+ end
44
+ end
45
+ end
46
+
47
+ specify "should have a short inheritence chain to make catching generically simple" do
48
+ begin
49
+ Halcyon::Server::Base::Exceptions::NotFound.new
50
+ rescue Halcon::Exceptions::Base => e
51
+ e.class.to_s.should == 'NotFound'
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,32 @@
1
+ context "Halcyon::Server::Router" do
2
+
3
+ before(:each) do
4
+ @app = Specr.new :port => 4000
5
+ end
6
+
7
+ specify "should prepares routes correctly when written correctly" do
8
+ # routes have been defined for Specr
9
+ Halcyon::Server::Router.routes.should_not == []
10
+ Halcyon::Server::Router.routes.length.should > 0
11
+ end
12
+
13
+ specify "should match URIs to the correct route" do
14
+ Halcyon::Server::Router.route(Rack::MockRequest.env_for('/'))[:action].should == 'index'
15
+ end
16
+
17
+ specify "should use the default route if no matching route is found" do
18
+ Halcyon::Server::Router.route(Rack::MockRequest.env_for('/erroneous/path'))[:action].should == 'not_found'
19
+ Halcyon::Server::Router.route(Rack::MockRequest.env_for("/random/#{rand}"))[:action].should == 'not_found'
20
+ end
21
+
22
+ specify "should map params in routes to parameters" do
23
+ response = Halcyon::Server::Router.route(Rack::MockRequest.env_for('/hello/Matt'))
24
+ response[:action].should == 'greeter'
25
+ response[:name].should == 'Matt'
26
+ end
27
+
28
+ specify "should supply arbitrary routing param values included as a param even if not in the URI" do
29
+ Halcyon::Server::Router.route(Rack::MockRequest.env_for('/'))[:arbitrary].should == 'random'
30
+ end
31
+
32
+ end
@@ -0,0 +1,93 @@
1
+ context "Halcyon::Server" do
2
+
3
+ before(:each) do
4
+ @app = Specr.new :port => 4000
5
+ end
6
+
7
+ specify "should dispatch methods according to their respective routes" do
8
+ Rack::MockRequest.new(@app).get("/hello/Matt")
9
+ last_line = File.new(@app.instance_variable_get("@config")[:log_file]).readlines.last
10
+ last_line.should =~ /INFO \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] \(\d+\) Specr#test :: \[200\] .* => greeter \(.+\)/
11
+ end
12
+
13
+ specify "should provide various shorthand methods for simple responses but take custom response values" do
14
+ response = {:status => 200, :body => 'OK'}
15
+ @app.ok.should == response
16
+ @app.success.should == response
17
+ @app.standard_response.should == response
18
+
19
+ @app.ok('').should == {:status => 200, :body => ''}
20
+ @app.ok(['OK', 'Sure Thang', 'Correcto']).should == {:status => 200, :body => ['OK', 'Sure Thang', 'Correcto']}
21
+ end
22
+
23
+ specify "should handle requests and respond with JSON" do
24
+ body = JSON.parse(Rack::MockRequest.new(@app).get("/").body)
25
+ body['status'].should == 200
26
+ body['body'].should == "Found"
27
+ end
28
+
29
+ specify "should handle requests with param values in the URL" do
30
+ body = JSON.parse(Rack::MockRequest.new(@app).get("/hello/Matt").body)
31
+ body['status'].should == 200
32
+ body['body'].should == "Hello Matt"
33
+ end
34
+
35
+ specify "should route unmatchable requests to the default route and return JSON with appropriate status" do
36
+ body = JSON.parse(Rack::MockRequest.new(@app).get("/garbage/request/url").body)
37
+ body['status'].should == 404
38
+ body['body'].should == "Not Found"
39
+ end
40
+
41
+ specify "should log activity" do
42
+ prev_line = File.new(@app.instance_variable_get("@config")[:log_file]).readlines.last
43
+ Rack::MockRequest.new(@app).get("/url/that/will/not/be/found/#{rand}")
44
+ last_line = File.new(@app.instance_variable_get("@config")[:log_file]).readlines.last
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.should_not == last_line
47
+ end
48
+
49
+ specify "should create a PID file while running with the correct process ID" do
50
+ pid_file = @app.instance_variable_get("@config")[:pid_file]
51
+ File.exist?(pid_file).should be_true
52
+ File.open(pid_file){|file|file.read.should == "#{$$}\n"}
53
+ end
54
+
55
+ specify "should parse URI query params correctly" do
56
+ Rack::MockRequest.new(@app).get("/?query=value&lang=en-US")
57
+ @app.query_params.should == {'query' => 'value', 'lang' => 'en-US'}
58
+ end
59
+
60
+ specify "should parse the URI correctly" do
61
+ Rack::MockRequest.new(@app).get("http://localhost:4000/slaughterhouse/5")
62
+ @app.uri.should == '/slaughterhouse/5'
63
+
64
+ Rack::MockRequest.new(@app).get("/slaughterhouse/5")
65
+ @app.uri.should == '/slaughterhouse/5'
66
+
67
+ Rack::MockRequest.new(@app).get("")
68
+ @app.uri.should == '/'
69
+ end
70
+
71
+ specify "should provide a quick way to find out what method the request was performed using" do
72
+ Rack::MockRequest.new(@app).get("/#{rand}")
73
+ @app.method.should == :get
74
+
75
+ Rack::MockRequest.new(@app).post("/#{rand}")
76
+ @app.method.should == :post
77
+
78
+ Rack::MockRequest.new(@app).put("/#{rand}")
79
+ @app.method.should == :put
80
+
81
+ Rack::MockRequest.new(@app).delete("/#{rand}")
82
+ @app.method.should == :delete
83
+ end
84
+
85
+ specify "should deny all unacceptable requests" do
86
+ conf = @app.instance_variable_get("@config")
87
+ conf[:acceptable_requests] = Halcyon::Server::ACCEPTABLE_REQUESTS
88
+
89
+ Rack::MockRequest.new(@app).put("/#{rand}")
90
+ @app.acceptable_request! rescue Halcyon::Exceptions::Base
91
+ end
92
+
93
+ end
@@ -0,0 +1,21 @@
1
+ require 'halcyon/server'
2
+ require 'rack/mock'
3
+
4
+ $test = true
5
+
6
+ class Specr < Halcyon::Server::Base
7
+
8
+ route do |r|
9
+ r.match('/hello/:name').to(:action => 'greeter')
10
+ r.match('/').to(:action => 'index', :arbitrary => 'random')
11
+ end
12
+
13
+ def index(params)
14
+ ok('Found')
15
+ end
16
+
17
+ def greeter(params)
18
+ ok("Hello #{params[:name]}")
19
+ end
20
+
21
+ 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.3.18
4
+ version: 0.3.22
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: 2007-12-31 00:00:00 -05:00
12
+ date: 2008-01-07 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -50,6 +50,13 @@ extra_rdoc_files:
50
50
  files:
51
51
  - lib
52
52
  - Rakefile
53
+ - spec/halcyon
54
+ - spec/halcyon/error_spec.rb
55
+ - spec/halcyon/router_spec.rb
56
+ - spec/halcyon/server_spec.rb
57
+ - spec/SPEC
58
+ - spec/SPEC.html
59
+ - spec/spec_helper.rb
53
60
  - lib/halcyon
54
61
  - lib/halcyon/client
55
62
  - lib/halcyon/client/base.rb