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,38 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
|
3
|
+
module Halcyon
|
4
|
+
class Client
|
5
|
+
private
|
6
|
+
|
7
|
+
def request(req, headers={})
|
8
|
+
# set default headers
|
9
|
+
req["Content-Type"] = CONTENT_TYPE
|
10
|
+
req["User-Agent"] = USER_AGENT
|
11
|
+
|
12
|
+
# apply provided headers
|
13
|
+
self.headers.merge(headers).each do |(header, value)|
|
14
|
+
req[header] = value
|
15
|
+
end
|
16
|
+
|
17
|
+
# prepare and send HTTPS request
|
18
|
+
serv = Net::HTTP.new(self.uri.host, self.uri.port)
|
19
|
+
serv.use_ssl = true if self.uri.scheme == 'https'
|
20
|
+
res = serv.start {|http|http.request(req)}
|
21
|
+
|
22
|
+
# parse response
|
23
|
+
body = JSON.parse(res.body).to_mash
|
24
|
+
|
25
|
+
# handle non-successes
|
26
|
+
if self.options[:raise_exceptions] && !res.kind_of?(Net::HTTPSuccess)
|
27
|
+
raise self.class.const_get(Exceptions::HTTP_STATUS_CODES[body[:status]].tr(' ', '_').camel_case.gsub(/( |\-)/,'')).new
|
28
|
+
end
|
29
|
+
|
30
|
+
# return response
|
31
|
+
body
|
32
|
+
rescue Halcyon::Exceptions::Base => e
|
33
|
+
# log exception if logger is in place
|
34
|
+
raise
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
module Halcyon
|
2
|
+
|
3
|
+
# The base controller providing methods application controllers will need.
|
4
|
+
class Controller
|
5
|
+
include Exceptions
|
6
|
+
|
7
|
+
attr_accessor :env
|
8
|
+
attr_accessor :request
|
9
|
+
attr_accessor :session
|
10
|
+
attr_accessor :cookies
|
11
|
+
|
12
|
+
# Sets the <tt>@env</tt> and <tt>@request</tt> instance variables, used by
|
13
|
+
# various helping methods.
|
14
|
+
# +env+ the request environment details
|
15
|
+
def initialize(env)
|
16
|
+
@env = env
|
17
|
+
@request = Rack::Request.new(@env)
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
|
22
|
+
# Not implemented.
|
23
|
+
def before method, &proc
|
24
|
+
raise NotImplemented.new
|
25
|
+
end
|
26
|
+
|
27
|
+
# Not implemented.
|
28
|
+
def after method, &proc
|
29
|
+
raise NotImplemented.new
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the request params and the route params.
|
35
|
+
#
|
36
|
+
# Returns Hash:{route_params, get_params, post_params}.to_mash
|
37
|
+
def params
|
38
|
+
self.request.params.merge(self.env['halcyon.route']).to_mash
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns any POST params, excluding GET and route params.
|
42
|
+
#
|
43
|
+
# Returns Hash:{...}.to_mash
|
44
|
+
def post
|
45
|
+
self.request.POST.to_mash
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns any GET params, excluding POST and route params.
|
49
|
+
#
|
50
|
+
# Returns Hash:{...}.to_mash
|
51
|
+
def get
|
52
|
+
self.request.GET.to_mash
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns query params (usually the GET params).
|
56
|
+
#
|
57
|
+
# Returns Hash:{...}.to_mash
|
58
|
+
def query_params
|
59
|
+
Rack::Utils.parse_query(self.env['QUERY_STRING']).to_mash
|
60
|
+
end
|
61
|
+
|
62
|
+
# The path of the request URL.
|
63
|
+
#
|
64
|
+
# Returns String:/path/of/request
|
65
|
+
def uri
|
66
|
+
# special parsing is done to remove the protocol, host, and port that
|
67
|
+
# some Handlers leave in there. (Fixes inconsistencies.)
|
68
|
+
URI.parse(self.env['REQUEST_URI'] || self.env['PATH_INFO']).path
|
69
|
+
end
|
70
|
+
|
71
|
+
# The request method.
|
72
|
+
#
|
73
|
+
# Returns Symbol:get|post|put|delete
|
74
|
+
def method
|
75
|
+
self.env['REQUEST_METHOD'].downcase.to_sym
|
76
|
+
end
|
77
|
+
|
78
|
+
# Formats message into the standard success response hash, with a status of
|
79
|
+
# 200 (the standard success response).
|
80
|
+
# +body+ the body of the response
|
81
|
+
#
|
82
|
+
# The <tt>body</tt> defaults to <tt>String:"OK"</tt> but can be anything,
|
83
|
+
# including hashes, arrays, and literal values.
|
84
|
+
#
|
85
|
+
# Alternatively, if you choose to modify the message format, add a key in
|
86
|
+
# addition to <tt>:status</tt> and <tt>:body</tt>. For example:
|
87
|
+
# <tt>{:status=>200,:body=>'OK', :stats=>[...], :receipt=>...}</tt>
|
88
|
+
#
|
89
|
+
# Changes to this method described above should be reflected for in the
|
90
|
+
# clients as well.
|
91
|
+
#
|
92
|
+
# Aliases
|
93
|
+
# <tt>success</tt>
|
94
|
+
#
|
95
|
+
# Returns Hash:{:status=>200, :body=>body}
|
96
|
+
def ok(body='OK')
|
97
|
+
{:status => 200, :body => body}
|
98
|
+
end
|
99
|
+
alias_method :success, :ok
|
100
|
+
|
101
|
+
# Formats message into the standard response hash, with a status of 404
|
102
|
+
# (the standard "Not Found" response value).
|
103
|
+
# +body+ the body of the response
|
104
|
+
#
|
105
|
+
# The <tt>body</tt> defaults to <tt>String:"Not Found"</tt> but can be
|
106
|
+
# anything, including hashes, arrays, and literal values. However, it is
|
107
|
+
# strongly discouraged since the <tt>body</tt> should simply describe the
|
108
|
+
# problem with processing their request.
|
109
|
+
#
|
110
|
+
# Alternatively, if you choose to modify the message format, add a key in
|
111
|
+
# addition to <tt>:status</tt> and <tt>:body</tt>. For example:
|
112
|
+
# <tt>{:status=>404,:body=>'Not Found', :suggestions=>[...]}</tt>
|
113
|
+
#
|
114
|
+
# Changes to this method described above should be reflected for in the
|
115
|
+
# clients as well.
|
116
|
+
#
|
117
|
+
# Aliases
|
118
|
+
# <tt>missing</tt>
|
119
|
+
#
|
120
|
+
# Returns Hash:{:status=>404, :body=>body}
|
121
|
+
def not_found(body='Not Found')
|
122
|
+
{:status => 404, :body => body}
|
123
|
+
end
|
124
|
+
alias_method :missing, :not_found
|
125
|
+
|
126
|
+
# Returns the name of the controller in path form.
|
127
|
+
def self.controller_name
|
128
|
+
@controller_name ||= self.name.to_const_path
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns the name of the controller in path form.
|
132
|
+
def controller_name
|
133
|
+
self.class.controller_name
|
134
|
+
end
|
135
|
+
|
136
|
+
# Generates a URL based on the given name and passed
|
137
|
+
# options. Used with named routes and resources:
|
138
|
+
#
|
139
|
+
# url(:users) # => "/users"
|
140
|
+
# url(:admin_permissons) # => "/admin/permissions"
|
141
|
+
# url(:user, @user) # => "/users/1"
|
142
|
+
#
|
143
|
+
# Based on the identical method of Merb's controller.
|
144
|
+
def url(name, rparams={})
|
145
|
+
Halcyon::Application::Router.generate(name, rparams,
|
146
|
+
{ :controller => controller_name,
|
147
|
+
:action => method
|
148
|
+
}
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
data/lib/halcyon/exceptions.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
-
#--
|
2
|
-
# Created by Matt Todd on 2007-12-14.
|
3
|
-
# Copyright (c) 2007. All rights reserved.
|
4
|
-
#++
|
5
|
-
|
6
|
-
#--
|
7
|
-
# module
|
8
|
-
#++
|
9
|
-
|
10
1
|
module Halcyon
|
2
|
+
|
3
|
+
# Included into Halcyon::Application in order to provide exception classes
|
4
|
+
# like NotModified, OK, Forbidden, or NotFound to simplify generating error
|
5
|
+
# methods and handling specific error instances.
|
6
|
+
#
|
7
|
+
# It is not intended for these exceptions to be destructive or process-ending
|
8
|
+
# in the least, only to simplify finishing up processing and relaying
|
9
|
+
# appropriate status to the caller/client.
|
10
|
+
#
|
11
|
+
# These classes inherit from StandardError because it is convenient to raise
|
12
|
+
# a given status and let Halcyon's dispatcher handle sending the message to
|
13
|
+
# the client, but it is possible to just instantiate an object without
|
14
|
+
# throwing an exception if necessary.
|
11
15
|
module Exceptions #:nodoc:
|
12
16
|
|
13
17
|
#--
|
@@ -15,19 +19,37 @@ module Halcyon
|
|
15
19
|
#++
|
16
20
|
|
17
21
|
class Base < StandardError #:nodoc:
|
18
|
-
attr_accessor :status, :
|
19
|
-
def initialize(status,
|
22
|
+
attr_accessor :status, :body
|
23
|
+
def initialize(status, body)
|
20
24
|
@status = status
|
21
|
-
@
|
22
|
-
super "[#{@status}] #{@
|
25
|
+
@body = body
|
26
|
+
super "[#{@status}] #{@body}"
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
26
30
|
#--
|
27
|
-
# HTTP
|
31
|
+
# HTTP Status Codes and Errors
|
28
32
|
#++
|
29
33
|
|
30
|
-
|
34
|
+
HTTP_STATUS_CODES = {
|
35
|
+
100 => 'Continue',
|
36
|
+
101 => 'Switching Protocols',
|
37
|
+
102 => 'Processing',
|
38
|
+
200 => 'OK',
|
39
|
+
201 => 'Created',
|
40
|
+
202 => 'Accepted',
|
41
|
+
203 => 'Non-Authoritative Information',
|
42
|
+
204 => 'No Content',
|
43
|
+
205 => 'Reset Content',
|
44
|
+
206 => 'Partial Content',
|
45
|
+
207 => 'Multi-Status',
|
46
|
+
300 => 'Multiple Choices',
|
47
|
+
301 => 'Moved Permanently',
|
48
|
+
302 => 'Moved Temporarily',
|
49
|
+
303 => 'See Other',
|
50
|
+
304 => 'Not Modified',
|
51
|
+
305 => 'Use Proxy',
|
52
|
+
307 => 'Temporary Redirect',
|
31
53
|
400 => 'Bad Request',
|
32
54
|
401 => 'Unauthorized',
|
33
55
|
402 => 'Payment Required',
|
@@ -44,55 +66,41 @@ module Halcyon
|
|
44
66
|
413 => 'Request Entity Too Large',
|
45
67
|
414 => 'Request-URI Too Large',
|
46
68
|
415 => 'Unsupported Media Type',
|
69
|
+
416 => 'Requested Range Not Satisfiable',
|
70
|
+
417 => 'Expectation Failed',
|
71
|
+
422 => 'Unprocessable Entity',
|
72
|
+
423 => 'Locked',
|
73
|
+
424 => 'Failed Dependency',
|
74
|
+
425 => 'No Code',
|
75
|
+
426 => 'Upgrade Required',
|
47
76
|
500 => 'Internal Server Error',
|
48
77
|
501 => 'Not Implemented',
|
49
78
|
502 => 'Bad Gateway',
|
50
79
|
503 => 'Service Unavailable',
|
51
80
|
504 => 'Gateway Time-out',
|
52
|
-
505 => 'HTTP Version not supported'
|
81
|
+
505 => 'HTTP Version not supported',
|
82
|
+
506 => 'Variant Also Negotiates',
|
83
|
+
507 => 'Insufficient Storage',
|
84
|
+
510 => 'Not Extended'
|
53
85
|
}
|
86
|
+
|
87
|
+
# Added extended HTTP status codes found from
|
88
|
+
# http://www.askapache.com/htaccess/apache-status-code-headers-errordocument.html
|
89
|
+
|
90
|
+
#--
|
91
|
+
# Classify Status Codes
|
92
|
+
#++
|
93
|
+
|
94
|
+
HTTP_STATUS_CODES.to_a.each do |http_status|
|
95
|
+
status, body = http_status
|
96
|
+
class_eval <<-"end;"
|
97
|
+
class #{body.gsub(/( |\-)/,'')} < Halcyon::Exceptions::Base
|
98
|
+
def initialize(s=#{status}, b='#{body}')
|
99
|
+
super
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end;
|
103
|
+
end
|
104
|
+
|
54
105
|
end
|
55
106
|
end
|
56
|
-
|
57
|
-
# Taken from Rack's definition:
|
58
|
-
# http://chneukirchen.org/darcs/darcsweb.cgi?r=rack;a=plainblob;f=/lib/rack/utils.rb
|
59
|
-
#
|
60
|
-
# HTTP_STATUS_CODES = {
|
61
|
-
# 100 => 'Continue',
|
62
|
-
# 101 => 'Switching Protocols',
|
63
|
-
# 200 => 'OK',
|
64
|
-
# 201 => 'Created',
|
65
|
-
# 202 => 'Accepted',
|
66
|
-
# 203 => 'Non-Authoritative Information',
|
67
|
-
# 204 => 'No Content',
|
68
|
-
# 205 => 'Reset Content',
|
69
|
-
# 206 => 'Partial Content',
|
70
|
-
# 300 => 'Multiple Choices',
|
71
|
-
# 301 => 'Moved Permanently',
|
72
|
-
# 302 => 'Moved Temporarily',
|
73
|
-
# 303 => 'See Other',
|
74
|
-
# 304 => 'Not Modified',
|
75
|
-
# 305 => 'Use Proxy',
|
76
|
-
# 400 => 'Bad Request',
|
77
|
-
# 401 => 'Unauthorized',
|
78
|
-
# 402 => 'Payment Required',
|
79
|
-
# 403 => 'Forbidden',
|
80
|
-
# 404 => 'Not Found',
|
81
|
-
# 405 => 'Method Not Allowed',
|
82
|
-
# 406 => 'Not Acceptable',
|
83
|
-
# 407 => 'Proxy Authentication Required',
|
84
|
-
# 408 => 'Request Time-out',
|
85
|
-
# 409 => 'Conflict',
|
86
|
-
# 410 => 'Gone',
|
87
|
-
# 411 => 'Length Required',
|
88
|
-
# 412 => 'Precondition Failed',
|
89
|
-
# 413 => 'Request Entity Too Large',
|
90
|
-
# 414 => 'Request-URI Too Large',
|
91
|
-
# 415 => 'Unsupported Media Type',
|
92
|
-
# 500 => 'Internal Server Error',
|
93
|
-
# 501 => 'Not Implemented',
|
94
|
-
# 502 => 'Bad Gateway',
|
95
|
-
# 503 => 'Service Unavailable',
|
96
|
-
# 504 => 'Gateway Time-out',
|
97
|
-
# 505 => 'HTTP Version not supported'
|
98
|
-
# }
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Halcyon
|
2
|
+
module Logging
|
3
|
+
|
4
|
+
autoload :Helpers, 'halcyon/logging/helpers'
|
5
|
+
|
6
|
+
autoload :Logger, 'halcyon/logging/logger'
|
7
|
+
autoload :Logging, 'halcyon/logging/logging'
|
8
|
+
autoload :Analogger, 'halcyon/logging/analogger'
|
9
|
+
autoload :Log4r, 'halcyon/logging/log4r'
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
# Sets up the Halcyon::Logger constant to reflect the logging
|
14
|
+
# configuration settings.
|
15
|
+
# +logger+ the name of the logging type
|
16
|
+
#
|
17
|
+
# Configs
|
18
|
+
# <tt>Halcyon.config[:logging][:type]</tt> #=> <tt>String:Logger</tt>
|
19
|
+
# Logger => specifies Logger
|
20
|
+
# Logging => specifies Logging
|
21
|
+
# Analogger => specifies Analogger
|
22
|
+
# Log4r => specifies Log4r
|
23
|
+
def set(logger = 'Logger')
|
24
|
+
Halcyon.send(:remove_const, :Logger) if Halcyon.const_defined? :Logger
|
25
|
+
Halcyon.const_set :Logger, Halcyon::Logging.const_get(logger)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'swiftcore/Analogger/Client'
|
2
|
+
module Halcyon
|
3
|
+
module Logging
|
4
|
+
class Analogger < Swiftcore::Analogger::Client
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def setup(config)
|
9
|
+
self.new((config[:app] || Halcyon.app), config[:host], config[:port].to_s, config[:key])
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
%w(debug info warn error fatal unknown).each do |level|
|
15
|
+
|
16
|
+
eval <<-"end;"
|
17
|
+
def #{level}(message)
|
18
|
+
self.log('#{level}', message)
|
19
|
+
end
|
20
|
+
end;
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def <<(message)
|
25
|
+
# Should << be assumed as INFO level?
|
26
|
+
self.log('info', message)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Halcyon
|
2
|
+
module Logging
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
def self.included(target)
|
6
|
+
target.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
# The current application logger instance.
|
10
|
+
#
|
11
|
+
# Examples
|
12
|
+
# self.logger.debug "Deleting user's cached data"
|
13
|
+
#
|
14
|
+
# Returns Logger:logger
|
15
|
+
def logger
|
16
|
+
Halcyon.logger
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
|
21
|
+
# The current application logger instance, usable in the class context.
|
22
|
+
# This means you can call <tt>self.logger</tt> from inside of class
|
23
|
+
# methods, filters, etc.
|
24
|
+
#
|
25
|
+
# Examples
|
26
|
+
# self.logger.debug "Test message"
|
27
|
+
#
|
28
|
+
# Returns Logger:logger
|
29
|
+
def logger
|
30
|
+
Halcyon.logger
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|