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,247 @@
1
+ module Halcyon
2
+
3
+ # The core of Halcyon on the server side is the Halcyon::Application class
4
+ # which handles dispatching requests and responding with appropriate messages
5
+ # to the client (which can be specified).
6
+ #
7
+ # Manages shutting down and starting up hooks, routing, dispatching, etc.
8
+ # Also restricts the requests to acceptable clients, defaulting to all.
9
+ #
10
+ class Application
11
+ include Exceptions
12
+
13
+ autoload :Router, 'halcyon/application/router'
14
+
15
+ attr_accessor :session
16
+
17
+ DEFAULT_OPTIONS = {
18
+ :root => Dir.pwd,
19
+ :logging => {
20
+ :type => 'Logger',
21
+ :level => 'info'
22
+ },
23
+ :allow_from => :all
24
+ }.to_mash
25
+
26
+ # Initializes the app:
27
+ # * runs startup hooks
28
+ # * registers shutdown hooks
29
+ #
30
+ def initialize
31
+ self.logger.info "Starting up..."
32
+
33
+ self.hooks[:startup].call(Halcyon.config) if self.hooks[:startup]
34
+
35
+ # clean after ourselves and get prepared to start serving things
36
+ self.logger.debug "Starting GC."
37
+ GC.start
38
+
39
+ self.logger.info "Started. PID is #{$$}"
40
+
41
+ at_exit do
42
+ self.logger.info "Shutting down #{$$}."
43
+ self.hooks[:shutdown].call(Halcyon.config) if self.hooks[:shutdown]
44
+ self.logger.info "Done."
45
+ end
46
+ end
47
+
48
+ # Sets up the request and response objects for use in the controllers and
49
+ # dispatches requests. Renders response data as JSON for response.
50
+ # +env+ the request environment details
51
+ #
52
+ # The internal router (which inherits from the Merb router) is sent the
53
+ # request to pass back the route for the dispatcher to call. This route is
54
+ # stored in <tt>env['halcyon.route']</tt> (as per the Rack spec).
55
+ #
56
+ # Configs
57
+ # <tt>Halcyon.config[:allow_from]</tt> #=> (default) <tt>:all</tt>
58
+ # :all => does not restrict requests from any User-Agent
59
+ # :local => restricts requests to only local requests (from
60
+ # localhost, 0.0.0.0, 127.0.0.1)
61
+ # :halcyon_clients => restricts to only Halcyon clients (identified by
62
+ # User-Agent)
63
+ #
64
+ # Exceptions
65
+ # If a request raises an exception that inherits from
66
+ # <tt>Halcyon::Exceptions::Base</tt> (<tt>NotFound</tt>, etc), then the
67
+ # response is sent with this information.
68
+ # If a request raises any other kind of <tt>Exception</tt>, it is logged
69
+ # as an error and a <tt>500 Internal Server Error</tt> is returned.
70
+ #
71
+ # Returns [Fixnum:status, {String:header => String:value}, [String:body].to_json]
72
+ #
73
+ def call(env)
74
+ timing = {:started => Time.now}
75
+
76
+ request = Rack::Request.new(env)
77
+ response = Rack::Response.new
78
+
79
+ response['Content-Type'] = "application/json"
80
+ response['User-Agent'] = "JSON/#{JSON::VERSION} Compatible (en-US) Halcyon::Application/#{Halcyon.version}"
81
+
82
+ begin
83
+ acceptable_request!(env)
84
+
85
+ env['halcyon.route'] = Router.route(request)
86
+ result = dispatch(env)
87
+ rescue Exceptions::Base => e
88
+ result = {:status => e.status, :body => e.body}
89
+ self.logger.info e.message
90
+ rescue Exception => e
91
+ result = {:status => 500, :body => 'Internal Server Error'}
92
+ self.logger.error "#{e.message}\n\t" << e.backtrace.join("\n\t")
93
+ end
94
+
95
+ response.status = result[:status]
96
+ response.write result.to_json
97
+
98
+ timing[:finished] = Time.now
99
+ timing[:total] = (((timing[:finished] - timing[:started])*1e4).round.to_f/1e4)
100
+ timing[:per_sec] = (((1.0/(timing[:total]))*1e2).round.to_f/1e2)
101
+
102
+ self.logger.info "[#{response.status}] #{URI.parse(env['REQUEST_URI'] || env['PATH_INFO']).path} (#{timing[:total]}s;#{timing[:per_sec]}req/s)"
103
+ # self.logger << "Session ID: #{self.session.id}\n" # TODO: Implement session
104
+ self.logger << "Params: #{filter_params_for_log(request, env).inspect}\n\n"
105
+
106
+ response.finish
107
+ end
108
+
109
+ # Dispatches the controller and action according the routed request.
110
+ # +env+ the request environment details, including "halcyon.route"
111
+ #
112
+ # If no <tt>:controller</tt> is specified, the default <tt>Application</tt>
113
+ # controller is dispatched to.
114
+ #
115
+ # Once the controller is selected and instantiated, the action is called,
116
+ # defaulting to <tt>:default</tt> if no action is provided.
117
+ #
118
+ # If the action called is not defined, a <tt>404 Not Found</tt> exception
119
+ # will be raised. This will be sent to the client as such, or handled by
120
+ # the Rack application container, such as the Rack Cascade middleware to
121
+ # failover to another application (such as Merb or Rails).
122
+ #
123
+ # Refer to Halcyon::Application::Router for more details on defining routes
124
+ # and for where to get further documentation.
125
+ #
126
+ # Returns (String|Array|Hash):body
127
+ #
128
+ def dispatch(env)
129
+ route = env['halcyon.route']
130
+ # make sure that the right controller/action is called based on the route
131
+ controller = case route[:controller]
132
+ when NilClass
133
+ # default to the Application controller
134
+ ::Application.new(env)
135
+ when String
136
+ # pulled from URL, so camelize (from merb/core_ext) and symbolize first
137
+ begin
138
+ Object.const_get(route[:controller].camel_case.to_sym).new(env)
139
+ rescue NameError => e
140
+ raise NotFound.new
141
+ end
142
+ end
143
+
144
+ # Establish the selected action, defaulting to +default+.
145
+ action = (route[:action] || 'default').to_sym
146
+
147
+ # Respond correctly that a non-existent action was specified if the
148
+ # method does not exist.
149
+ raise NotFound.new unless controller.methods.include?(action.to_s)
150
+
151
+ # if no errors have occured up to this point, the route should be fully
152
+ # valid and all exceptions raised should be treated as
153
+ # <tt>500 Internal Server Error</tt>s, which is handled by <tt>call</tt>.
154
+ controller.send(action)
155
+ end
156
+
157
+ # Filters unacceptable requests depending on the configuration of the
158
+ # <tt>:allow_from</tt> option.
159
+ #
160
+ # This method is not directly called by the user, instead being called
161
+ # in the #call method.
162
+ #
163
+ # Acceptable values include:
164
+ # <tt>:all</tt>:: allow every request to go through
165
+ # <tt>:halcyon_clients</tt>:: only allow Halcyon clients
166
+ # <tt>:local</tt>:: do not allow for requests from an outside host
167
+ #
168
+ # Raises Forbidden
169
+ #
170
+ def acceptable_request!(env)
171
+ case Halcyon.config[:allow_from].to_sym
172
+ when :all
173
+ # allow every request to go through
174
+ when :halcyon_clients
175
+ # only allow Halcyon clients
176
+ raise Forbidden.new unless env['USER_AGENT'] =~ /JSON\/1\.1\.\d+ Compatible \(en-US\) Halcyon::Client\(\d+\.\d+\.\d+\)/
177
+ when :local
178
+ # do not allow for requests from an outside host
179
+ raise Forbidden.new unless ['localhost', '127.0.0.1', '0.0.0.0'].member? env["REMOTE_ADDR"]
180
+ else
181
+ logger.warn "Unrecognized allow_from configuration value (#{Halcyon.config[:allow_from].to_s}); use all, halcyon_clients, or local. Allowing all requests."
182
+ end
183
+ end
184
+
185
+ # Assemble params for logging.
186
+ #
187
+ # This method exists to be overridden or method-chained to filter out params
188
+ # from being logged for applications with sensitive data like passwords.
189
+ #
190
+ # Returns Hash:params_to_log
191
+ #
192
+ def filter_params_for_log(request, env)
193
+ request.params.merge(env['halcyon.route'])
194
+ end
195
+
196
+ # See the documentation for generated apps in <tt>config/initialze/hooks.rb</tt>
197
+ #
198
+ def hooks
199
+ self.class.hooks
200
+ end
201
+
202
+ class << self
203
+
204
+ attr_accessor :hooks
205
+
206
+ # See the documentation for generated apps in <tt>config/initialze/hooks.rb</tt>
207
+ #
208
+ def hooks
209
+ @hooks ||= {}
210
+ end
211
+
212
+ # Defines routes for the application.
213
+ #
214
+ # Refer to Halcyon::Application::Router for documentation and resources.
215
+ #
216
+ def route
217
+ if block_given?
218
+ Router.prepare do |router|
219
+ Router.default_to yield(router) || {:controller => 'application', :action => 'not_found'}
220
+ end
221
+ end
222
+ end
223
+
224
+ # Sets the startup hook to the proc.
225
+ #
226
+ # Use this to initialize application-wide resources, such as database
227
+ # connections.
228
+ #
229
+ # Use initializers where possible.
230
+ #
231
+ def startup &hook
232
+ self.hooks[:startup] = hook
233
+ end
234
+
235
+ # Sets the shutdown hook to the proc.
236
+ #
237
+ # Close any resources opened in the +startup+ hook.
238
+ #
239
+ def shutdown &hook
240
+ self.hooks[:shutdown] = hook
241
+ end
242
+
243
+ end
244
+
245
+ end
246
+
247
+ end
@@ -0,0 +1,86 @@
1
+ %w(rubygems merb-core/core_ext merb-core/dispatch/router uri).each {|dep|require dep}
2
+
3
+ module Halcyon
4
+ class Application
5
+
6
+ # = Routing
7
+ #
8
+ # Handles routing.
9
+ #
10
+ # == Usage
11
+ #
12
+ # class Halcyon::Application
13
+ # route do |r|
14
+ # r.match('/path/to/match').to(:controller => 'a', :action => 'b')
15
+ # {:action => 'not_found'} # the default route
16
+ # end
17
+ # end
18
+ #
19
+ # == Default Routes
20
+ #
21
+ # Supplying a default route if none of the others match is good practice,
22
+ # but is unnecessary as the predefined route is always, automatically,
23
+ # going to contain a redirection to the +not_found+ method which already
24
+ # exists in Halcyon::Controller. This method is freely overwritable, and
25
+ # is recommended for those that wish to handle unroutable requests
26
+ # themselves.
27
+ #
28
+ # In order to set a different default route, simply end the call to +route+
29
+ # with a hash containing the action (and optionally the module) to run.
30
+ #
31
+ # == The Hard Work
32
+ #
33
+ # The mechanics of the router are solely from the efforts of the Merb
34
+ # community. This functionality is completely ripped right out of Merb
35
+ # and makes it functional. All credit to them, and be sure to check out
36
+ # their great framework: if Halcyon isn't quite what you need, maybe Merb
37
+ # is.
38
+ #
39
+ # http://merbivore.com/
40
+ class Router < Merb::Router
41
+
42
+ class << self
43
+
44
+ # Retrieves the last value from the +route+ call in Halcyon::Controller
45
+ # and, if it's a Hash, sets it to +@@default_route+ to designate the
46
+ # failover route. If +route+ is not a Hash, though, the internal default
47
+ # should be used instead (as the last returned value is probably a Route
48
+ # object returned by the +r.match().to()+ call).
49
+ # +route+ the default route, or nothing to use <tt>not_found</tt>
50
+ #
51
+ # Used exclusively internally.
52
+ #
53
+ # Returns nothing
54
+ def default_to(route)
55
+ @@default_route = route.is_a?(Hash) ? route : {:action => 'not_found'}
56
+ end
57
+
58
+ # Called internally by the Halcyon::Controller#call method to match
59
+ # the current request against the currently defined routes. Returns the
60
+ # params list defined in the +to+ routing definition, opting for the
61
+ # default route if no match is made.
62
+ # +request+ the request object to route against
63
+ #
64
+ # Returns Hash:{:controller=>..., :action=>..., ...}
65
+ def route(request)
66
+ req = Struct.new(:path, :method, :params).new(request.path_info, request.request_method.downcase.to_sym, request.params)
67
+
68
+ # perform match
69
+ route = self.match(req)
70
+
71
+ # make sure a route is returned even if no match is found
72
+ if route[0].nil?
73
+ #return default route
74
+ self.logger.debug "No route found. Using default."
75
+ @@default_route
76
+ else
77
+ # params (including action and module if set) for the matching route
78
+ route[1]
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -1,49 +1,201 @@
1
- #--
2
- # Created by Matt Todd on 2007-12-14.
3
- # Copyright (c) 2007. All rights reserved.
4
- #++
5
-
6
- $:.unshift File.dirname(File.join('..', __FILE__))
7
- $:.unshift File.dirname(__FILE__)
8
-
9
- #--
10
- # dependencies
11
- #++
12
-
13
- %w(rubygems halcyon).each {|dep|require dep}
14
- begin
15
- require 'json/ext'
16
- rescue LoadError => e
17
- warn 'Using the Pure Ruby JSON... install the json gem to get faster JSON parsing.'
18
- require 'json/pure'
19
- end
20
-
21
- #--
22
- # module
23
- #++
1
+ %w(net/http uri json).each {|dep|require dep}
24
2
 
