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,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
|