halcyon 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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