25
3
  module Halcyon
26
4
 
27
- # The Client library provides a simple way to package up a client lib to
28
- # simplify communicating with the accompanying Halcyon server app.
5
+ # = Building Custom Clients
6
+ #
7
+ # Once your Halcyon JSON Server App starts to take shape, it may be useful
8
+ # to begin to write tests on expected functionality, and then to implement
9
+ # API calls with a designated Client lib for your Ruby or Rails apps, etc.
10
+ # The Base class provides a standard implementation and several options for
11
+ # wrapping up functionality of your app from the server side into the
12
+ # client side so that you may begin to use response data.
13
+ #
14
+ # == Creating Your Client
15
+ #
16
+ # Creating a simple client can be as simple as this:
29
17
  #
30
- # = Usage
18
+ # class Simple < Halcyon::Client
19
+ # def greet(name)
20
+ # get("/hello/#{name}")
21
+ # end
22
+ # end
23
+ #
24
+ # The only thing simply may be actually using the Simple client you just
25
+ # created.
26
+ #
27
+ # But to actually get in and use the library, one has to take full
28
+ # advantage of the HTTP request methods, +get+, +post+, +put+, and
29
+ # +delete+. These methods simply return the JSON-parsed data from the
30
+ # server, effectively returning a hash with two key values, +status+ which
31
+ # contains the HTTP status code, and +body+ which contains the body of the
32
+ # content returned which can be any number of objects, including, but not
33
+ # limited to Hash, Array, Numeric, Nil, Boolean, String, etc.
34
+ #
35
+ # You are not limited to what your methods can call: they are arbitrarily
36
+ # and solely up to your whims and needs. It is simply a matter of good
37
+ # design and performance when it comes to structuring and implementing
38
+ # client actions which can be complex or simple series of requests to the
39
+ # server.
31
40
  #
