halcyon 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +1 -0
- data/LICENSE +20 -0
- data/README +107 -0
- data/Rakefile +8 -6
- data/bin/halcyon +3 -204
- data/lib/halcyon.rb +55 -42
- data/lib/halcyon/application.rb +247 -0
- data/lib/halcyon/application/router.rb +86 -0
- data/lib/halcyon/client.rb +187 -35
- data/lib/halcyon/client/ssl.rb +38 -0
- data/lib/halcyon/controller.rb +154 -0
- data/lib/halcyon/exceptions.rb +67 -59
- data/lib/halcyon/logging.rb +31 -0
- data/lib/halcyon/logging/analogger.rb +31 -0
- data/lib/halcyon/logging/helpers.rb +37 -0
- data/lib/halcyon/logging/log4r.rb +25 -0
- data/lib/halcyon/logging/logger.rb +20 -0
- data/lib/halcyon/logging/logging.rb +19 -0
- data/lib/halcyon/runner.rb +141 -0
- data/lib/halcyon/runner/commands.rb +141 -0
- data/lib/halcyon/runner/helpers.rb +9 -0
- data/lib/halcyon/runner/helpers/command_helper.rb +71 -0
- data/spec/halcyon/application_spec.rb +70 -0
- data/spec/halcyon/client_spec.rb +63 -0
- data/spec/halcyon/controller_spec.rb +68 -0
- data/spec/halcyon/halcyon_spec.rb +63 -0
- data/spec/halcyon/logging_spec.rb +31 -0
- data/spec/halcyon/router_spec.rb +37 -12
- data/spec/halcyon/runner_spec.rb +54 -0
- data/spec/spec_helper.rb +75 -9
- data/support/generators/halcyon/USAGE +0 -0
- data/support/generators/halcyon/halcyon_generator.rb +52 -0
- data/support/generators/halcyon/templates/README +26 -0
- data/support/generators/halcyon/templates/Rakefile +32 -0
- data/support/generators/halcyon/templates/app/application.rb +43 -0
- data/support/generators/halcyon/templates/config/config.yml +36 -0
- data/support/generators/halcyon/templates/config/init/environment.rb +11 -0
- data/support/generators/halcyon/templates/config/init/hooks.rb +39 -0
- data/support/generators/halcyon/templates/config/init/requires.rb +10 -0
- data/support/generators/halcyon/templates/config/init/routes.rb +50 -0
- data/support/generators/halcyon/templates/lib/client.rb +77 -0
- data/support/generators/halcyon/templates/runner.ru +8 -0
- data/support/generators/halcyon_flat/USAGE +0 -0
- data/support/generators/halcyon_flat/halcyon_flat_generator.rb +52 -0
- data/support/generators/halcyon_flat/templates/README +26 -0
- data/support/generators/halcyon_flat/templates/Rakefile +32 -0
- data/support/generators/halcyon_flat/templates/app.rb +49 -0
- data/support/generators/halcyon_flat/templates/lib/client.rb +17 -0
- data/support/generators/halcyon_flat/templates/runner.ru +8 -0
- metadata +73 -20
- data/lib/halcyon/client/base.rb +0 -261
- data/lib/halcyon/client/exceptions.rb +0 -41
- data/lib/halcyon/client/router.rb +0 -106
- data/lib/halcyon/server.rb +0 -62
- data/lib/halcyon/server/auth/basic.rb +0 -107
- data/lib/halcyon/server/base.rb +0 -774
- data/lib/halcyon/server/exceptions.rb +0 -41
- data/lib/halcyon/server/router.rb +0 -103
- data/spec/halcyon/error_spec.rb +0 -55
- data/spec/halcyon/server_spec.rb +0 -105
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'log4r'
|
2
|
+
include Log4r
|
3
|
+
module Halcyon
|
4
|
+
module Logging
|
5
|
+
class Log4r < Log4r::Logger
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def setup(config)
|
10
|
+
logger = self.new(config[:label] || Halcyon.app)
|
11
|
+
if config[:file]
|
12
|
+
logger.outputters = Log4r::FileOutputter.new(:filename => config[:file])
|
13
|
+
else
|
14
|
+
logger.outputters = Log4r::Outputter.stdout
|
15
|
+
end
|
16
|
+
logger.level = Object.const_get((config[:level] || 'debug').upcase.to_sym)
|
17
|
+
logger.outputters[0].formatter = Log4r::PatternFormatter.new(:pattern => "%5l [%d] (#{$$}) #{Halcyon.app} :: %m\n", :date_pattern => "%Y-%m-%d %H:%M:%S")
|
18
|
+
logger
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'logger'
|
2
|
+
module Halcyon
|
3
|
+
module Logging
|
4
|
+
class Logger < ::Logger
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def setup(config)
|
9
|
+
logger = config[:logger] || self.new(config[:file] || STDOUT)
|
10
|
+
logger.formatter = proc{|s,t,p,m|"%5s [%s] (%s) %s :: %s\n" % [s, t.strftime("%Y-%m-%d %H:%M:%S"), $$, p, m]}
|
11
|
+
logger.progname = Halcyon.app
|
12
|
+
logger.level = Logger.const_get((config[:level] || 'info').upcase)
|
13
|
+
logger
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'logging'
|
2
|
+
module Halcyon
|
3
|
+
module Logging
|
4
|
+
class Logging < Logging::Logger
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def setup(config)
|
9
|
+
logger = config[:logger] || ::Logging.logger(config[:file] || STDOUT)
|
10
|
+
logger.level = config[:level].downcase.to_sym
|
11
|
+
logger.instance_variable_get("@appenders")[0].instance_variable_set("@layout", ::Logging::Layouts::Pattern.new(:pattern => "%5l [%d] (%p) #{Halcyon.app} :: %m\n", :date_pattern => "%Y-%m-%d %H:%M:%S"))
|
12
|
+
logger
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Halcyon
|
2
|
+
|
3
|
+
# Handles initializing and running the application, including:
|
4
|
+
# * setting up the logger
|
5
|
+
# * loading initializers
|
6
|
+
# * loading controllers
|
7
|
+
#
|
8
|
+
# The Runner is a full-fledged Rack application, and accepts calls to #call.
|
9
|
+
#
|
10
|
+
# Also handles running commands form the command line.
|
11
|
+
#
|
12
|
+
# Examples
|
13
|
+
# # start serving the current app (in .)
|
14
|
+
# Halcyon::Runner.run!(['start', '-p', '4647'])
|
15
|
+
#
|
16
|
+
# # load the config file and initialize the app
|
17
|
+
# Halcyon::Runner.load_config Halcyon.root/'config'/'config.yml'
|
18
|
+
# Halcyon::Runner.new
|
19
|
+
class Runner
|
20
|
+
|
21
|
+
autoload :Commands, 'halcyon/runner/commands'
|
22
|
+
|
23
|
+
class << self
|
24
|
+
|
25
|
+
# Runs commands from the CLI.
|
26
|
+
# +argv+ the arguments to pass to the commands
|
27
|
+
#
|
28
|
+
# Returns nothing
|
29
|
+
def run!(argv=ARGV)
|
30
|
+
Commands.send(argv.shift, argv)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the path to the configuration file specified, defaulting
|
34
|
+
# to the path for the <tt>config.yml</tt> file.
|
35
|
+
# +file+ the name of the config file path (without the <tt>.yml</tt>
|
36
|
+
# extension)
|
37
|
+
def config_path(file = "config")
|
38
|
+
Halcyon.paths[:config]/"#{file}.yml"
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
# Initializes the application and application resources.
|
44
|
+
def initialize
|
45
|
+
Halcyon::Runner.load_paths if Halcyon.paths.nil?
|
46
|
+
|
47
|
+
# Load the configuration if none is set already
|
48
|
+
if Halcyon.config.nil?
|
49
|
+
if File.exist?(Halcyon::Runner.config_path)
|
50
|
+
Halcyon.config = Halcyon::Runner.load_config
|
51
|
+
else
|
52
|
+
Halcon.config = Halcyon::Application::DEFAULT_OPTIONS
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Set application name
|
57
|
+
Halcyon.app = Halcyon.config[:app] || Halcyon.root.split('/').last.camel_case
|
58
|
+
|
59
|
+
# Setup logger
|
60
|
+
if Halcyon.config[:logger]
|
61
|
+
Halcyon.config[:logging] = (Halcyon.config[:logging] || Halcyon::Application::DEFAULT_OPTIONS[:logging]).merge({
|
62
|
+
:type => Halcyon.config[:logger].class.to_s,
|
63
|
+
:logger => Halcyon.config[:logger]
|
64
|
+
})
|
65
|
+
end
|
66
|
+
Halcyon::Logging.set((Halcyon.config[:logging][:type] rescue nil))
|
67
|
+
Halcyon.logger = Halcyon::Logger.setup(Halcyon.config[:logging])
|
68
|
+
|
69
|
+
# Run initializers
|
70
|
+
Dir.glob(Halcyon.paths[:init]/'{requires,hooks,routes,environment,*}.rb').each do |initializer|
|
71
|
+
self.logger.debug "Init: #{File.basename(initializer).chomp('.rb').camel_case}" if
|
72
|
+
require initializer.chomp('.rb')
|
73
|
+
end
|
74
|
+
|
75
|
+
# Setup autoloads for Controllers found in Halcyon.root/'app'
|
76
|
+
Dir.glob(Halcyon.paths[:controller]/'{application,*}.rb').each do |controller|
|
77
|
+
self.logger.debug "Load: #{File.basename(controller).chomp('.rb').camel_case} Controller" if
|
78
|
+
require controller.chomp('.rb')
|
79
|
+
end
|
80
|
+
|
81
|
+
@app = Halcyon::Application.new
|
82
|
+
end
|
83
|
+
|
84
|
+
# Calls the application, which gets proxied to the dispatcher.
|
85
|
+
# +env+ the request environment details
|
86
|
+
#
|
87
|
+
# Returns [Fixnum:status, {String:header => String:value}, [String:body]]
|
88
|
+
def call(env)
|
89
|
+
@app.call(env)
|
90
|
+
end
|
91
|
+
|
92
|
+
class << self
|
93
|
+
|
94
|
+
# Loads the configuration file specified into <tt>Halcyon.config</tt>.
|
95
|
+
# +file+ the configuration file to load
|
96
|
+
#
|
97
|
+
# Examples
|
98
|
+
# Halcyon::Runner.load_config Halcyon.root/'config'/'config.yml'
|
99
|
+
# Halcyon.config #=> {:allow_from => :all, :logging => {...}, ...}.to_mash
|
100
|
+
#
|
101
|
+
# Returns {Symbol:key => String:value}.to_mash
|
102
|
+
def load_config(file=Halcyon::Runner.config_path)
|
103
|
+
if File.exist?(file)
|
104
|
+
require 'yaml'
|
105
|
+
|
106
|
+
# load the config file
|
107
|
+
begin
|
108
|
+
config = YAML.load_file(file).to_mash
|
109
|
+
rescue Errno::EACCES
|
110
|
+
raise LoadError.new("Can't access #{file}, try 'sudo #{$0}'")
|
111
|
+
end
|
112
|
+
else
|
113
|
+
warn "#{file} not found, ensure the path to this file is correct. Ignoring."
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Set the paths for resources to be located.
|
119
|
+
#
|
120
|
+
# Used internally for setting the load paths if not manually overridden
|
121
|
+
# and needed to be set before normal application initialization.
|
122
|
+
#
|
123
|
+
# TODO: Move this to the planned <tt>Halcyon::Config</tt> object.
|
124
|
+
#
|
125
|
+
# Returns nothing.
|
126
|
+
def load_paths
|
127
|
+
# Set the default application paths, not overwriting manually set paths
|
128
|
+
Halcyon.paths = {
|
129
|
+
:controller => Halcyon.root/'app',
|
130
|
+
:model => Halcyon.root/'app'/'models',
|
131
|
+
:lib => Halcyon.root/'lib',
|
132
|
+
:config => Halcyon.root/'config',
|
133
|
+
:init => Halcyon.root/'config'/'{init,initialize}',
|
134
|
+
:log => Halcyon.root/'log'
|
135
|
+
}.to_mash.merge(Halcyon.paths || {})
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Halcyon
|
4
|
+
class Runner
|
5
|
+
|
6
|
+
autoload :Helpers, 'halcyon/runner/helpers'
|
7
|
+
|
8
|
+
class Commands
|
9
|
+
class << self
|
10
|
+
|
11
|
+
# Run the Halcyon application
|
12
|
+
def start(argv)
|
13
|
+
options = {
|
14
|
+
:port => 4647,
|
15
|
+
:server => (Gem.searcher.find('thin').nil? ? 'mongrel' : 'thin')
|
16
|
+
}
|
17
|
+
|
18
|
+
OptionParser.new do |opts|
|
19
|
+
opts.banner = "Usage: halcyon start [options]"
|
20
|
+
|
21
|
+
opts.separator ""
|
22
|
+
opts.separator "Start options:"
|
23
|
+
opts.on("-s", "--server SERVER", "") { |server| options[:server] = server }
|
24
|
+
|
25
|
+
begin
|
26
|
+
opts.parse! argv
|
27
|
+
rescue OptionParser::InvalidOption => e
|
28
|
+
# the other options can be used elsewhere, like in RubiGen
|
29
|
+
argv = e.recover(argv)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if options[:server] == 'thin'
|
34
|
+
# Thin is installed
|
35
|
+
command = "thin start -R runner.ru #{argv.join(' ')}"
|
36
|
+
else
|
37
|
+
# Thin is not installed
|
38
|
+
command = "rackup runner.ru -s #{options[:server]} #{argv.join(' ')}"
|
39
|
+
end
|
40
|
+
|
41
|
+
# run command
|
42
|
+
exec command
|
43
|
+
end
|
44
|
+
|
45
|
+
# Start the Halcyon server up in interactive mode
|
46
|
+
def console(argv)
|
47
|
+
# Notify user of environment
|
48
|
+
puts "(Starting Halcyon app in console...)"
|
49
|
+
|
50
|
+
# Add ./lib to load path
|
51
|
+
$:.unshift(Halcyon.root/'lib')
|
52
|
+
|
53
|
+
# prepare environment for IRB
|
54
|
+
ARGV.clear
|
55
|
+
require 'rack/mock'
|
56
|
+
require 'logger'
|
57
|
+
require 'irb'
|
58
|
+
require 'irb/completion'
|
59
|
+
if File.exists? '.irbrc'
|
60
|
+
ENV['IRBRC'] = '.irbrc'
|
61
|
+
end
|
62
|
+
|
63
|
+
# Set up the application
|
64
|
+
Object.instance_eval do
|
65
|
+
$log = ''
|
66
|
+
Halcyon::Runner.load_paths if Halcyon.paths.nil?
|
67
|
+
(Halcyon.config = Halcyon::Runner.load_config) || require(Halcyon.root/'app')
|
68
|
+
Halcyon.config[:logger] = Logger.new(StringIO.new($log))
|
69
|
+
$app = Halcyon::Runner.new
|
70
|
+
$response = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# Setup helper methods
|
74
|
+
Object.send(:include, Halcyon::Runner::Helpers::CommandHelper)
|
75
|
+
|
76
|
+
# Let users know what methods and values are available
|
77
|
+
puts "Call #usage for usage details."
|
78
|
+
|
79
|
+
# Start IRB session
|
80
|
+
IRB.start
|
81
|
+
|
82
|
+
exit
|
83
|
+
end
|
84
|
+
alias_method :interactive, :console
|
85
|
+
alias_method :irb, :console
|
86
|
+
alias_method :"-i", :console
|
87
|
+
|
88
|
+
# Generate a new Halcyon application
|
89
|
+
def init(argv)
|
90
|
+
app_name = argv.last
|
91
|
+
|
92
|
+
options = {
|
93
|
+
:generator => 'halcyon',
|
94
|
+
:git => false
|
95
|
+
}
|
96
|
+
|
97
|
+
OptionParser.new do |opts|
|
98
|
+
opts.banner = "Usage: halcyon init [options]"
|
99
|
+
|
100
|
+
opts.separator ""
|
101
|
+
opts.separator "Generator options:"
|
102
|
+
opts.on("-f", "--flat", "") { options[:generator] = 'halcyon_flat' }
|
103
|
+
|
104
|
+
opts.separator ""
|
105
|
+
opts.separator "Additional options:"
|
106
|
+
opts.on("-g", "--git", "Initialize a Git repository when finished generating") { options[:git] = true }
|
107
|
+
opts.on("-G", "--git-commit", "Initialize a Git repo and commit") { options[:git] = options[:git_commit] = true }
|
108
|
+
|
109
|
+
begin
|
110
|
+
opts.parse! argv
|
111
|
+
rescue OptionParser::InvalidOption => e
|
112
|
+
# the other options can be used elsewhere, like in RubiGen
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
require 'rubigen'
|
117
|
+
require 'rubigen/scripts/generate'
|
118
|
+
RubiGen::Base.use_application_sources!
|
119
|
+
RubiGen::Base.sources << RubiGen::PathSource.new(:custom, File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', "support/generators")))
|
120
|
+
RubiGen::Scripts::Generate.new.run(argv, :generator => options[:generator])
|
121
|
+
|
122
|
+
# Create a Git repository in the new app dir
|
123
|
+
if options[:git]
|
124
|
+
system("cd #{app_name} && git init -q && cd #{Dir.pwd}")
|
125
|
+
puts "Initialized Git repository in #{app_name}/"
|
126
|
+
File.open(File.join("#{app_name}",'.gitignore'),"w") {|f| f << "log/*.log" }
|
127
|
+
File.open(File.join("#{app_name}",'log','.gitignore'),"w") {|f| f << "" }
|
128
|
+
end
|
129
|
+
|
130
|
+
# commit to the git repo
|
131
|
+
if options[:git_commit]
|
132
|
+
system("cd #{app_name} && git add . && git commit -m 'Initial import.' -q && cd #{Dir.pwd}")
|
133
|
+
puts "Committed empty application in #{app_name}/"
|
134
|
+
puts "Run `git commit --amend` to change the commit message."
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Halcyon
|
2
|
+
class Runner
|
3
|
+
class Helpers
|
4
|
+
module CommandHelper
|
5
|
+
|
6
|
+
def usage
|
7
|
+
msg = <<-"end;"
|
8
|
+
|
9
|
+
These methods will provide you with most of the
|
10
|
+
functionality you will need to test your app.
|
11
|
+
|
12
|
+
#app The loaded application
|
13
|
+
#log The contents of the log (Ex: puts log)
|
14
|
+
#tail The tail end of the log (Ex: tail)
|
15
|
+
#clear Clears the log (Ex: clear)
|
16
|
+
#get Sends a GET request to the app
|
17
|
+
Ex: get '/controller/action'
|
18
|
+
#post Sends a POST request to #app
|
19
|
+
Ex: post '/controller/action', :key => value
|
20
|
+
#put See #post
|
21
|
+
#delete See #get
|
22
|
+
#response Response of the last request
|
23
|
+
|
24
|
+
end;
|
25
|
+
puts msg.gsub(/^[ ]{12}/, '')
|
26
|
+
end
|
27
|
+
|
28
|
+
def app
|
29
|
+
$app
|
30
|
+
end
|
31
|
+
|
32
|
+
def log
|
33
|
+
$log
|
34
|
+
end
|
35
|
+
|
36
|
+
def tail
|
37
|
+
puts $log.split("\n").reverse[0..5].reverse.join("\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
def clear
|
41
|
+
$log = ''
|
42
|
+
end
|
43
|
+
|
44
|
+
def get(path)
|
45
|
+
$response = Rack::MockRequest.new($app).get(path)
|
46
|
+
JSON.parse($response.body)
|
47
|
+
end
|
48
|
+
|
49
|
+
def post(path, params = {})
|
50
|
+
$response = Rack::MockRequest.new($app).post(path, :input => params.to_params)
|
51
|
+
JSON.parse($response.body)
|
52
|
+
end
|
53
|
+
|
54
|
+
def put(path, params = {})
|
55
|
+
$response = Rack::MockRequest.new($app).put(path, :input => params.to_params)
|
56
|
+
JSON.parse($response.body)
|
57
|
+
end
|
58
|
+
|
59
|
+
def delete(path)
|
60
|
+
$response = Rack::MockRequest.new($app).delete(path)
|
61
|
+
JSON.parse($response.body)
|
62
|
+
end
|
63
|
+
|
64
|
+
def response
|
65
|
+
$response
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
describe "Halcyon::Application" do
|
2
|
+
|
3
|
+
before do
|
4
|
+
@log = ''
|
5
|
+
@logger = Logger.new(StringIO.new(@log))
|
6
|
+
@config = $config.dup
|
7
|
+
@config[:logger] = @logger
|
8
|
+
@config[:app] = 'Specs'
|
9
|
+
Halcyon.config = @config
|
10
|
+
@app = Halcyon::Runner.new
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should run startup hook if defined" do
|
14
|
+
# $started is set by the startup hook
|
15
|
+
$started.should.be.true?
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should dispatch methods according to their respective routes" do
|
19
|
+
Rack::MockRequest.new(@app).get("/hello/Matt")
|
20
|
+
@log.should =~ / INFO \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] \(\d+\) Specs :: \[200\] \/hello\/Matt \(.+\)\n/
|
21
|
+
end
|
22
|
+
|
23
|
+
it "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
|
+
it "should handle requests with param values in the URL" do
|
30
|
+
body = JSON.parse(Rack::MockRequest.new(@app).get("/hello/Matt?test=value").body)
|
31
|
+
body['status'].should == 200
|
32
|
+
body['body'].should == "Hello Matt"
|
33
|
+
@log.split("\n").last.should =~ /"test"=>"value"/
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should not dispatch private methods" do
|
37
|
+
body = JSON.parse(Rack::MockRequest.new(@app).get("/specs/undispatchable_private_method").body)
|
38
|
+
body['status'].should == 404
|
39
|
+
body['body'].should == "Not Found"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should route unmatchable requests to the default route and return JSON with appropriate status" do
|
43
|
+
body = JSON.parse(Rack::MockRequest.new(@app).get("/garbage/request/url").body)
|
44
|
+
body['status'].should == 404
|
45
|
+
body['body'].should == "Not Found"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should log activity" do
|
49
|
+
Halcyon.logger.is_a?(Logger).should.be.true?
|
50
|
+
Rack::MockRequest.new(@app).get("/lolcats/r/cute")
|
51
|
+
@log.should =~ / INFO \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] \(\d+\) Specs :: \[404\] \/lolcats\/r\/cute \(.+\)\n/
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should allow all requests by default" do
|
55
|
+
Halcyon.config[:allow_from].should == :all
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should handle exceptions gracefully" do
|
59
|
+
body = JSON.parse(Rack::MockRequest.new(@app).get("/specs/cause_exception").body)
|
60
|
+
body['status'].should == 500
|
61
|
+
body['body'].should == "Internal Server Error"
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should not confuse a NoMethodFound error in an action as a missing route" do
|
65
|
+
body = JSON.parse(Rack::MockRequest.new(@app).get("/specs/call_nonexistent_method").body)
|
66
|
+
body['status'].should.not == 404
|
67
|
+
body['body'].should == "Internal Server Error"
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|