oj_serializers 1.0.2 → 2.0.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.
data/README.md CHANGED
@@ -6,29 +6,33 @@ Oj Serializers
6
6
  <a href="https://codeclimate.com/github/ElMassimo/oj_serializers"><img alt="Maintainability" src="https://codeclimate.com/github/ElMassimo/oj_serializers/badges/gpa.svg"/></a>
7
7
  <a href="https://codeclimate.com/github/ElMassimo/oj_serializers"><img alt="Test Coverage" src="https://codeclimate.com/github/ElMassimo/oj_serializers/badges/coverage.svg"/></a>
8
8
  <a href="https://rubygems.org/gems/oj_serializers"><img alt="Gem Version" src="https://img.shields.io/gem/v/oj_serializers.svg?colorB=e9573f"/></a>
9
- <a href="https://github.com/ElMassimo/oj_serializers/blob/master/LICENSE.txt"><img alt="License" src="https://img.shields.io/badge/license-MIT-428F7E.svg"/></a>
9
+ <a href="https://github.com/ElMassimo/oj_serializers/blob/main/LICENSE.txt"><img alt="License" src="https://img.shields.io/badge/license-MIT-428F7E.svg"/></a>
10
10
  </p>
11
11
  </h1>
12
12
 
13
- JSON serializers for Ruby, built on top of the powerful [`oj`][oj] library.
13
+ Faster JSON serializers for Ruby, built on top of the powerful [`oj`][oj] library.
14
14
 
15
15
  [oj]: https://github.com/ohler55/oj
16
16
  [mongoid]: https://github.com/mongodb/mongoid
17
17
  [ams]: https://github.com/rails-api/active_model_serializers
18
18
  [jsonapi]: https://github.com/jsonapi-serializer/jsonapi-serializer
19
19
  [panko]: https://github.com/panko-serializer/panko_serializer
20
+ [blueprinter]: https://github.com/procore/blueprinter
20
21
  [benchmarks]: https://github.com/ElMassimo/oj_serializers/tree/master/benchmarks
21
- [raw_benchmarks]: https://github.com/ElMassimo/oj_serializers/blob/master/benchmarks/document_benchmark.rb
22
- [sugar]: https://github.com/ElMassimo/oj_serializers/blob/master/lib/oj_serializers/sugar.rb#L14
23
- [migration guide]: https://github.com/ElMassimo/oj_serializers/blob/master/MIGRATION_GUIDE.md
22
+ [raw_benchmarks]: https://github.com/ElMassimo/oj_serializers/blob/main/benchmarks/document_benchmark.rb
23
+ [sugar]: https://github.com/ElMassimo/oj_serializers/blob/main/lib/oj_serializers/sugar.rb#L14
24
+ [migration guide]: https://github.com/ElMassimo/oj_serializers/blob/main/MIGRATION_GUIDE.md
24
25
  [design]: https://github.com/ElMassimo/oj_serializers#design-
25
26
  [raw_json]: https://github.com/ohler55/oj/issues/542
26
27
  [trailing_commas]: https://maximomussini.com/posts/trailing-commas/
28
+ [render dsl]: https://github.com/ElMassimo/oj_serializers#render-dsl-
29
+ [sorbet]: https://sorbet.org/
30
+ [Discussion]: https://github.com/ElMassimo/oj_serializers/discussions
27
31
 
28
32
  ## Why? 🤔
29
33
 
30
- [`ActiveModel::Serializer`][ams] has a nice DSL, but it allocates many objects leading
31
- to memory bloat, time spent on GC, and lower performance.
34
+ [`ActiveModel::Serializer`][ams] has a nice DSL, but it allocates many objects
35
+ leading to memory bloat, time spent on GC, and lower performance.
32
36
 
33
37
  `Oj::Serializer` provides a similar API, with [better performance][benchmarks].
34
38
 
@@ -36,12 +40,11 @@ Learn more about [how this library achieves its performance][design].
36
40
 