32
- # For documentation on using Halcyon, check out the Halcyon::Server::Base and
33
- # Halcyon::Client::Base classes which contain much more usage documentation.
34
41
  class Client
35
- def self.version
36
- VERSION.join('.')
42
+ include Exceptions
43
+
44
+ USER_AGENT = "JSON/#{JSON::VERSION} Compatible (en-US) Halcyon::Client/#{Halcyon.version}"
45
+ CONTENT_TYPE = 'application/json'
46
+ DEFAULT_OPTIONS = {
47
+ :raise_exceptions => false
48
+ }
49
+
50
+ attr_accessor :uri # The server URI
51
+ attr_accessor :headers # Instance-wide headers
52
+ attr_accessor :options # Options
53
+
54
+ #--
55
+ # Initialization and setup
56
+ #++
57
+
58
+ # = Connecting to the Server
59
+ #
60
+ # Creates a new Client object to allow for requests and responses from
61
+ # the specified server.
62
+ #
63
+ # The +uri+ param contains the URL to the actual server, and should be in
64
+ # the format: "http://localhost:3801" or "http://app.domain.com:3401/"
65
+ #
66
+ # == Server Connections
67
+ #
68
+ # Connecting only occurs at the actual event that a request is performed,
69
+ # so there is no need to worry about closing connections or managing
70
+ # connections in general other than good object housecleaning. (Be nice
71
+ # to your Garbage Collector.)
72
+ #
73
+ # == Usage
74
+ #
75
+ # You can either provide a block to perform all of your requests and
76
+ # processing inside of or you can simply accept the object in response
77
+ # and call your request methods off of the returned object.
78
+ #
79
+ # Alternatively, you could do both.
80
+ #
81
+ # An example of creating and using a Simple client:
82
+ #
83
+ # class Simple < Halcyon::Client
84
+ # def greet(name)
85
+ # get("/hello/#{name}")
86
+ # end
87
+ # end
88
+ # Simple.new('http://localhost:3801') do |s|
89
+ # puts s.greet("Johnny").inspect
90
+ # end
91
+ #
92
+ # This should effectively call +inspect+ on a response hash similar to
93
+ # this:
94
+ #
95
+ # {:status => 200, :body => 'Hello Johnny'}
96
+ #
97
+ # Alternatively, you could perform the same with the following:
98
+ #
99
+ # s = Simple.new('http://localhost:3801')
100
+ # puts s.greet("Johnny").inspect
101
+ #
102
+ # This should generate the exact same outcome as the previous example,
103
+ # except that it is not executed in a block.
104
+ #
105
+ # The differences are purely semantic and of personal taste.
106
+ def initialize(uri, headers = {})
107
+ self.uri = URI.parse(uri)
108
+ self.headers = headers
109
+ self.options = DEFAULT_OPTIONS
110
+ if block_given?
111
+ yield self
112
+ end
113
+ end
114
+
115
+ def raise_exceptions!(setting = true)
116
+ self.options[:raise_exceptions] = setting
37
117
  end
