fragrant 0.0.1

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 (39) hide show
  1. data/LICENSE +22 -0
  2. data/README.md +6 -0
  3. data/bin/fragrant +14 -0
  4. data/lib/fragrant/address_manager.rb +80 -0
  5. data/lib/fragrant/vagrantfile_generator.rb +64 -0
  6. data/lib/fragrant.rb +323 -0
  7. data/spec/fragrant/environments_spec.rb +18 -0
  8. data/spec/fragrant/vagrantfile_generator_spec.rb +40 -0
  9. data/spec/fragrant/vms_spec.rb +26 -0
  10. data/spec/spec_helper.rb +15 -0
  11. data/vendor/grape/LICENSE +20 -0
  12. data/vendor/grape/lib/grape/api.rb +420 -0
  13. data/vendor/grape/lib/grape/cookies.rb +41 -0
  14. data/vendor/grape/lib/grape/endpoint.rb +377 -0
  15. data/vendor/grape/lib/grape/entity.rb +378 -0
  16. data/vendor/grape/lib/grape/exceptions/base.rb +17 -0
  17. data/vendor/grape/lib/grape/exceptions/validation_error.rb +10 -0
  18. data/vendor/grape/lib/grape/middleware/auth/basic.rb +30 -0
  19. data/vendor/grape/lib/grape/middleware/auth/digest.rb +30 -0
  20. data/vendor/grape/lib/grape/middleware/auth/oauth2.rb +72 -0
  21. data/vendor/grape/lib/grape/middleware/base.rb +154 -0
  22. data/vendor/grape/lib/grape/middleware/error.rb +87 -0
  23. data/vendor/grape/lib/grape/middleware/filter.rb +17 -0
  24. data/vendor/grape/lib/grape/middleware/formatter.rb +81 -0
  25. data/vendor/grape/lib/grape/middleware/prefixer.rb +21 -0
  26. data/vendor/grape/lib/grape/middleware/versioner/header.rb +59 -0
  27. data/vendor/grape/lib/grape/middleware/versioner/param.rb +44 -0
  28. data/vendor/grape/lib/grape/middleware/versioner/path.rb +42 -0
  29. data/vendor/grape/lib/grape/middleware/versioner.rb +29 -0
  30. data/vendor/grape/lib/grape/route.rb +23 -0
  31. data/vendor/grape/lib/grape/util/deep_merge.rb +23 -0
  32. data/vendor/grape/lib/grape/util/hash_stack.rb +100 -0
  33. data/vendor/grape/lib/grape/validations/coerce.rb +61 -0
  34. data/vendor/grape/lib/grape/validations/presence.rb +11 -0
  35. data/vendor/grape/lib/grape/validations/regexp.rb +13 -0
  36. data/vendor/grape/lib/grape/validations.rb +192 -0
  37. data/vendor/grape/lib/grape/version.rb +3 -0
  38. data/vendor/grape/lib/grape.rb +44 -0
  39. metadata +216 -0