37
41
  ## Features ⚡️
38
42
 
39
- - Declaration syntax similar to Active Model Serializers
40
- - Reduced memory allocation and [improved performance][benchmarks]
43
+ - Declaration syntax [similar to Active Model Serializers][migration guide]
44
+ - Reduced [memory allocation][benchmarks] and [improved performance][benchmarks]
41
45
  - Support for `has_one` and `has_many`, compose with `flat_one`
42
46
  - Useful development checks to avoid typos and mistakes
43
47
  - Integrates nicely with Rails controllers
44
- - Caching
45
48
 
46
49
  ## Installation 💿
47
50
 
@@ -58,13 +61,13 @@ And then run:
58
61
  ## Usage 🚀
59
62
 
60
63
  You can define a serializer by subclassing `Oj::Serializer`, and specify which
61
- attributes should be serialized to JSON.
64
+ attributes should be serialized.
62
65
 
63
66
  ```ruby
64
67
  class AlbumSerializer < Oj::Serializer
65
68
  attributes :name, :genres
66
69
 
67
- attribute \
70
+ attr
68
71
  def release
69
72
  album.release_date.strftime('%B %d, %Y')
70
73
  end
@@ -76,129 +79,84 @@ end
76
79
  <details>
77
80
  <summary>Example Output</summary>
78
81
 
79
- ```json
82
+ ```ruby
80
83
  {
81
- "name": "Abraxas",
82
- "genres": [
84
+ name: "Abraxas",
85
+ genres: [
83
86
  "Pyschodelic Rock",
84
87
  "Blues Rock",
85
88
  "Jazz Fusion",
86
- "Latin Rock"
89
+ "Latin Rock",
87
90
  ],
88
- "release": "September 23, 1970",
89
- "songs": [
91
+ release: "September 23, 1970",
92
+ songs: [
90
93
  {
91
- "track": 1,
92
- "name": "Sing Winds, Crying Beasts",
93
- "composers": [
94
- "Michael Carabello"
95
- ]
94
+ track: 1,
95
+ name: "Sing Winds, Crying Beasts",
96
+ composers: ["Michael Carabello"],
96
97
  },
97
98
  {
98
- "track": 2,
99
- "name": "Black Magic Woman / Gypsy Queen",
100
- "composers": [
101
- "Peter Green",
102
- "Gábor Szabó"
103
- ]
99
+ track: 2,
100
+ name: "Black Magic Woman / Gypsy Queen",
101
+ composers: ["Peter Green", "Gábor Szabó"],
104
102
  },
105
103
  {
106
- "track": 3,
107
- "name": "Oye como va",
108
- "composers": [
109
- "Tito Puente"
110
- ]
104
+ track: 3,
105
+ name: "Oye como va",
106
+ composers: ["Tito Puente"],
111
107
  },
112
108
  {
113
- "track": 4,
114
- "name": "Incident at Neshabur",
115
- "composers": [
116
- "Alberto Gianquinto",
117
- "Carlos Santana"
118
- ]
109
+ track: 4,
110
+ name: "Incident at Neshabur",
111
+ composers: ["Alberto Gianquinto", "Carlos Santana"],
119
112
  },
120
113
  {
121
- "track": 5,
122
- "name": "Se acabó",
123
- "composers": [
124
- "José Areas"
125
- ]
114
+ track: 5,
115
+ name: "Se acabó",
116
+ composers: ["José Areas"],
126
117
  },
127
118
  {
128
- "track": 6,
129
- "name": "Mother's Daughter",
130
- "composers": [
131
- "Gregg Rolie"
132
- ]
119
+ track: 6,
120
+ name: "Mother's Daughter",
121
+ composers: ["Gregg Rolie"],
133
122
  },
134
123
  {
135
- "track": 7,
136
- "name": "Samba pa ti",
137
- "composers": [
138
- "Santana"
139
- ]
124
+ track: 7,
125
+ name: "Samba pa ti",
126
+ composers: ["Santana"],
140
127
  },
141
128
  {
142
- "track": 8,
143
- "name": "Hope You're Feeling Better",
144
- "composers": [
145
- "Rolie"
146
- ]
129
+ track: 8,
130
+ name: "Hope You're Feeling Better",
131
+ composers: ["Rolie"],
147
132
  },
148
133
  {
149
- "track": 9,
150
- "name": "El Nicoya",
151
- "composers": [
152
- "Areas"
153
- ]
154
- }
155
- ]
134
+ track: 9,
135
+ name: "El Nicoya",
136
+ composers: ["Areas"],
137
+ },
138
+ ],
156
139
  }
157
140
  ```