38
118
 
39
119
  #--
40
- # module dependencies
120
+ # Request Handling
41
121
  #++
42
122
 
43
- autoload :Base, 'halcyon/client/base'
44
- autoload :Router, 'halcyon/client/router'
123
+ # Performs a GET request on the URI specified.
124
+ def get(uri, headers={})
125
+ req = Net::HTTP::Get.new(uri)
126
+ request(req, headers)
127
+ end
128
+
129
+ # Performs a POST request on the URI specified.
130
+ def post(uri, data = {}, headers={})
131
+ req = Net::HTTP::Post.new(uri)
132
+ req.body = format_body(data)
133
+ request(req, headers)
134
+ end
135
+
136
+ # Performs a DELETE request on the URI specified.
137
+ def delete(uri, headers={})
138
+ req = Net::HTTP::Delete.new(uri)
139
+ request(req, headers)
140
+ end
141
+
142
+ # Performs a PUT request on the URI specified.
143
+ def put(uri, data = {}, headers={})
144
+ req = Net::HTTP::Put.new(uri)
145
+ req.body = format_body(data)
146
+ request(req, headers)
147
+ end
148
+
149
+ private
150
+
151
+ # Performs an arbitrary HTTP request, receive the response, parse it with
152
+ # JSON, and return it to the caller. This is a private method because the
153
+ # user/developer should be quite satisfied with the +get+, +post+, +put+,
154
+ # and +delete+ methods.
155
+ #
156
+ # == Request Failures
157
+ #
158
+ # If the server responds with any kind of failure (anything with a status
159
+ # that isn't 200), Halcyon will in turn raise the respective exception
160
+ # (defined in Halcyon::Exceptions) which all inherit from
161
+ # +Halcyon::Exceptions+. It is up to the client to handle these
162
+ # exceptions specifically.
163
+ def request(req, headers={})
164
+ # set default headers
165
+ req["Content-Type"] = CONTENT_TYPE
166
+ req["User-Agent"] = USER_AGENT
167
+
168
+ # apply provided headers
169
+ self.headers.merge(headers).each do |(header, value)|
170
+ req[header] = value
171
+ end
172
+
173
+ # prepare and send HTTP request
174
+ res = Net::HTTP.start(self.uri.host, self.uri.port) {|http|http.request(req)}
175
+
176
+ # parse response
177
+ body = JSON.parse(res.body).to_mash
178
+
179
+ # handle non-successes
180
+ if self.options[:raise_exceptions] && !res.kind_of?(Net::HTTPSuccess)
181
+ raise self.class.const_get(Exceptions::HTTP_STATUS_CODES[body[:status]].tr(' ', '_').camel_case.gsub(/( |\-)/,'')).new
182
+ end
183
+
184
+ # return response
185
+ body
186
+ rescue Halcyon::Exceptions::Base => e
187
+ # log exception if logger is in place
188
+ raise
189
+ end
190
+
191
+ # Formats the data of a POST or PUT request (the body) into an acceptable
192
+ # format according to Net::HTTP for sending through as a Hash.
193
+ def format_body(data)
194
+ data = {:body => data} unless data.is_a? Hash
195
+ data.to_mash
196
+ # uses the Merb Hash#to_params method defined in merb/core_ext.
197
+ data.to_params
198
+ end
45
199
 
46
200
  end
47
201
  end
48
-
49
- %w(halcyon/client/exceptions).each {|dep|require dep}