@@ -0,0 +1,378 @@
1
+ require 'hashie'
2
+
3
+ module Grape
4
+ # An Entity is a lightweight structure that allows you to easily
5
+ # represent data from your application in a consistent and abstracted
6
+ # way in your API. Entities can also provide documentation for the
7
+ # fields exposed.
8
+ #
9
+ # @example Entity Definition
10
+ #
11
+ # module API
12
+ # module Entities
13
+ # class User < Grape::Entity
14
+ # expose :first_name, :last_name, :screen_name, :location
15
+ # expose :field, :documentation => {:type => "string", :desc => "describe the field"}
16
+ # expose :latest_status, :using => API::Status, :as => :status, :unless => {:collection => true}
17
+ # expose :email, :if => {:type => :full}
18
+ # expose :new_attribute, :if => {:version => 'v2'}
19
+ # expose(:name){|model,options| [model.first_name, model.last_name].join(' ')}
20
+ # end
21
+ # end
22
+ # end
23
+ #
24
+ # Entities are not independent structures, rather, they create
25
+ # **representations** of other Ruby objects using a number of methods
26
+ # that are convenient for use in an API. Once you've defined an Entity,
27
+ # you can use it in your API like this:
28
+ #
29
+ # @example Usage in the API Layer
30
+ #
31
+ # module API
32
+ # class Users < Grape::API
33
+ # version 'v2'
34
+ #
35
+ # desc 'User index', { :object_fields => API::Entities::User.documentation }
36
+ # get '/users' do
37
+ # @users = User.all
38
+ # type = current_user.admin? ? :full : :default
39
+ # present @users, :with => API::Entities::User, :type => type
40
+ # end
41
+ # end
42
+ # end
43
+ class Entity
44
+ attr_reader :object, :options
45
+
46
+ # The Entity DSL allows you to mix entity functionality into
47
+ # your existing classes.
48
+ module DSL
49
+ def self.included(base)
50
+ base.extend ClassMethods
51
+ ancestor_entity_class = base.ancestors.detect{|a| a.entity_class if a.respond_to?(:entity_class)}
52
+ base.const_set(:Entity, Class.new(ancestor_entity_class || Grape::Entity)) unless const_defined?(:Entity)
53
+ end
54
+
55
+ module ClassMethods
56
+ # Returns the automatically-created entity class for this
57
+ # Class.
58
+ def entity_class(search_ancestors=true)
59
+ klass = const_get(:Entity) if const_defined?(:Entity)
60
+ klass ||= ancestors.detect{|a| a.entity_class(false) if a.respond_to?(:entity_class) } if search_ancestors
61
+ klass
62
+ end
63
+
64
+ # Call this to make exposures to the entity for this Class.
65
+ # Can be called with symbols for the attributes to expose,
66
+ # a block that yields the full Entity DSL (See Grape::Entity),
67
+ # or both.
68
+ #
69
+ # @example Symbols only.
70
+ #
71
+ # class User
72
+ # include Grape::Entity::DSL
73
+ #
74
+ # entity :name, :email
75
+ # end
76
+ #
77
+ # @example Mixed.
78
+ #
79
+ # class User
80
+ # include Grape::Entity::DSL
81
+ #
82
+ # entity :name, :email do
83
+ # expose :latest_status, using: Status::Entity, if: :include_status
84
+ # expose :new_attribute, :if => {:version => 'v2'}
85
+ # end
86
+ # end
87
+ def entity(*exposures, &block)
88
+ entity_class.expose *exposures if exposures.any?
89
+ entity_class.class_eval(&block) if block_given?
90
+ entity_class
91
+ end
92
+ end
93
+
94
+ # Instantiates an entity version of this object.
95
+ def entity
96
+ self.class.entity_class.new(self)
97
+ end
98
+ end
99
+
100
+ # This method is the primary means by which you will declare what attributes
101
+ # should be exposed by the entity.
102
+ #
103
+ # @option options :as Declare an alias for the representation of this attribute.
104
+ # @option options :if When passed a Hash, the attribute will only be exposed if the
105
+ # runtime options match all the conditions passed in. When passed a lambda, the
106
+ # lambda will execute with two arguments: the object being represented and the
107
+ # options passed into the representation call. Return true if you want the attribute
108
+ # to be exposed.
109
+ # @option options :unless When passed a Hash, the attribute will be exposed if the
110
+ # runtime options fail to match any of the conditions passed in. If passed a lambda,
111
+ # it will yield the object being represented and the options passed to the
112
+ # representation call. Return true to prevent exposure, false to allow it.
113
+ # @option options :using This option allows you to map an attribute to another Grape
114
+ # Entity. Pass it a Grape::Entity class and the attribute in question will
115
+ # automatically be transformed into a representation that will receive the same
116
+ # options as the parent entity when called. Note that arrays are fine here and
117
+ # will automatically be detected and handled appropriately.
118
+ # @option options :proc If you pass a Proc into this option, it will
119
+ # be used directly to determine the value for that attribute. It
120
+ # will be called with the represented object as well as the
121
+ # runtime options that were passed in. You can also just supply a
122
+ # block to the expose call to achieve the same effect.
123
+ # @option options :documentation Define documenation for an exposed
124
+ # field, typically the value is a hash with two fields, type and desc.
125
+ def self.expose(*args, &block)
126
+ options = args.last.is_a?(Hash) ? args.pop : {}
127
+
128
+ if args.size > 1
129
+ raise ArgumentError, "You may not use the :as option on multi-attribute exposures." if options[:as]
130
+ raise ArgumentError, "You may not use block-setting on multi-attribute exposures." if block_given?
131
+ end
132
+
133
+ raise ArgumentError, "You may not use block-setting when also using format_with" if block_given? && options[:format_with].respond_to?(:call)
134
+
135
+ options[:proc] = block if block_given?
136
+
137
+ args.each do |attribute|
138
+ exposures[attribute.to_sym] = options
139
+ end
140
+ end
141
+
142
+ # Returns a hash of exposures that have been declared for this Entity or ancestors. The keys
143
+ # are symbolized references to methods on the containing object, the values are
144
+ # the options that were passed into expose.
145
+ def self.exposures
146
+ @exposures ||= {}
147
+
148
+ if superclass.respond_to? :exposures
149
+ @exposures = superclass.exposures.merge(@exposures)
150
+ end
151
+
152
+ @exposures
153
+ end
154
+
155
+ # Returns a hash, the keys are symbolized references to fields in the entity,
156
+ # the values are document keys in the entity's documentation key. When calling
157
+ # #docmentation, any exposure without a documentation key will be ignored.
158
+ def self.documentation
159
+ @documentation ||= exposures.inject({}) do |memo, value|
160
+ unless value[1][:documentation].nil? || value[1][:documentation].empty?
161
+ memo[value[0]] = value[1][:documentation]
162
+ end
163
+ memo
164
+ end
165
+
166
+ if superclass.respond_to? :documentation
167
+ @documentation = superclass.documentation.merge(@documentation)
168
+ end
169
+
170
+ @documentation
171
+ end
172
+
173
+ # This allows you to declare a Proc in which exposures can be formatted with.
174
+ # It take a block with an arity of 1 which is passed as the value of the exposed attribute.
175
+ #
176
+ # @param name [Symbol] the name of the formatter
177
+ # @param block [Proc] the block that will interpret the exposed attribute
178
+ #
179
+ #
180
+ #
181
+ # @example Formatter declaration
182
+ #
183
+ # module API
184
+ # module Entities
185
+ # class User < Grape::Entity
186
+ # format_with :timestamp do |date|
187
+ # date.strftime('%m/%d/%Y')
188
+ # end
189
+ #
190
+ # expose :birthday, :last_signed_in, :format_with => :timestamp
191
+ # end
192
+ # end
193
+ # end
194
+ #
195
+ # @example Formatters are available to all decendants
196
+ #
197
+ # Grape::Entity.format_with :timestamp do |date|
198
+ # date.strftime('%m/%d/%Y')
199
+ # end
200
+ #
201
+ def self.format_with(name, &block)
202
+ raise ArgumentError, "You must pass a block for formatters" unless block_given?
203
+ formatters[name.to_sym] = block
204
+ end
205
+
206
+ # Returns a hash of all formatters that are registered for this and it's ancestors.
207
+ def self.formatters
208
+ @formatters ||= {}
209
+
210
+ if superclass.respond_to? :formatters
211
+ @formatters = superclass.formatters.merge(@formatters)
212
+ end
213
+
214
+ @formatters
215
+ end
216
+
217
+ # This allows you to set a root element name for your representation.
218
+ #
219
+ # @param plural [String] the root key to use when representing
220
+ # a collection of objects. If missing or nil, no root key will be used
221
+ # when representing collections of objects.
222
+ # @param singular [String] the root key to use when representing
223
+ # a single object. If missing or nil, no root key will be used when
224
+ # representing an individual object.
225
+ #
226
+ # @example Entity Definition
227
+ #
228
+ # module API
229
+ # module Entities
230
+ # class User < Grape::Entity
231
+ # root 'users', 'user'
232
+ # expose :id
233
+ # end
234
+ # end
235
+ # end
236
+ #
237
+ # @example Usage in the API Layer
238
+ #
239
+ # module API
240
+ # class Users < Grape::API
241
+ # version 'v2'
242
+ #
243
+ # # this will render { "users": [ {"id":"1"}, {"id":"2"} ] }
244
+ # get '/users' do
245
+ # @users = User.all
246
+ # present @users, :with => API::Entities::User
247
+ # end
248
+ #
249
+ # # this will render { "user": {"id":"1"} }
250
+ # get '/users/:id' do
251
+ # @user = User.find(params[:id])
252
+ # present @user, :with => API::Entities::User
253
+ # end
254
+ # end
255
+ # end
256
+ def self.root(plural, singular=nil)
257
+ @collection_root = plural
258
+ @root = singular
259
+ end
260
+
261
+ # This convenience method allows you to instantiate one or more entities by
262
+ # passing either a singular or collection of objects. Each object will be
263
+ # initialized with the same options. If an array of objects is passed in,
264
+ # an array of entities will be returned. If a single object is passed in,
265
+ # a single entity will be returned.
266
+ #
267
+ # @param objects [Object or Array] One or more objects to be represented.
268
+ # @param options [Hash] Options that will be passed through to each entity
269
+ # representation.
270
+ #
271
+ # @option options :root [String] override the default root name set for the
272
+ #  entity. Pass nil or false to represent the object or objects with no
273
+ # root name even if one is defined for the entity.
274
+ def self.represent(objects, options = {})
275
+ inner = if objects.respond_to?(:to_ary)
276
+ objects.to_ary().map{|o| self.new(o, {:collection => true}.merge(options))}
277
+ else
278
+ self.new(objects, options)
279
+ end
280
+
281
+ root_element = if options.has_key?(:root)
282
+ options[:root]
283
+ else
284
+ objects.respond_to?(:to_ary) ? @collection_root : @root
285
+ end
286
+ root_element ? { root_element => inner } : inner
287
+ end
288
+
289
+ def initialize(object, options = {})
290
+ @object, @options = object, options
291
+ end
292
+
293
+ def exposures
294
+ self.class.exposures
295
+ end
296
+
297
+ def documentation
298
+ self.class.documentation
299
+ end
300
+
301
+ def formatters
302
+ self.class.formatters
303
+ end
304
+
305
+ # The serializable hash is the Entity's primary output. It is the transformed
306
+ # hash for the given data model and is used as the basis for serialization to
307
+ # JSON and other formats.
308
+ #
309
+ # @param options [Hash] Any options you pass in here will be known to the entity
310
+ # representation, this is where you can trigger things from conditional options
311
+ # etc.
312
+ def serializable_hash(runtime_options = {})
313
+ return nil if object.nil?
314
+ opts = options.merge(runtime_options || {})
315
+ exposures.inject({}) do |output, (attribute, exposure_options)|
316
+ if (exposure_options.has_key?(:proc) || object.respond_to?(attribute)) && conditions_met?(exposure_options, opts)
317
+ partial_output = value_for(attribute, opts)
318
+ output[key_for(attribute)] =
319
+ if partial_output.respond_to? :serializable_hash
320
+ partial_output.serializable_hash(runtime_options)
321
+ elsif partial_output.kind_of?(Array) && !partial_output.map {|o| o.respond_to? :serializable_hash}.include?(false)
322
+ partial_output.map {|o| o.serializable_hash}
323
+ else
324
+ partial_output
325
+ end
326
+ end
327
+ output
328
+ end
329
+ end
330
+
331
+ alias :as_json :serializable_hash
332
+
333
+ protected
334
+
335
+ def key_for(attribute)
336
+ exposures[attribute.to_sym][:as] || attribute.to_sym
337
+ end
338
+
339
+ def value_for(attribute, options = {})
340
+ exposure_options = exposures[attribute.to_sym]
341
+
342
+ if exposure_options[:proc]
343
+ exposure_options[:proc].call(object, options)
344
+ elsif exposure_options[:using]
345
+ exposure_options[:using].represent(object.send(attribute), :root => nil)
346
+ elsif exposure_options[:format_with]
347
+ format_with = exposure_options[:format_with]
348
+
349
+ if format_with.is_a?(Symbol) && formatters[format_with]
350
+ formatters[format_with].call(object.send(attribute))
351
+ elsif format_with.is_a?(Symbol)
352
+ self.send(format_with, object.send(attribute))
353
+ elsif format_with.respond_to? :call
354
+ format_with.call(object.send(attribute))
355
+ end
356
+ else
357
+ object.send(attribute)
358
+ end
359
+ end
360
+
361
+ def conditions_met?(exposure_options, options)
362
+ if_condition = exposure_options[:if]
363
+ unless_condition = exposure_options[:unless]
364
+
365
+ case if_condition
366
+ when Hash; if_condition.each_pair{|k,v| return false if options[k.to_sym] != v }
367
+ when Proc; return false unless if_condition.call(object, options)
368
+ end
369
+
370
+ case unless_condition
371
+ when Hash; unless_condition.each_pair{|k,v| return false if options[k.to_sym] == v}
372
+ when Proc; return false if unless_condition.call(object, options)
373
+ end
374
+
375
+ true
376
+ end
377
+ end
378
+ end
@@ -0,0 +1,17 @@
1
+ module Grape
2
+ module Exceptions
3
+ class Base < StandardError
4
+ attr_reader :status, :message, :headers
5
+
6
+ def initialize(args = {})
7
+ @status = args[:status] || nil
8
+ @message = args[:message] || nil
9
+ @headers = args[:headers] || nil
10
+ end
11
+
12
+ def [](index)
13
+ self.send(index)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ require 'grape/exceptions/base'
2
+
3
+ class ValidationError < Grape::Exceptions::Base
4
+ attr_accessor :param
5
+
6
+ def initialize(args = {})
7
+ @param = args[:param].to_s if args.has_key? :param
8
+ super
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ require 'rack/auth/basic'
2
+
3
+ module Grape
4
+ module Middleware
5
+ module Auth
6
+ class Basic < Grape::Middleware::Base
7
+ attr_reader :authenticator
8
+
9
+ def initialize(app, options = {}, &authenticator)
10
+ super(app, options)
11
+ @authenticator = authenticator
12
+ end
13
+
14
+ def basic_request
15
+ Rack::Auth::Basic::Request.new(env)
16
+ end
17
+
18
+ def credentials
19
+ basic_request.provided?? basic_request.credentials : [nil, nil]
20
+ end
21
+
22
+ def before
23
+ unless authenticator.call(*credentials)
24
+ throw :error, :status => 401, :message => "API Authorization Failed."
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ require 'rack/auth/digest/md5'
2
+
3
+ module Grape
4
+ module Middleware
5
+ module Auth
6
+ class Digest < Grape::Middleware::Base
7
+ attr_reader :authenticator
8
+
9
+ def initialize(app, options = {}, &authenticator)
10
+ super(app, options)
11
+ @authenticator = authenticator
12
+ end
13
+
14
+ def digest_request
15
+ Rack::Auth::Digest::Request.new(env)
16
+ end
17
+
18
+ def credentials
19
+ digest_request.provided?? digest_request.credentials : [nil, nil]
20
+ end
21
+
22
+ def before
23
+ unless authenticator.call(*credentials)
24
+ throw :error, :status => 401, :message => "API Authorization Failed."
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,72 @@
1
+ module Grape::Middleware::Auth
2
+ # OAuth 2.0 authorization for Grape APIs.
3
+ class OAuth2 < Grape::Middleware::Base
4
+ def default_options
5
+ {
6
+ :token_class => 'AccessToken',
7
+ :realm => 'OAuth API',
8
+ :parameter => %w(bearer_token oauth_token),
9
+ :accepted_headers => %w(HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION),
10
+ :header => [/Bearer (.*)/i, /OAuth (.*)/i]
11
+ }
12
+ end
13
+
14
+ def before
15
+ verify_token(token_parameter || token_header)
16
+ end
17
+
18
+ def token_parameter
19
+ Array(options[:parameter]).each do |p|
20
+ return request[p] if request[p]
21
+ end
22
+ nil
23
+ end
24
+
25
+ def token_header
26
+ return false unless authorization_header
27
+ Array(options[:header]).each do |regexp|
28
+ if authorization_header =~ regexp
29
+ return $1
30
+ end
31
+ end
32
+ nil
33
+ end
34
+
35
+ def authorization_header
36
+ options[:accepted_headers].each do |head|
37
+ return env[head] if env[head]
38
+ end
39
+ nil
40
+ end
41
+
42
+ def token_class
43
+ @klass ||= eval(options[:token_class])
44
+ end
45
+
46
+ def verify_token(token)
47
+ if token = token_class.verify(token)
48
+ if token.respond_to?(:expired?) && token.expired?
49
+ error_out(401, 'expired_token')
50
+ else
51
+ if !token.respond_to?(:permission_for?) || token.permission_for?(env)
52
+ env['api.token'] = token
53
+ else
54
+ error_out(403, 'insufficient_scope')
55
+ end
56
+ end
57
+ else
58
+ error_out(401, 'invalid_token')
59
+ end
60
+ end
61
+
62
+ def error_out(status, error)
63
+ throw :error,
64
+ :message => error,
65
+ :status => status,
66
+ :headers => {
67
+ 'WWW-Authenticate' => "OAuth realm='#{options[:realm]}', error='#{error}'"
68
+ }
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,154 @@
1
+ require 'multi_json'
2
+ require 'multi_xml'
3
+
4
+ module Grape
5
+ module Middleware
6
+ class Base
7
+ attr_reader :app, :env, :options
8
+
9
+ # @param [Rack Application] app The standard argument for a Rack middleware.
10
+ # @param [Hash] options A hash of options, simply stored for use by subclasses.
11
+ def initialize(app, options = {})
12
+ @app = app
13
+ @options = default_options.merge(options)
14
+ end
15
+
16
+ def default_options; {} end
17
+
18
+ def call(env)
19
+ dup.call!(env)
20
+ end
21
+
22
+ def call!(env)
23
+ @env = env
24
+ before
25
+ @app_response = @app.call(@env)
26
+ after || @app_response
27
+ end
28
+
29
+ # @abstract
30
+ # Called before the application is called in the middleware lifecycle.
31
+ def before; end
32
+ # @abstract
33
+ # Called after the application is called in the middleware lifecycle.
34
+ # @return [Response, nil] a Rack SPEC response or nil to call the application afterwards.
35
+ def after; end
36
+
37
+ def request
38
+ Rack::Request.new(self.env)
39
+ end
40
+
41
+ def response
42
+ Rack::Response.new(@app_response)
43
+ end
44
+
45
+
46
+ module Formats
47
+
48
+ CONTENT_TYPES = {
49
+ :xml => 'application/xml',
50
+ :json => 'application/json',
51
+ :atom => 'application/atom+xml',
52
+ :rss => 'application/rss+xml',
53
+ :txt => 'text/plain'
54
+ }
55
+ FORMATTERS = {
56
+ :json => :encode_json,
57
+ :txt => :encode_txt,
58
+ :xml => :encode_xml
59
+ }
60
+ PARSERS = {
61
+ :json => :decode_json,
62
+ :xml => :decode_xml
63
+ }
64
+
65
+ def formatters
66
+ FORMATTERS.merge(options[:formatters] || {})
67
+ end
68
+
69
+ def parsers
70
+ PARSERS.merge(options[:parsers] || {})
71
+ end
72
+
73
+ def content_types
74
+ CONTENT_TYPES.merge(options[:content_types] || {})
75
+ end
76
+
77
+ def content_type
78
+ content_types[options[:format]] || 'text/html'
79
+ end
80
+
81
+ def mime_types
82
+ content_types.invert
83
+ end
84
+
85
+ def formatter_for(api_format)
86
+ spec = formatters[api_format]
87
+ case spec
88
+ when nil
89
+ lambda { |obj| obj }
90
+ when Symbol
91
+ method(spec)
92
+ else
93
+ spec
94
+ end
95
+ end
96
+
97
+ def parser_for(api_format)
98
+ spec = parsers[api_format]
99
+ case spec
100
+ when nil
101
+ nil
102
+ when Symbol
103
+ method(spec)
104
+ else
105
+ spec
106
+ end
107
+ end
108
+
109
+ def decode_json(object)
110
+ MultiJson.load(object)
111
+ end
112
+
113
+ def serializable?(object)
114
+ object.respond_to?(:serializable_hash) ||
115
+ object.kind_of?(Array) && !object.map {|o| o.respond_to? :serializable_hash }.include?(false) ||
116
+ object.kind_of?(Hash)
117
+ end
118
+
119
+ def serialize(object)
120
+ if object.respond_to? :serializable_hash
121
+ object.serializable_hash
122
+ elsif object.kind_of?(Array) && !object.map {|o| o.respond_to? :serializable_hash }.include?(false)
123
+ object.map {|o| o.serializable_hash }
124
+ elsif object.kind_of?(Hash)
125
+ object.inject({}) { |h,(k,v)| h[k] = serialize(v); h }
126
+ else
127
+ object
128
+ end
129
+ end
130
+
131
+ def encode_json(object)
132
+ return object if object.is_a?(String)
133
+ return MultiJson.dump(serialize(object)) if serializable?(object)
134
+ return object.to_json if object.respond_to?(:to_json)
135
+
136
+ MultiJson.dump(object)
137
+ end
138
+
139
+ def encode_txt(object)
140
+ object.respond_to?(:to_txt) ? object.to_txt : object.to_s
141
+ end
142
+
143
+ def decode_xml(object)
144
+ MultiXml.parse(object)
145
+ end
146
+
147
+ def encode_xml(object)
148
+ object.respond_to?(:to_xml) ? object.to_xml : object.to_s
149
+ end
150
+ end
151
+
152
+ end
153
+ end
154
+ end