active_model_serializers 0.9.0.alpha1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +132 -15
  3. data/lib/action_controller/serialization.rb +5 -0
  4. data/lib/action_controller/serialization_test_case.rb +79 -0
  5. data/lib/active_model/array_serializer.rb +15 -8
  6. data/lib/active_model/default_serializer.rb +12 -2
  7. data/lib/active_model/serializable.rb +21 -6
  8. data/lib/active_model/serializer.rb +84 -9
  9. data/lib/active_model/serializer/associations.rb +38 -11
  10. data/lib/active_model/serializer/generators/serializer/serializer_generator.rb +1 -1
  11. data/lib/active_model/serializer/version.rb +1 -1
  12. data/lib/active_model_serializers.rb +6 -2
  13. data/test/fixtures/poro.rb +11 -0
  14. data/test/integration/action_controller/serialization_test.rb +53 -0
  15. data/test/integration/action_controller/serialization_test_case_test.rb +61 -0
  16. data/test/integration/active_record/active_record_test.rb +1 -1
  17. data/test/integration/generators/scaffold_controller_generator_test.rb +0 -3
  18. data/test/test_helper.rb +4 -1
  19. data/test/unit/active_model/array_serializer/except_test.rb +18 -0
  20. data/test/unit/active_model/array_serializer/key_format_test.rb +18 -0
  21. data/test/unit/active_model/array_serializer/meta_test.rb +1 -1
  22. data/test/unit/active_model/array_serializer/only_test.rb +18 -0
  23. data/test/unit/active_model/array_serializer/root_test.rb +2 -2
  24. data/test/unit/active_model/array_serializer/scope_test.rb +1 -1
  25. data/test/unit/active_model/array_serializer/serialization_test.rb +120 -4
  26. data/test/unit/active_model/default_serializer_test.rb +1 -1
  27. data/test/unit/active_model/serializer/associations/build_serializer_test.rb +1 -1
  28. data/test/unit/active_model/serializer/associations_test.rb +1 -1
  29. data/test/unit/active_model/serializer/attributes_test.rb +1 -1
  30. data/test/unit/active_model/serializer/config_test.rb +7 -5
  31. data/test/unit/active_model/serializer/filter_test.rb +22 -2
  32. data/test/unit/active_model/serializer/has_many_test.rb +61 -4
  33. data/test/unit/active_model/serializer/has_one_test.rb +59 -3
  34. data/test/unit/active_model/serializer/key_format_test.rb +25 -0
  35. data/test/unit/active_model/serializer/meta_test.rb +1 -1
  36. data/test/unit/active_model/serializer/options_test.rb +15 -0
  37. data/test/unit/active_model/serializer/root_test.rb +2 -2
  38. data/test/unit/active_model/serializer/scope_test.rb +3 -3
  39. metadata +36 -27
  40. data/test/coverage_setup.rb +0 -15
  41. data/test/tmp/app/serializers/account_serializer.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aeb402f8407aece224529052410e2ad33485d7eb
4
- data.tar.gz: db4621bc2e62921b75a8e02e77b30e4782951b1a
3
+ metadata.gz: 65701e1dd4ae30a60340c60fa9361c540148bb64
4
+ data.tar.gz: f7b2b52348b116ac1eea607a51e9ddb8d9c15fce
5
5
  SHA512:
