halcyon 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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,63 @@
|
|
1
|
+
#--
|
2
|
+
# Start App for Tests
|
3
|
+
# and wait for it to be responsive
|
4
|
+
#++
|
5
|
+
|
6
|
+
fork do
|
7
|
+
dir = Halcyon.root/'support'/'generators'/'halcyon'/'templates'
|
8
|
+
command = "thin start -R runner.ru -p 89981 -c #{dir} > /dev/null 2>&1"
|
9
|
+
STDOUT.close
|
10
|
+
STDERR.close
|
11
|
+
exec command
|
12
|
+
end
|
13
|
+
client = Halcyon::Client.new('http://localhost:89981')
|
14
|
+
begin
|
15
|
+
sleep 1.5
|
16
|
+
client.get('/time')
|
17
|
+
rescue Errno::ECONNREFUSED => e
|
18
|
+
retry
|
19
|
+
end
|
20
|
+
|
21
|
+
#--
|
22
|
+
# Cleanup
|
23
|
+
#++
|
24
|
+
|
25
|
+
at_exit do
|
26
|
+
pids = (`ps auxww | grep support/generators/halcyon/templates`).split("\n").collect{|pid|pid.match(/(\w+)\s+(\w+).+/)[2]}
|
27
|
+
pids.each {|pid| Process.kill(9, pid.to_i) rescue nil }
|
28
|
+
end
|
29
|
+
|
30
|
+
#--
|
31
|
+
# Tests
|
32
|
+
#++
|
33
|
+
|
34
|
+
describe "Halcyon::Client" do
|
35
|
+
|
36
|
+
before do
|
37
|
+
@client = Halcyon::Client.new('http://localhost:89981')
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should perform requests and return the response values" do
|
41
|
+
response = @client.get('/time')[:body]
|
42
|
+
response.length.should > 25
|
43
|
+
response.include?(Time.now.year.to_s).should.be.true?
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should be able to perform get, post, put, and delete requests" do
|
47
|
+
@client.get('/time')[:body].length.should > 25
|
48
|
+
@client.post('/time')[:body].length.should > 20
|
49
|
+
@client.put('/time')[:body].should == "Not Implemented"
|
50
|
+
@client.delete('/time')[:status].should == 501
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should throw exceptions unless an OK response is sent if toggled to" do
|
54
|
+
# default behavior is to not raise exceptions
|
55
|
+
@client.get('/nonexistent/route')[:status].should == 404
|
56
|
+
|
57
|
+
# tell it to raise exceptions
|
58
|
+
@client.raise_exceptions! true
|
59
|
+
should.raise(Halcyon::Exceptions::NotFound) { @client.get('/nonexistent/route') }
|
60
|
+
@client.get('/time')[:status].should == 200
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
describe "Halcyon::Controller" 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 provide various shorthand methods for simple responses but take custom response values" do
|
14
|
+
controller = Specs.new(Rack::MockRequest.env_for('/'))
|
15
|
+
|
16
|
+
response = {:status => 200, :body => 'OK'}
|
17
|
+
controller.ok.should == response
|
18
|
+
controller.success.should == response
|
19
|
+
|
20
|
+
controller.ok('').should == {:status => 200, :body => ''}
|
21
|
+
controller.ok(['OK', 'Sure Thang', 'Correcto']).should == {:status => 200, :body => ['OK', 'Sure Thang', 'Correcto']}
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should provide a quick way to find out what method the request was performed using" do
|
25
|
+
%w(GET POST PUT DELETE).each do |m|
|
26
|
+
controller = Specs.new(Rack::MockRequest.env_for('/', :method => m))
|
27
|
+
controller.method.should == m.downcase.to_sym
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should provide convenient access to GET and POST data" do
|
32
|
+
controller = Specs.new(Rack::MockRequest.env_for("/#{rand}?foo=bar"))
|
33
|
+
controller.get[:foo].should == 'bar'
|
34
|
+
|
35
|
+
controller = Specs.new(Rack::MockRequest.env_for("/#{rand}", :method => 'POST', :input => {:foo => 'bar'}.to_params))
|
36
|
+
controller.post[:foo].should == 'bar'
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should parse URI query params correctly" do
|
40
|
+
controller = Specs.new(Rack::MockRequest.env_for("/?query=value&lang=en-US"))
|
41
|
+
controller.get[:query].should == 'value'
|
42
|
+
controller.get[:lang].should == 'en-US'
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should parse the URI correctly" do
|
46
|
+
controller = Specs.new(Rack::MockRequest.env_for("http://localhost:4000/slaughterhouse/5"))
|
47
|
+
controller.uri.should == '/slaughterhouse/5'
|
48
|
+
|
49
|
+
controller = Specs.new(Rack::MockRequest.env_for("/slaughterhouse/5"))
|
50
|
+
controller.uri.should == '/slaughterhouse/5'
|
51
|
+
|
52
|
+
controller = Specs.new(Rack::MockRequest.env_for(""))
|
53
|
+
controller.uri.should == '/'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should provide url accessor for resource index route' do
|
57
|
+
controller = Resources.new(Rack::MockRequest.env_for("/resources"))
|
58
|
+
controller.uri.should == controller.url(:resources)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should provide url accessor for resource show route' do
|
62
|
+
resource = Model.new
|
63
|
+
resource.id = 1
|
64
|
+
controller = Resources.new(Rack::MockRequest.env_for("/resources/1"))
|
65
|
+
controller.uri.should == controller.url(:resource, resource)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
describe "Halcyon" 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 provide the path of the application root directory" do
|
14
|
+
Halcyon.root.should == Dir.pwd
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should provide quick access to the configuration hash" do
|
18
|
+
Halcyon.config.is_a?(Hash).should.be.true?
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should provide environment label" do
|
22
|
+
Halcyon.environment.should == :development
|
23
|
+
Halcyon.environment.should == Halcyon.config[:environment]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should provide universal access to a logger" do
|
27
|
+
# We assume Logger here because, you know, we're gods of the test
|
28
|
+
Halcyon.logger.is_a?(Logger).should.be.true?
|
29
|
+
# And this is just a side affect of making the logger universally accessible
|
30
|
+
{}.logger.is_a?(Logger).should.be.true?
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should provide the (estimated) application name" do
|
34
|
+
# We set this above
|
35
|
+
Halcyon.app.should == "Specs"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should provide sane default paths for essential components" do
|
39
|
+
Halcyon.paths.is_a?(Hash).should.be.true?
|
40
|
+
Halcyon.paths[:controller].should == Halcyon.root/"app"
|
41
|
+
Halcyon.paths[:lib].should == Halcyon.root/"lib"
|
42
|
+
Halcyon.paths[:config].should == Halcyon.root/"config"
|
43
|
+
Halcyon.paths[:init].should == Halcyon.root/"config"/"{init,initialize}"
|
44
|
+
Halcyon.paths[:log].should == Halcyon.root/"log"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should provide configurable attribute definition for quick access to specific configuration values" do
|
48
|
+
test_method = "oracle"
|
49
|
+
method_count = Halcyon.methods.length
|
50
|
+
Halcyon.configurable("oracle")
|
51
|
+
(Halcyon.methods.length - method_count).should == 2
|
52
|
+
Halcyon.method(test_method.to_sym).is_a?(Method).should.be.true?
|
53
|
+
Halcyon.method("#{test_method}=".to_sym).is_a?(Method).should.be.true?
|
54
|
+
Halcyon.send("#{test_method}=".to_sym, 10)
|
55
|
+
Halcyon.send(test_method).should == Halcyon.config[test_method.to_sym]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should predefine quick access to the 'db' configuration value" do
|
59
|
+
Halcyon.db = 100
|
60
|
+
Halcyon.db.should == Halcyon.config[:db]
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
describe "Halcyon::Logging" do
|
2
|
+
|
3
|
+
it "should set the default logger when none specified" do
|
4
|
+
Halcyon.send(:remove_const, :Logger)
|
5
|
+
Halcyon::Logging.set
|
6
|
+
Halcyon::Logger.ancestors.include?(::Logger).should.be.true?
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should set the logger type specified" do
|
10
|
+
Halcyon.send(:remove_const, :Logger)
|
11
|
+
Halcyon::Logging.set('Logger')
|
12
|
+
Halcyon::Logger.ancestors.include?(::Logger).should.be.true?
|
13
|
+
|
14
|
+
# Not running these because the above test is equivalent as well as
|
15
|
+
# throwing errors for folks who do not have Logging, Log4r, and
|
16
|
+
# Analogger installed.
|
17
|
+
|
18
|
+
# Halcyon.send(:remove_const, :Logger)
|
19
|
+
# Halcyon::Logging.set('Analogger')
|
20
|
+
# Halcyon::Logger.ancestors.include?(::Swiftcore::Analogger::Client).should.be.true?
|
21
|
+
#
|
22
|
+
# Halcyon.send(:remove_const, :Logger)
|
23
|
+
# Halcyon::Logging.set('Log4r')
|
24
|
+
# Halcyon::Logger.ancestors.include?(::Log4r::Logger).should.be.true?
|
25
|
+
#
|
26
|
+
# Halcyon.send(:remove_const, :Logger)
|
27
|
+
# Halcyon::Logging.set('Logging')
|
28
|
+
# Halcyon::Logger.ancestors.include?(::Logging::Logger).should.be.true?
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/spec/halcyon/router_spec.rb
CHANGED
@@ -1,32 +1,57 @@
|
|
1
|
-
describe "Halcyon::
|
1
|
+
describe "Halcyon::Application::Router" do
|
2
2
|
|
3
3
|
before do
|
4
|
-
@
|
4
|
+
@log = ''
|
5
|
+
@logger = Logger.new(StringIO.new(@log))
|
6
|
+
@config = $config.dup
|
7
|
+
@config[:logger] = @logger
|
8
|
+
Halcyon.config = @config
|
9
|
+
@app = Halcyon::Runner.new
|
5
10
|
end
|
6
11
|
|
7
|
-
it "should
|
12
|
+
it "should prepare routes correctly when written correctly" do
|
8
13
|
# routes have been defined for Specr
|
9
|
-
Halcyon::
|
10
|
-
Halcyon::
|
14
|
+
Halcyon::Application::Router.routes.should.not == []
|
15
|
+
Halcyon::Application::Router.routes.length.should > 0
|
11
16
|
end
|
12
17
|
|
13
18
|
it "should match URIs to the correct route" do
|
14
|
-
|
19
|
+
request = Rack::Request.new(Rack::MockRequest.env_for('/'))
|
20
|
+
Halcyon::Application::Router.route(request)[:action].should == 'index'
|
15
21
|
end
|
16
22
|
|
17
23
|
it "should use the default route if no matching route is found" do
|
18
|
-
|
19
|
-
|
24
|
+
# missing instead of not_found because we gave a different default route
|
25
|
+
request = Rack::Request.new(Rack::MockRequest.env_for("/erroneous/path/#{rand}/#{rand}"))
|
26
|
+
Halcyon::Application::Router.route(request)[:action].should == 'missing'
|
27
|
+
|
28
|
+
request = Rack::Request.new(Rack::MockRequest.env_for("/random/#{rand}/#{rand}"))
|
29
|
+
Halcyon::Application::Router.route(request)[:action].should == 'missing'
|
20
30
|
end
|
21
31
|
|
22
32
|
it "should map params in routes to parameters" do
|
23
|
-
|
24
|
-
|
25
|
-
|
33
|
+
request = Rack::Request.new(Rack::MockRequest.env_for('/hello/Matt'))
|
34
|
+
response = Halcyon::Application::Router.route(request)
|
35
|
+
response[:action].should == 'greeter'
|
36
|
+
response[:name].should == 'Matt'
|
26
37
|
end
|
27
38
|
|
28
39
|
it "should supply arbitrary routing param values included as a param even if not in the URI" do
|
29
|
-
|
40
|
+
request = Rack::Request.new(Rack::MockRequest.env_for('/'))
|
41
|
+
request.env['rack.input'] << "arbitrary=random"
|
42
|
+
Halcyon::Application::Router.route(request)[:arbitrary].should == 'random'
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should match index method of resources" do
|
46
|
+
index_req = Rack::Request.new(Rack::MockRequest.env_for('/resources'))
|
47
|
+
Halcyon::Application::Router.route(index_req)[:controller].should == 'resources'
|
48
|
+
Halcyon::Application::Router.route(index_req)[:action].should == 'index'
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should match show method of resource" do
|
52
|
+
show_req = Rack::Request.new(Rack::MockRequest.env_for('/resources/id'))
|
53
|
+
Halcyon::Application::Router.route(show_req)[:controller].should == 'resources'
|
54
|
+
Halcyon::Application::Router.route(show_req)[:action].should == 'show'
|
30
55
|
end
|
31
56
|
|
32
57
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Kernel
|
2
|
+
alias_method :__warn, :warn
|
3
|
+
def warn(msg)
|
4
|
+
$warning = msg
|
5
|
+
__warn(msg) if $do_warns
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "Halcyon::Runner" do
|
10
|
+
|
11
|
+
before do
|
12
|
+
@log = ''
|
13
|
+
@logger = Logger.new(StringIO.new(@log))
|
14
|
+
@config = $config.dup
|
15
|
+
@config[:logger] = @logger
|
16
|
+
Halcyon.config = @config
|
17
|
+
@app = Halcyon::Runner.new
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should warn if a non-existent config file is loaded" do
|
21
|
+
$do_warns = false
|
22
|
+
path = Halcyon.root/'config'/'config.yml'
|
23
|
+
Halcyon::Runner.load_config(path).nil?.should == true
|
24
|
+
$warning.should =~ %r{#{path} not found}
|
25
|
+
$do_warns = true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should set up logging according to configuration" do
|
29
|
+
time = Time.now.to_s
|
30
|
+
@app.logger.debug "Test message for #{time}"
|
31
|
+
@log.should =~ /Test message for #{time}/
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should recognize what application it is running as" do
|
35
|
+
# Without setting explicitly in config
|
36
|
+
Halcyon.app.should == Halcyon.root.split('/').last.camel_case
|
37
|
+
|
38
|
+
# With setting explicitly in config
|
39
|
+
Halcyon.config[:app] = 'Specr'
|
40
|
+
Halcyon::Runner.new
|
41
|
+
Halcyon.app.should == 'Specr'
|
42
|
+
|
43
|
+
# Setting directly
|
44
|
+
Halcyon.app = 'Specr2'
|
45
|
+
Halcyon.app.should == 'Specr2'
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should proxy calls to Halcyon::Application" do
|
49
|
+
status, headers, body = @app.call(Rack::MockRequest.env_for('/'))
|
50
|
+
status.should == 200
|
51
|
+
body.body[0].should == Specs.new(Rack::MockRequest.env_for('/')).send(:index).to_json
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,21 +1,87 @@
|
|
1
|
-
require 'halcyon
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'halcyon')
|
2
2
|
require 'rack/mock'
|
3
|
+
require 'logger'
|
3
4
|
|
4
|
-
|
5
|
+
# Default Settings
|
5
6
|
|
6
|
-
|
7
|
+
$config = {
|
8
|
+
:allow_from => :all,
|
9
|
+
:environment => :development,
|
10
|
+
:logger => nil,
|
11
|
+
:logging => {
|
12
|
+
:level => 'debug'
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
# Testing Application
|
17
|
+
|
18
|
+
# Default controller
|
19
|
+
class Application < Halcyon::Controller; end
|
20
|
+
|
21
|
+
# Weird edge-case controller
|
22
|
+
class Specs < Application
|
7
23
|
|
8
|
-
|
9
|
-
|
10
|
-
r.match('/').to(:action => 'index', :arbitrary => 'random')
|
24
|
+
def greeter
|
25
|
+
ok("Hello #{params[:name]}")
|
11
26
|
end
|
12
27
|
|
13
|
-
def index
|
28
|
+
def index
|
14
29
|
ok('Found')
|
15
30
|
end
|
16
31
|
|
17
|
-
def
|
18
|
-
|
32
|
+
def cause_exception
|
33
|
+
raise Exception.new("Oops!")
|
34
|
+
end
|
35
|
+
|
36
|
+
def call_nonexistent_method
|
37
|
+
hash = Hash.new
|
38
|
+
hash.please_dont_exist_and_please_throw_no_method_error
|
39
|
+
ok
|
19
40
|
end
|
20
41
|
|
42
|
+
private
|
43
|
+
|
44
|
+
def undispatchable_private_method
|
45
|
+
"it's private, so it won't be found by the dispatcher"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
# Resources controller
|
51
|
+
class Resources < Application
|
52
|
+
|
53
|
+
def index
|
54
|
+
ok('List of resources')
|
55
|
+
end
|
56
|
+
|
57
|
+
def show
|
58
|
+
ok("One resource: #{params[:id]}")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Models
|
63
|
+
|
64
|
+
class Model
|
65
|
+
attr_accessor :id
|
66
|
+
end
|
67
|
+
|
68
|
+
# Environment
|
69
|
+
|
70
|
+
Halcyon.configurable_attr(:environment)
|
71
|
+
|
72
|
+
# Testing routes
|
73
|
+
|
74
|
+
Halcyon::Application.route do |r|
|
75
|
+
r.resources :resources
|
76
|
+
|
77
|
+
r.match('/hello/:name').to(:controller => 'specs', :action => 'greeter')
|
78
|
+
r.match('/:action').to(:controller => 'specs')
|
79
|
+
r.match('/:controller/:action').to()
|
80
|
+
r.match('/').to(:controller => 'specs', :action => 'index', :arbitrary => 'random')
|
81
|
+
# r.default_routes
|
82
|
+
{:action => 'missing'}
|
83
|
+
end
|
84
|
+
|
85
|
+
Halcyon::Application.startup do |config|
|
86
|
+
$started = true
|
21
87
|
end
|