158
141
  </details>
159
142
 
160
- <br/>
161
-
162
- To use the serializer, the recommended approach is:
143
+ You can then use your new serializer to render an object or collection:
163
144
 
164
145
  ```ruby
165
146
  class AlbumsController < ApplicationController
166
147
  def show
167
- album = Album.find(params[:id])
168
148
  render json: AlbumSerializer.one(album)
169
149
  end
170
150
 
171
151
  def index
172
- albums = Album.all
173
152
  render json: { albums: AlbumSerializer.many(albums) }
174
153
  end
175
154
  end
176
155
  ```
177
156
 
178
- If you are using Rails you can also use something closer to Active Model Serializers by adding [`sugar`][sugar]:
179
-
180
- ```ruby
181
- require 'oj_serializers/sugar'
182
-
183
- class AlbumsController < ApplicationController
184
- def show
185
- album = Album.find(params[:id])
186
- render json: album, serializer: AlbumSerializer
187
- end
188
-
189
- def index
190
- albums = Album.all
191
- render json: albums, each_serializer: AlbumSerializer, root: :albums
192
- end
193
- end
194
- ```
195
-
196
- It's recommended to create your own `BaseSerializer` class in order to easily
197
- add custom extensions, specially when migrating from `active_model_serializers`.
198
-
199
- ## Render DSL 🛠
157
+ ## Rendering 🖨
200
158
 
201
- In order to efficiently reuse the instances, serializers can't be instantiated directly. Use `one` and `many` to serialize objects or enumerables:
159
+ Use `one` to serialize objects, and `many` to serialize enumerables:
202
160
 
203
161
  ```ruby
204
162
  render json: {
@@ -207,173 +165,212 @@ render json: {
207
165
  }
208
166
  ```
209
167
 
210
- You can use these serializers inside arrays, hashes, or even inside `ActiveModel::Serializer` by using a method in the serializer.
211
-
212
- Follow [this discussion][raw_json] to find out more about [the `raw_json` extensions][raw_json] that made this high level of interoperability possible.
213
-
214
- ## Attributes DSL 🛠
168
+ Serializers can be rendered arrays, hashes, or even inside `ActiveModel::Serializer`
169
+ by using a method in the serializer, making it very easy to combine with other
170
+ libraries and migrate incrementally.
215
171
 
216
- Attributes methods can be used to define which model attributes should be serialized
217
- to JSON. Each method provides a different strategy to obtain the values to serialize.
172
+ You can use `render` as a shortcut for `one` and `many`, but it might be less readable:
218
173
 
219
- The internal design is simple and extensible, so creating new strategies requires very little code.
220
- Please open an issue if you need help 😃
174
+ ```ruby
175
+ render json: {
176
+ favorite_album: AlbumSerializer.render(album),
177
+ purchased_albums: AlbumSerializer.render(albums),
178
+ }
179
+ ```
221
180
 
222
- ### `attributes`
181
+ ## Attributes DSL 🪄
223
182
 
224
- Obtains the attribute value by calling a method in the object being serialized.
183
+ Specify which attributes should be rendered by calling a method in the object to serialize.
225
184
 
226
185
  ```ruby
227
186
  class PlayerSerializer < Oj::Serializer
228
- attributes :full_name
187
+ attributes :first_name, :last_name, :full_name
229
188
  end
230
189
  ```