6
- metadata.gz: 5496e9420a975333a78f239eb2bbeb680f4240ec22e1bdbbe989fceb480325e14fbd96ab706510f0b0297b8317d85298451b034eabb5f1e67627e03bd0aa5c57
7
- data.tar.gz: 2320ddcf2bf5c13028669dabc470331008de7e3fe79bf3f4ffb4d70df701731061541ccfc4ed57160dc520b8637e02e75b8510bfb6c72470be4ffd2603618be1
6
+ metadata.gz: 658d1fadb66a26b4b93a72cdb87498bb89ee83565de5d846ca3e2cdaa913303256e9267627d8e55593aa1696963f255e2fe44918e858ce2e66251710d3694241
7
+ data.tar.gz: 2b100c2bcb26d9696001eefae6fd3bddc68d779578c2c313f64b06f47cf51024f6e525a7afd70d8afd28d973bb7fd2d9bb7923391d2271233317d2a6669948b0
data/README.md CHANGED
@@ -1,15 +1,8 @@
1
- [![Build Status](https://api.travis-ci.org/rails-api/active_model_serializers.png)](https://travis-ci.org/rails-api/active_model_serializers)
1
+ [![Build Status](https://api.travis-ci.org/rails-api/active_model_serializers.png?branch=0-9-stable)](https://travis-ci.org/rails-api/active_model_serializers)
2
2
  [![Code Climate](https://codeclimate.com/github/rails-api/active_model_serializers.png)](https://codeclimate.com/github/rails-api/active_model_serializers)
3
- [![Coverage Status](https://coveralls.io/repos/rails-api/active_model_serializers/badge.png?branch=master)](https://coveralls.io/r/rails-api/active_model_serializers)
4
3
 
5
4
  # ActiveModel::Serializers
6
5
 
7
- ## Master - 0.9.0
8
-
9
- **master is under development, there are some incompatible changes with the current stable release.**
10
-
11
- If you want to read the stable documentation visit [0.8 README](https://github.com/rails-api/active_model_serializers/blob/0-8-stable/README.md)
12
-
13
6
  ## Purpose
14
7
 
15
8
  `ActiveModel::Serializers` encapsulates the JSON serialization of objects.
@@ -67,13 +60,11 @@ $ rails g serializer post
67
60
 
68
61
  ### Support for POROs
69
62
 
70
- Currently `ActiveModel::Serializers` expects objects to implement
71
- read\_attribute\_for\_serialization. That's all you need to do to have
72
- your POROs supported.
63
+ The PORO should include ActiveModel::SerializerSupport. That's all you need to
64
+ do to have your POROs supported.
73
65
 
74
- # ActiveModel::Serializer
75
-
76
- All new serializers descend from ActiveModel::Serializer
66
+ For Rails versions before Rails 4 ActiveModel::Serializers expects objects to
67
+ implement `read_attribute_for_serialization`.
77
68
 
78
69
  # render :json
79
70
 
@@ -158,6 +149,29 @@ To specify a custom serializer for the items within an array:
158
149
  render json: @posts, each_serializer: FancyPostSerializer
159
150
  ```
160
151
 
152
+ ## Render independently
153
+
154
+ By default the setting of serializer is in controller as described above which is the
155
+ recommended way. However, there may be cases you need to render the json object elsewhere
156
+ say in a helper or a view when controller is only for main object.
157
+
158
+ Then you can render the serialized JSON independently.
159
+
160
+ ```ruby
161
+ def current_user_as_json_helper
162
+ CurrentUserSerializer.new(current_user).to_json
163
+ end
164
+ ```
165
+
166
+ You can also render an array of objects using ArraySerializer.
167
+
168
+ ```ruby
169
+ def users_array_as_json_helper(users)
170
+ ActiveModel::ArraySerializer.new(users, each_serializer: UserSerializer).to_json
171
+ end
172
+ ```
173
+
174
+
161
175
  ## Disabling the root element
162
176
 
163
177
  You have 4 options to disable the root element, each with a slightly different scope:
@@ -205,6 +219,23 @@ def default_serializer_options
205
219
  end
206
220
  ```
207
221
 
222
+ ## Changing the Key Format
223
+
224
+ You can specify that serializers use the lower-camel key format at the config, class or instance level.
225
+
226
+ ```ruby
227
+
228
+ ActiveModel::Serializer.setup do |config|
229
+ config.key_format = :lower_camel
230
+ end
231
+
232
+ class BlogLowerCamelSerializer < ActiveModel::Serializer
233
+ format_keys :lower_camel
234
+ end
235
+
236
+ BlogSerializer.new(object, key_format: :lower_camel)
237
+ ```
238
+
208
239
  ## Getting the old version
209
240
 
210
241
  If you find that your project is already relying on the old rails to_json
@@ -263,7 +294,7 @@ authorization context to your serializer. By default, the context
263
294
  is the current user of your application, but this
264
295
  [can be customized](#customizing-scope).
265
296
 
266
- Serializers provides a method named `filter`, which should return an array
297
+ Serializers provide a method named `filter`, which should return an array
267
298
  used to determine what attributes and associations should be included in the output.
268
299
  This is typically used to customize output based on `current_user`. For example:
269
300
 
@@ -485,6 +516,33 @@ Now, any associations will be supplied as an Array of IDs:
485
516
  }
486
517
  ```
487
518
 
519
+ You may also choose to embed the IDs by the association's name underneath an
520
+ `embed_key` for the resource. For example, say we want to change `comment_ids`
521
+ to `comments` underneath a `links` key:
522
+
523
+ ```ruby
524
+ class PostSerializer < ActiveModel::Serializer
525
+ attributes :id, :title, :body
526
+
527
+ has_many :comments, embed: ids, embed_namespace: :links
528
+ end
529
+ ```
530
+
531
+ The JSON will look like this:
532
+
533
+ ```json
534
+ {
535
+ "post": {
536
+ "id": 1,
537
+ "title": "New post",
538
+ "body": "A body!",
539
+ "links": {
540
+ "comments": [ 1, 2, 3 ]
541
+ }
542
+ }
543
+ }
544
+ ```
545
+
488
546
  Alternatively, you can choose to embed only the ids or the associated objects per association:
489
547
 
490
548
  ```ruby
@@ -552,6 +610,42 @@ this:
552
610
  }
553
611
  ```
554
612
 
613
+ If you would like to namespace association JSON underneath a certain key in
614
+ the root document (say, `linked`), you can specify an `embed_in_root_key`:
615
+
616
+ ```ruby
617
+ class PostSerializer < ActiveModel::Serializer
618
+ embed: ids, include: true, embed_in_root_key: :linked
619
+
620
+ attributes: :id, :title, :body
621
+ has_many :comments, :tags
622
+ end
623
+ ```
624
+
625
+ The above would yield the following JSON document:
626
+
627
+ ```json
628
+ {
629
+ "post": {
630
+ "id": 1,
631
+ "title": "New post",
632
+ "body": "A body!",
633
+ "comment_ids": [ 1, 2 ]
634
+ },
635
+ "linked": {
636
+ "comments": [
637
+ { "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] },
638
+ { "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] },
639
+ ],
640
+ "tags": [
641
+ { "id": 1, "name": "short" },
642
+ { "id": 2, "name": "whiny" },
643
+ { "id": 3, "name": "happy" }
644
+ ]
645
+ }
646
+ }
647
+ ```
648
+
555
649
  When side-loading data, your serializer cannot have the `{ root: false }` option,
556
650
  as this would lead to invalid JSON. If you do not have a root key, the `include`
557
651
  instruction will be ignored
@@ -724,6 +818,29 @@ end
724
818
 
725
819
  The caching interface uses `Rails.cache` under the hood.
726
820
 
821
+ # ApplicationSerializer
822
+
823
+ By default, new serializers descend from ActiveModel::Serializer. However, if you wish to share behaviour across your serializers you can create an ApplicationSerializer at ```app/serializers/application_serializer.rb```:
824
+
825
+ ```ruby
826
+ class ApplicationSerializer < ActiveModel::Serializer
827
+ end
828
+ ```
829
+
830
+ Any newly generated serializers will automatically descend from ApplicationSerializer.
831
+
832
+ ```
833
+ $ rails g serializer post
834
+ ```
835
+
836
+ now generates:
837
+
838
+ ```ruby
839
+ class PostSerializer < ApplicationSerializer
840
+ attributes :id
841
+ end
842
+ ````
843
+
727
844
  # Design and Implementation Guidelines
728
845
 
729
846
  ## Keep it Simple
@@ -29,6 +29,11 @@ module ActionController
29
29
 
30
30
  include ActionController::Renderers
31
31
 
32
+ class << self
33
+ attr_accessor :enabled
34
+ end
35
+ self.enabled = true
36
+
32
37
  included do
33
38
  class_attribute :_serialization_scope
34
39
  self._serialization_scope = :current_user
@@ -0,0 +1,79 @@
1
+ module ActionController
2
+ module SerializationAssertions
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ setup :setup_subscriptions
7
+ teardown :teardown_subscriptions
8
+ end
9
+
10
+ def setup_subscriptions
11
+ @serializers = Hash.new(0)
12
+
13
+ ActiveSupport::Notifications.subscribe("!serialize.active_model_serializers") do |name, start, finish, id, payload|
14
+ serializer = payload[:serializer]
15
+ @serializers[serializer] += 1
16
+ end
17
+ end
18
+
19
+ def teardown_subscriptions
20
+ ActiveSupport::Notifications.unsubscribe("!serialize.active_model_serializers")
21
+ end
22
+
23
+ def process(*args)
24
+ @serializers = Hash.new(0)
25
+ super
26
+ end
27
+
28
+ # Asserts that the request was rendered with the appropriate serializers.
29
+ #
30
+ # # assert that the "PostSerializer" serializer was rendered
31
+ # assert_serializer "PostSerializer"
32
+ #
33
+ # # assert that the instance of PostSerializer was rendered
34
+ # assert_serializer PostSerializer
35
+ #
36
+ # # assert that the "PostSerializer" serializer was rendered
37
+ # assert_serializer :post_serializer
38
+ #
39
+ # # assert that the rendered serializer starts with "Post"
40
+ # assert_serializer %r{\APost.+\Z}
41
+ #
42
+ # # assert that no serializer was rendered
43
+ # assert_serializer nil
44
+ #
45
+ #
46
+ def assert_serializer(options = {}, message = nil)
47
+ # Force body to be read in case the template is being streamed.
48
+ response.body
49
+
50
+ rendered = @serializers
51
+ msg = message || "expecting <#{options.inspect}> but rendering with <#{rendered.keys}>"
52
+
53
+ matches_serializer = case options
54
+ when lambda { |options| options.kind_of?(Class) && options < ActiveModel::Serializer }
55
+ rendered.any? do |serializer, count|
56
+ options.name == serializer
57
+ end
58
+ when Symbol
59
+ options = options.to_s.camelize
60
+ rendered.any? do |serializer, count|
61
+ serializer == options
62
+ end
63
+ when String
64
+ !options.empty? && rendered.any? do |serializer, count|
65
+ serializer == options
66
+ end
67
+ when Regexp
68
+ rendered.any? do |serializer, count|
69
+ serializer.match(options)
70
+ end
71
+ when NilClass
72
+ rendered.blank?
73
+ else
74
+ raise ArgumentError, "assert_serializer only accepts a String, Symbol, Regexp, ActiveModel::Serializer, or nil"
75
+ end
76
+ assert matches_serializer, msg
77
+ end
78
+ end
79
+ end
@@ -1,6 +1,5 @@
1
1
  require 'active_model/default_serializer'
2
2
  require 'active_model/serializable'
3
- require 'active_model/serializer'
4
3
 
5
4
  module ActiveModel
6
5
  class ArraySerializer
@@ -20,20 +19,21 @@ module ActiveModel
20
19
  @meta = options[@meta_key]
21
20
  @each_serializer = options[:each_serializer]
22
21
  @resource_name = options[:resource_name]
22
+ @only = options[:only] ? Array(options[:only]) : nil
23
+ @except = options[:except] ? Array(options[:except]) : nil
24
+ @key_format = options[:key_format] || options[:each_serializer].try(:key_format)
23
25
  end
24
- attr_accessor :object, :scope, :root, :meta_key, :meta
26
+ attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format
25
27
 
26
28
  def json_key
27
- if root.nil?
28
- @resource_name
29
- else
30
- root
31
- end
29
+ key = root.nil? ? @resource_name : root
30
+
31
+ key_format == :lower_camel && key.present? ? key.camelize(:lower) : key
32
32
  end
33
33
 
34
34
  def serializer_for(item)
35
35
  serializer_class = @each_serializer || Serializer.serializer_for(item) || DefaultSerializer
36
- serializer_class.new(item, scope: scope)
36
+ serializer_class.new(item, scope: scope, key_format: key_format, only: @only, except: @except)
37
37
  end
38
38
 
39
39
  def serializable_object
@@ -46,6 +46,8 @@ module ActiveModel
46
46
  def embedded_in_root_associations
47
47
  @object.each_with_object({}) do |item, hash|
48
48
  serializer_for(item).embedded_in_root_associations.each_pair do |type, objects|
49
+ next if !objects || objects.flatten.empty?
50
+
49
51
  if hash.has_key?(type)
50
52
  hash[type].concat(objects).uniq!
51
53
  else
@@ -54,5 +56,10 @@ module ActiveModel
54
56
  end
55
57
  end
56
58
  end
59
+
60
+ private
61
+ def instrumentation_keys
62
+ [:object, :scope, :root, :meta_key, :meta, :each_serializer, :resource_name, :key_format]
63
+ end
57
64
  end
58
65
  end
@@ -9,14 +9,24 @@ module ActiveModel
9
9
 
10
10
  attr_reader :object
11
11
 
12
- def initialize(object, options=nil)
12
+ def initialize(object, options={})
13
13
  @object = object
14
+ @wrap_in_array = options[:_wrap_in_array]
14
15
  end
15
16
 
16
17
  def as_json(options={})
17
- @object.as_json
18
+ instrument('!serialize') do
19
+ return [] if @object.nil? && @wrap_in_array
20
+ hash = @object.as_json
21
+ @wrap_in_array ? [hash] : hash
22
+ end
18
23
  end
19
24
  alias serializable_hash as_json
20
25
  alias serializable_object as_json
26
+
27
+ private
28
+ def instrumentation_keys
29
+ [:object, :wrap_in_array]
30
+ end
21
31
  end
22
32
  end
@@ -1,12 +1,14 @@
1
1
  module ActiveModel
2
2
  module Serializable
3
3
  def as_json(options={})
4
- if root = options.fetch(:root, json_key)
5
- hash = { root => serializable_object }
6
- hash.merge!(serializable_data)
7
- hash
8
- else
9
- serializable_object
4
+ instrument('!serialize') do
5
+ if root = options.fetch(:root, json_key)
6
+ hash = { root => serializable_object }
7
+ hash.merge!(serializable_data)
8
+ hash
9
+ else
10
+ serializable_object
11
+ end
10
12
  end
11
13
  end
12
14
 
@@ -21,5 +23,18 @@ module ActiveModel
21
23
  def embedded_in_root_associations
22
24
  {}
23
25
  end
26
+
27
+ private
28
+ def instrument(action, &block)
29
+ payload = instrumentation_keys.inject({ serializer: self.class.name }) do |payload, key|
30
+ payload[:payload] = self.instance_variable_get(:"@#{key}")
31
+ payload
32
+ end
33
+ ActiveSupport::Notifications.instrument("#{action}.active_model_serializers", payload, &block)
34
+ end
35
+
36
+ def instrumentation_keys
37
+ [:object, :scope, :root, :meta_key, :meta, :wrap_in_array, :only, :except, :key_format]
38
+ end
24
39
  end
25
40
  end
@@ -24,9 +24,21 @@ module ActiveModel
24
24
  end
25
25
  end
26
26
 
27
+ EMBED_IN_ROOT_OPTIONS = [
28
+ :include,
29
+ :embed_in_root,
30
+ :embed_in_root_key,
31
+ :embed_namespace
32
+ ].freeze
33
+
27
34
  def embed(type, options={})
28
35
  CONFIG.embed = type
29
- CONFIG.embed_in_root = true if options[:embed_in_root] || options[:include]
36
+ if EMBED_IN_ROOT_OPTIONS.any? { |opt| options[opt].present? }
37
+ CONFIG.embed_in_root = true
38
+ end
39
+ if options[:embed_in_root_key].present?
40
+ CONFIG.embed_in_root_key = options[:embed_in_root_key]
41
+ end
30
42
  ActiveSupport::Deprecation.warn <<-WARN
31
43
  ** Notice: embed is deprecated. **
32
44
  The use of .embed method on a Serializer will be soon removed, as this should have a global scope and not a class scope.
@@ -38,10 +50,19 @@ end
38
50
  WARN
39
51
  end
40
52
 
53
+ def format_keys(format)
54
+ @key_format = format
55
+ end
56
+ attr_reader :key_format
57
+
41
58
  if RUBY_VERSION >= '2.0'
42
59
  def serializer_for(resource)
43
60
  if resource.respond_to?(:to_ary)
44
- ArraySerializer
61
+ if Object.constants.include?(:ArraySerializer)
62
+ ::ArraySerializer
63
+ else
64
+ ArraySerializer
65
+ end
45
66
  else
46
67
  begin
47
68
  Object.const_get "#{resource.class.name}Serializer"
@@ -53,7 +74,11 @@ end
53
74
  else
54
75
  def serializer_for(resource)
55
76
  if resource.respond_to?(:to_ary)
56
- ArraySerializer
77
+ if Object.constants.include?(:ArraySerializer)
78
+ ::ArraySerializer
79
+ else
80
+ ArraySerializer
81
+ end
57
82
  else
58
83
  "#{resource.class.name}Serializer".safe_constantize
59
84
  end
@@ -108,15 +133,21 @@ end
108
133
  @meta_key = options[:meta_key] || :meta
109
134
  @meta = options[@meta_key]
110
135
  @wrap_in_array = options[:_wrap_in_array]
136
+ @only = options[:only] ? Array(options[:only]) : nil
137
+ @except = options[:except] ? Array(options[:except]) : nil
138
+ @key_format = options[:key_format]
139
+ @context = options[:context]
111
140
  end
112
- attr_accessor :object, :scope, :root, :meta_key, :meta
141
+ attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format, :context
113
142
 
114
143
  def json_key
115
- if root == true || root.nil?
144
+ key = if root == true || root.nil?
116
145
  self.class.root_name
117
146
  else
118
147
  root
119
148
  end
149
+
150
+ key_format == :lower_camel && key.present? ? key.camelize(:lower) : key
120
151
  end
121
152
 
122
153
  def attributes
@@ -131,8 +162,17 @@ end
131
162
  associations.each_with_object({}) do |(name, association), hash|
132
163
  if included_associations.include? name
133
164
  if association.embed_ids?
134
- hash[association.key] = serialize_ids association
165
+ ids = serialize_ids association
166
+ if association.embed_namespace?
167
+ hash = hash[association.embed_namespace] ||= {}
168
+ hash[association.key] = ids
169
+ else
170
+ hash[association.key] = ids
171
+ end
135
172
  elsif association.embed_objects?
173
+ if association.embed_namespace?
174
+ hash = hash[association.embed_namespace] ||= {}
175
+ end
136
176
  hash[association.embedded_key] = serialize association
137
177
  end
138
178
  end
@@ -140,7 +180,13 @@ end
140
180
  end
141
181
 
142
182
  def filter(keys)
143
- keys
183
+ if @only
184
+ keys & @only
185
+ elsif @except
186
+ keys - @except
187
+ else
188
+ keys
189
+ end
144
190
  end
145
191
 
146
192
  def embedded_in_root_associations
@@ -149,8 +195,11 @@ end
149
195
  associations.each_with_object({}) do |(name, association), hash|
150
196
  if included_associations.include? name
151
197
  if association.embed_in_root?
198
+ if association.embed_in_root_key?
199
+ hash = hash[association.embed_in_root_key] ||= {}
200
+ end
152
201
  association_serializer = build_serializer(association)
153
- hash.merge! association_serializer.embedded_in_root_associations
202
+ hash.merge!(association_serializer.embedded_in_root_associations) {|key, oldval, newval| [newval, oldval].flatten }
154
203
 
155
204
  serialized_data = association_serializer.serializable_object
156
205
  key = association.root_key
@@ -182,12 +231,38 @@ end
182
231
  end
183
232
  end
184
233
 
234
+ def key_format
235
+ @key_format || self.class.key_format || CONFIG.key_format
236
+ end
237
+
238
+ def format_key(key)
239
+ if key_format == :lower_camel
240
+ key.to_s.camelize(:lower)
241
+ else
242
+ key
243
+ end
244
+ end
245
+
246
+ def convert_keys(hash)
247
+ Hash[hash.map do |k,v|
248
+ key = if k.is_a?(Symbol)
249
+ format_key(k).to_sym
250
+ else
251
+ format_key(k)
252
+ end
253
+
254
+ [key ,v]
255
+ end]
256
+ end
257
+
185
258
  def serializable_object(options={})
186
- return nil if object.nil?
259
+ return @wrap_in_array ? [] : nil if @object.nil?
187
260
  hash = attributes
188
261
  hash.merge! associations
262
+ hash = convert_keys(hash) if key_format.present?
189
263
  @wrap_in_array ? [hash] : hash
190
264
  end
191
265
  alias_method :serializable_hash, :serializable_object
192
266
  end
267
+
193
268
  end