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