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.
Files changed (60) hide show
  1. data/AUTHORS +1 -0
  2. data/LICENSE +20 -0
  3. data/README +107 -0
  4. data/Rakefile +8 -6
  5. data/bin/halcyon +3 -204
  6. data/lib/halcyon.rb +55 -42
  7. data/lib/halcyon/application.rb +247 -0
  8. data/lib/halcyon/application/router.rb +86 -0
  9. data/lib/halcyon/client.rb +187 -35
  10. data/lib/halcyon/client/ssl.rb +38 -0
  11. data/lib/halcyon/controller.rb +154 -0
  12. data/lib/halcyon/exceptions.rb +67 -59
  13. data/lib/halcyon/logging.rb +31 -0
  14. data/lib/halcyon/logging/analogger.rb +31 -0
  15. data/lib/halcyon/logging/helpers.rb +37 -0
  16. data/lib/halcyon/logging/log4r.rb +25 -0
  17. data/lib/halcyon/logging/logger.rb +20 -0
  18. data/lib/halcyon/logging/logging.rb +19 -0
  19. data/lib/halcyon/runner.rb +141 -0
  20. data/lib/halcyon/runner/commands.rb +141 -0
  21. data/lib/halcyon/runner/helpers.rb +9 -0
  22. data/lib/halcyon/runner/helpers/command_helper.rb +71 -0
  23. data/spec/halcyon/application_spec.rb +70 -0
  24. data/spec/halcyon/client_spec.rb +63 -0
  25. data/spec/halcyon/controller_spec.rb +68 -0
  26. data/spec/halcyon/halcyon_spec.rb +63 -0
  27. data/spec/halcyon/logging_spec.rb +31 -0
  28. data/spec/halcyon/router_spec.rb +37 -12
  29. data/spec/halcyon/runner_spec.rb +54 -0
  30. data/spec/spec_helper.rb +75 -9
  31. data/support/generators/halcyon/USAGE +0 -0
  32. data/support/generators/halcyon/halcyon_generator.rb +52 -0
  33. data/support/generators/halcyon/templates/README +26 -0
  34. data/support/generators/halcyon/templates/Rakefile +32 -0
  35. data/support/generators/halcyon/templates/app/application.rb +43 -0
  36. data/support/generators/halcyon/templates/config/config.yml +36 -0
  37. data/support/generators/halcyon/templates/config/init/environment.rb +11 -0
  38. data/support/generators/halcyon/templates/config/init/hooks.rb +39 -0
  39. data/support/generators/halcyon/templates/config/init/requires.rb +10 -0
  40. data/support/generators/halcyon/templates/config/init/routes.rb +50 -0
  41. data/support/generators/halcyon/templates/lib/client.rb +77 -0
  42. data/support/generators/halcyon/templates/runner.ru +8 -0
  43. data/support/generators/halcyon_flat/USAGE +0 -0
  44. data/support/generators/halcyon_flat/halcyon_flat_generator.rb +52 -0
  45. data/support/generators/halcyon_flat/templates/README +26 -0
  46. data/support/generators/halcyon_flat/templates/Rakefile +32 -0
  47. data/support/generators/halcyon_flat/templates/app.rb +49 -0
  48. data/support/generators/halcyon_flat/templates/lib/client.rb +17 -0
  49. data/support/generators/halcyon_flat/templates/runner.ru +8 -0
  50. metadata +73 -20
  51. data/lib/halcyon/client/base.rb +0 -261
  52. data/lib/halcyon/client/exceptions.rb +0 -41
  53. data/lib/halcyon/client/router.rb +0 -106
  54. data/lib/halcyon/server.rb +0 -62
  55. data/lib/halcyon/server/auth/basic.rb +0 -107
  56. data/lib/halcyon/server/base.rb +0 -774
  57. data/lib/halcyon/server/exceptions.rb +0 -41
  58. data/lib/halcyon/server/router.rb +0 -103
  59. data/spec/halcyon/error_spec.rb +0 -55
  60. 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
@@ -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, :error
19
- def initialize(status, error)
22
+ attr_accessor :status, :body
23
+ def initialize(status, body)
20
24
  @status = status
21
- @error = error
22
- super "[#{@status}] #{@error}"
25
+ @body = body
26
+ super "[#{@status}] #{@body}"
23
27
  end
24
28
  end
25
29
 
26
30
  #--
27
- # HTTP Error Codes and Errors
31
+ # HTTP Status Codes and Errors
28
32
  #++
29
33
 
30
- HTTP_ERROR_CODES = {
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