231
190
 
232
- Have in mind that unlike Active Model Serializers, it will _not_ take into
233
- account methods defined in the serializer. Being explicit about where the
234
- attribute is coming from makes the serializers easier to understand and more
235
- maintainable.
236
-
237
- ### `serializer_attributes`
238
-
239
- Obtains the attribute value by calling a method defined in the serializer.
240
-
241
-
242
- You may call [`serializer_attributes`](https://github.com/ElMassimo/oj_serializers/blob/master/spec/support/serializers/song_serializer.rb#L13-L15) or use the `attribute` inline syntax:
191
+ You can serialize custom values by specifying that a method is an `attribute`:
243
192
 
244
193
  ```ruby
245
194
  class PlayerSerializer < Oj::Serializer
246
- attribute \
247
- def full_name
195
+ attribute :name do
248
196
  "#{player.first_name} #{player.last_name}"
249
197
  end
250
- end
251
- ```
252
-
253
- Instance methods can access the object by the serializer name without the
254
- `Serializer` suffix, `player` in the example above, or directly as `@object`.
255
-
256
- You can customize this by using [`object_as`](https://github.com/ElMassimo/oj_serializers#using-a-different-alias-for-the-internal-object).
257
198
 
258
- ### `ams_attributes` 🐌
199
+ # or
259
200
 
260
- Works like `attributes` in Active Model Serializers, by calling a method in the serializer if defined, or calling `read_attribute_for_serialization` in the model.
261
-
262
- ```ruby
263
- class AlbumSerializer < Oj::Serializer
264
- ams_attributes :name, :release
265
-
266
- def release
267
- album.release_date.strftime('%B %d, %Y')
201
+ attribute
202
+ def name
203
+ "#{player.first_name} #{player.last_name}"
268
204
  end
269
205
  end
270
206
  ```
271
207
 
272
- Should only be used when migrating from Active Model Serializers, as it's slower and can create confusion.
208
+ > **Note**
209
+ >
210
+ > In this example, `player` was inferred from `PlayerSerializer`.
211
+ >
212
+ > You can customize this by using [`object_as`](#using-a-different-alias-for-the-internal-object).
273
213
 
274
- Instead, use `attributes` for model methods, and the inline `attribute` for serializer attributes. Being explicit makes serializers easier to understand, and to maintain.
275
214
 
276
- Please refer to the [migration guide] for more information.
215
+ ### Associations 🔗
277
216
 
278
- ### `hash_attributes` 🚀
217
+ Use `has_one` to serialize individual objects, and `has_many` to serialize a collection.
279
218
 
280
- Very convenient when serializing Hash-like structures, this strategy uses the `[]` operator.
219
+ You must specificy which serializer to use with the `serializer` option.
281
220
 
282
221
  ```ruby
283
- class PersonSerializer < Oj::Serializer
284
- hash_attributes 'first_name', :last_name
222
+ class SongSerializer < Oj::Serializer
223
+ has_one :album, serializer: AlbumSerializer
224
+ has_many :composers, serializer: ComposerSerializer
285
225
  end
286
-
287
- PersonSerializer.one('first_name' => 'Mary', :middle_name => 'Jane', :last_name => 'Watson')
288
- # {"first_name":"Mary","last_name":"Watson"}
289
226
  ```
290
227
 
291
- ### `mongo_attributes` 🚀
292
-
293
- Reads data directly from `attributes` in a [Mongoid] document.
294
-
295
- By skipping type casting, coercion, and defaults, it [achieves the best performance][raw_benchmarks].
296
-
297
- Although there are some downsides, depending on how consistent your schema is,
298
- and which kind of consumer the API has, it can be really powerful.
228
+ Specify a different value for the association by providing a block:
299
229
 
300
230
  ```ruby
301
- class AlbumSerializer < Oj::Serializer
302
- mongo_attributes :id, :name
231
+ class SongSerializer < Oj::Serializer
232
+ has_one :album, serializer: AlbumSerializer do
233
+ Album.find_by(song_ids: song.id)
234
+ end
303
235
  end
304
236
  ```
305
237
 
306
- ## Associations DSL 🛠
238
+ In case you need to pass options, you can call the serializer manually:
307
239
 
308
- Use `has_one` to serialize individual objects, and `has_many` to serialize a collection.
240
+ ```ruby
241
+ class SongSerializer < Oj::Serializer
242
+ attribute :album do
243
+ AlbumSerializer.one(song.album, for_song: song)
244
+ end
245
+ end
246
+ ```
309
247
 
310
- The value for the association is obtained from a serializer method if defined, or by calling the method in the object being serialized.
248
+ ### Aliasing or renaming attributes ↔️
311
249
 
312
- You must specificy which serializer to use with the `serializer` option.
250
+ You can pass `as` when defining an attribute or association to serialize it
251
+ using a different key:
313
252
 
314
253
  ```ruby
315
254
  class SongSerializer < Oj::Serializer
316
- has_one :album, serializer: AlbumSerializer
317
- has_many :composers, serializer: ComposerSerializer
255
+ has_one :album, as: :first_release, serializer: AlbumSerializer
318
256
 
319
- # You can also compose serializers using `flat_one`.
320
- flat_one :song, serializer: SongMetadataSerializer
257
+ attributes title: {as: :name}
258
+
259
+ # or as a shortcut
260
+ attributes title: :name
321
261
  end
322
262
  ```
323
263
 
324
- The associations DSL is more concise and achieves better performance, so prefer to use it instead of manually definining attributes:
264
+ ### Conditional attributes
265
+
266
+ You can render attributes and associations conditionally by using `:if`.
325
267
 
326
268
  ```ruby
327
- class SongSerializer < SongMetadataSerializer
328
- attribute \
329
- def album
330
- AlbumSerializer.one(song.album)
331
- end
269
+ class PlayerSerializer < Oj::Serializer
270
+ attributes :first_name, :last_name, if: -> { player.display_name? }
332
271
 
333
- attribute \
334
- def composers
335
- ComposerSerializer.many(song.composers)
336
- end
272
+ has_one :album, serializer: AlbumSerializer, if: -> { player.album }
337
273
  end
338
274
  ```
339
275
 
340
- ## Other DSL 🛠
276
+ This is useful in cases where you don't want to `null` values to be in the response.
277
+
278
+ ## Advanced Usage 🧙‍♂️
341
279
 
342
280
  ### Using a different alias for the internal object
343
281
 
344
- You can use `object_as` to create an alias for the serialized object to access it from instance methods:
282
+ In most cases, the default alias for the `object` will be convenient enough.
283
+
284
+ However, if you would like to specify it manually, use `object_as`:
345
285
 
346
286
  ```ruby
347
287
  class DiscographySerializer < Oj::Serializer
348
288
  object_as :artist
349
289
 
350
290
  # Now we can use `artist` instead of `object` or `discography`.
291
+ attribute
351
292
  def latest_albums
352
293
  artist.albums.desc(:year)
353
294
  end
354
295
  end
355
296
  ```
356
297
 
357
- ### Rendering an attribute conditionally
298
+ ### Identifier attributes
358
299
 
359
- All the attributes and association methods can take an `if` option to render conditionally.
300
+ The `identifier` method allows you to only include an identifier if the record
301
+ or document has been persisted.
360
302
 
361
303
  ```ruby
362
304
  class AlbumSerializer < Oj::Serializer
363
- mongo_attributes :release_date, if: -> { album.released? }
305
+ identifier
306
+
307
+ # or if it's a different field
308
+ identifier :uuid
309
+ end
310
+ ```
311
+
312
+ Additionally, identifier fields are always rendered first, even when sorting
313
+ fields alphabetically.
314
+
315
+ ### Transforming attribute keys 🗝
316
+
317
+ When serialized data will be consumed from a client language that has different
318
+ naming conventions, it can be convenient to transform keys accordingly.
319
+
320
+ For example, when rendering an API to be consumed from the browser via JavaScript,
321
+ where properties are traditionally named using camel case.
322
+
323
+ Use `transform_keys` to handle that conversion.
324
+
325
+ ```ruby
326
+ class BaseSerializer < Oj::Serializer
327
+ transform_keys :camelize
328
+
329
+ # shortcut for
330
+ transform_keys -> (key) { key.to_s.camelize(:lower) }
331
+ end
332
+ ```
333
+
334
+ This has no performance impact, as keys will be transformed at load time.
335
+
336
+ ### Sorting attributes 📶
364
337
 
365
- has_many :songs, serializer: SongSerializer, if: -> { album.songs.any? }
338
+ By default attributes are rendered in the order they are defined.
366
339
 
367
- # You can achieve the same by manually defining a method:
368
- def include_songs?
369
- album.songs.any?
340
+ If you would like to sort attributes alphabetically, you can specify it at a
341
+ serializer level:
342
+
343
+ ```ruby
344
+ class BaseSerializer < Oj::Serializer
345
+ sort_attributes_by :name # or a Proc
346
+ end
347
+ ```
348
+
349
+ This has no performance impact, as attributes will be sorted at load time.
350
+
351
+ ### Path helpers 🛣
352
+
353
+ In case you need to access path helpers in your serializers, you can use the
354
+ following:
355
+
356
+ ```ruby
357
+ class BaseSerializer < Oj::Serializer
358
+ include Rails.application.routes.url_helpers
359
+
360
+ def default_url_options
361
+ Rails.application.routes.default_url_options
370
362
  end
371
363
  end
372
364
  ```
373
365
 
374
- ### Memoization & Local State
366
+ One slight variation that might make it easier to maintain in the long term is
367
+ to use a separate singleton service to provide the url helpers and options, and
368
+ make it available as `urls`.
375
369
 
376
- Serializers are designed to be stateless so that an instanced can be reused, but sometimes it's convenient to store intermediate calculations.
370
+ ### Memoization & local state
371
+
372
+ Serializers are designed to be stateless so that an instanced can be reused, but
373
+ sometimes it's convenient to store intermediate calculations.
377
374
 
378
375
  Use `memo` for memoization and storing temporary information.
379
376
 
@@ -381,7 +378,7 @@ Use `memo` for memoization and storing temporary information.
381
378
  class DownloadSerializer < Oj::Serializer
382
379
  attributes :filename, :size
383
380
 
384
- attribute \
381
+ attribute
385
382
  def progress
386
383
  "#{ last_event&.progress || 0 }%"
387
384
  end
@@ -396,9 +393,50 @@ private
396
393
  end
397
394
  ```
398
395
 
396
+ ### `hash_attributes` 🚀
397
+
398
+ Very convenient when serializing Hash-like structures, this strategy uses the `[]` operator.
399
+
400
+ ```ruby
401
+ class PersonSerializer < Oj::Serializer
402
+ hash_attributes 'first_name', :last_name
403
+ end
404
+
405
+ PersonSerializer.one('first_name' => 'Mary', :middle_name => 'Jane', :last_name => 'Watson')
406
+ # {first_name: "Mary", last_name: "Watson"}
407
+ ```
408
+
409
+ ### `mongo_attributes` 🚀
410
+
411
+ Reads data directly from `attributes` in a [Mongoid] document.
412
+
413
+ By skipping type casting, coercion, and defaults, it [achieves the best performance][raw_benchmarks].
414
+
415
+ Although there are some downsides, depending on how consistent your schema is,
416
+ and which kind of consumer the API has, it can be really powerful.
417
+
418
+ ```ruby
419
+ class AlbumSerializer < Oj::Serializer
420
+ mongo_attributes :id, :name
421
+ end
422
+ ```
423
+
399
424
  ### Caching 📦
400
425
 
401
- Use `cached` to leverage key-based caching, which calls `cache_key` in the object. You can also provide a lambda to `cached_with_key` to define a custom key:
426
+ Usually rendering is so fast that __turning caching on can be slower__.
427
+
428
+ However, in cases of deeply nested structures, unpredictable query patterns, or
429
+ methods that take a long time to run, caching can improve performance.
430
+
431
+ To enable caching, use `cached`, which calls `cache_key` in the object:
432
+
433
+ ```ruby
434
+ class CachedUserSerializer < UserSerializer
435
+ cached
436
+ end
437
+ ```
438
+
439
+ You can also provide a lambda to `cached_with_key` to define a custom key:
402
440
 
403
441
  ```ruby
404
442
  class CachedUserSerializer < UserSerializer
@@ -408,33 +446,164 @@ class CachedUserSerializer < UserSerializer
408
446
  end
409
447
  ```
410
448
 
411
- It will leverage `fetch_multi` when serializing a collection with `many` or `has_many`, to minimize the amount of round trips needed to read and write all items to cache. This works specially well if your cache store also supports `write_multi`.
449
+ It will leverage `fetch_multi` when serializing a collection with `many` or
450
+ `has_many`, to minimize the amount of round trips needed to read and write all
451
+ items to cache.
452
+
453
+ This works specially well if your cache store also supports `write_multi`.
454
+
455
+ ### Writing to JSON
456
+
457
+ In some corner cases it might be faster to serialize using a `Oj::StringWriter`,
458
+ which you can access by using `one_as_json` and `many_as_json`.
459
+
460
+ Alternatively, you can toggle this mode at a serializer level by using
461
+ `default_format :json`, or configure it globally from your base serializer:
462
+
463
+ ```ruby
464
+ class BaseSerializer < Oj::Serializer
465
+ default_format :json
466
+ end
467
+ ```
468
+
469
+ This will change the default shortcuts (`render`, `one`, `one_if`, and `many`),
470
+ so that the serializer writes directly to JSON instead of returning a Hash.
471
+
472
+ > **Note**
473
+ >
474
+ > This was the default behavior in `oj_serializers` v1, but was replaced with
475
+ `default_format :hash` in v2.
476
+
477
+ <details>
478
+ <summary>Example Output</summary>
479
+
480
+ ```json
481
+ {
482
+ "name": "Abraxas",
483
+ "genres": [
484
+ "Pyschodelic Rock",
485
+ "Blues Rock",
486
+ "Jazz Fusion",
487
+ "Latin Rock"
488
+ ],
489
+ "release": "September 23, 1970",
490
+ "songs": [
491
+ {
492
+ "track": 1,
493
+ "name": "Sing Winds, Crying Beasts",
494
+ "composers": [
495
+ "Michael Carabello"
496
+ ]
497
+ },
498
+ {
499
+ "track": 2,
500
+ "name": "Black Magic Woman / Gypsy Queen",
501
+ "composers": [
502
+ "Peter Green",
503
+ "Gábor Szabó"
504
+ ]
505
+ },
506
+ {
507
+ "track": 3,
508
+ "name": "Oye como va",
509
+ "composers": [
510
+ "Tito Puente"
511
+ ]
512
+ },
513
+ {
514
+ "track": 4,
515
+ "name": "Incident at Neshabur",
516
+ "composers": [
517
+ "Alberto Gianquinto",
518
+ "Carlos Santana"
519
+ ]
520
+ },
521
+ {
522
+ "track": 5,
523
+ "name": "Se acabó",
524
+ "composers": [
525
+ "José Areas"
526
+ ]
527
+ },
528
+ {
529
+ "track": 6,
530
+ "name": "Mother's Daughter",
531
+ "composers": [
532
+ "Gregg Rolie"
533
+ ]
534
+ },
535
+ {
536
+ "track": 7,
537
+ "name": "Samba pa ti",
538
+ "composers": [
539
+ "Santana"
540
+ ]
541
+ },
542
+ {
543
+ "track": 8,
544
+ "name": "Hope You're Feeling Better",
545
+ "composers": [
546
+ "Rolie"
547
+ ]
548
+ },
549
+ {
550
+ "track": 9,
551
+ "name": "El Nicoya",
552
+ "composers": [
553
+ "Areas"
554
+ ]
555
+ }
556
+ ]
557
+ }
558
+ ```
559
+ </details>
412
560
 
413
- Usually serialization happens so fast that __turning caching on can be slower__. Always benchmark to make sure it's worth it, and use caching only for time-consuming or deeply nested structures.
561
+ Even when using this mode, you can still use rendered values inside arrays,
562
+ hashes, and other serializers, thanks to [the `raw_json` extensions][raw_json].
414
563
 
415
564
  ## Design 📐
416
565
 
417
566
  Unlike `ActiveModel::Serializer`, which builds a Hash that then gets encoded to
418
- JSON, this implementation uses `Oj::StringWriter` to write JSON directly,
567
+ JSON, this implementation can use `Oj::StringWriter` to write JSON directly,
419
568
  greatly reducing the overhead of allocating and garbage collecting the hashes.
420
569
 
421
570
  It also allocates a single instance per serializer class, which makes it easy
422
571
  to use, while keeping memory usage under control.
423
572
 
573
+ The internal design is simple and extensible, and because the library is written
574
+ in Ruby, creating new serialization strategies requires very little code.
575
+ Please open a [Discussion] if you need help 😃
576
+
424
577
  ### Comparison with other libraries
425
578
 
426
579
  `ActiveModel::Serializer` instantiates one serializer object per item to be serialized.
427
580
 
428
- Other libraries such as [`jsonapi-serializer`][jsonapi] evaluate serializers in the context of
429
- a `class` instead of an `instance` of a class. Although it is efficient in terms
430
- of memory usage, the downside is that you can't use instance methods or local
431
- memoization, and any mixins must be applied to the class itself.
581
+ Other libraries such as [`blueprinter`][blueprinter] [`jsonapi-serializer`][jsonapi]
582
+ evaluate serializers in the context of a `class` instead of an `instance` of a class.
583
+ The downside is that you can't use instance methods or local memoization, and any
584
+ mixins must be applied to the class itself.
432
585
 
433
586
  [`panko-serializer`][panko] also uses `Oj::StringWriter`, but it has the big downside of having to own the entire render tree. Putting a serializer inside a Hash or an Active Model Serializer and serializing that to JSON doesn't work, making a gradual migration harder to achieve. Also, it's optimized for Active Record but I needed good Mongoid support.
434
587
 
435
588
  `Oj::Serializer` combines some of these ideas, by using instances, but reusing them to avoid object allocations. Serializing 10,000 items instantiates a single serializer. Unlike `panko-serializer`, it doesn't suffer from [double encoding problems](https://panko.dev/docs/response-bag) so it's easier to use.
436
589
 
437
- As a result, migrating from `active_model_serializers` is relatively straightforward because instance methods, inheritance, and mixins work as usual.
590
+ Follow [this discussion][raw_json] to find out more about [the `raw_json` extensions][raw_json] that made this high level of interoperability possible.
591
+
592
+ As a result, migrating from `active_model_serializers` is relatively
593
+ straightforward because instance methods, inheritance, and mixins work as usual.
594
+
595
+ ### Benchmarks 📊
596
+
597
+ This library includes some [benchmarks] to compare performance with similar libraries.
598
+
599
+ See [this pull request](https://github.com/ElMassimo/oj_serializers/pull/9) for a quick comparison,
600
+ or check the CI to see the latest results.
601
+
602
+ ### Migrating from other libraries
603
+
604
+ Please refer to the [migration guide] for a full discussion of the compatibility
605
+ modes available to make it easier to migrate from `active_model_serializers` and
606
+ similar libraries.
438
607
 
439
608
  ## Formatting 📏
440
609