json_serializers 2.0.3

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 ADDED
@@ -0,0 +1,699 @@
1
+ <h1 align="center">
2
+ Oj Serializers
3
+ <p align="center">
4
+ <a href="https://github.com/ElMassimo/oj_serializers/actions"><img alt="Build Status" src="https://github.com/ElMassimo/oj_serializers/workflows/build/badge.svg"/></a>
5
+ <a href="https://codeclimate.com/github/ElMassimo/oj_serializers"><img alt="Maintainability" src="https://codeclimate.com/github/ElMassimo/oj_serializers/badges/gpa.svg"/></a>
6
+ <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>
7
+ <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>
8
+ <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>
9
+ </p>
10
+ </h1>
11
+
12
+ Faster JSON serializers for Ruby, built on top of the powerful [`oj`][oj] library.
13
+
14
+ [oj]: https://github.com/ohler55/oj
15
+ [mongoid]: https://github.com/mongodb/mongoid
16
+ [ams]: https://github.com/rails-api/active_model_serializers
17
+ [jsonapi]: https://github.com/jsonapi-serializer/jsonapi-serializer
18
+ [panko]: https://github.com/panko-serializer/panko_serializer
19
+ [blueprinter]: https://github.com/procore/blueprinter
20
+ [benchmarks]: https://github.com/ElMassimo/oj_serializers/tree/master/benchmarks
21
+ [raw_benchmarks]: https://github.com/ElMassimo/oj_serializers/blob/main/benchmarks/document_benchmark.rb
22
+ [sugar]: https://github.com/ElMassimo/oj_serializers/blob/main/lib/oj_serializers/sugar.rb#L14
23
+ [migration guide]: https://github.com/ElMassimo/oj_serializers/blob/main/MIGRATION_GUIDE.md
24
+ [design]: https://github.com/ElMassimo/oj_serializers#design-
25
+ [associations]: https://github.com/ElMassimo/oj_serializers#associations-
26
+ [compose]: https://github.com/ElMassimo/oj_serializers#composing-serializers-
27
+ [raw_json]: https://github.com/ohler55/oj/issues/542
28
+ [trailing_commas]: https://maximomussini.com/posts/trailing-commas/
29
+ [render dsl]: https://github.com/ElMassimo/oj_serializers#render-dsl-
30
+ [sorbet]: https://sorbet.org/
31
+ [Discussion]: https://github.com/ElMassimo/oj_serializers/discussions
32
+ [TypeScript]: https://www.typescriptlang.org/
33
+ [types_from_serializers]: https://github.com/ElMassimo/types_from_serializers
34
+ [inheritance]: https://github.com/ElMassimo/types_from_serializers/blob/main/playground/vanilla/app/serializers/song_with_videos_serializer.rb#L1
35
+
36
+ ## Why? 🤔
37
+
38
+ [`ActiveModel::Serializer`][ams] has a nice DSL, but it allocates many objects
39
+ leading to memory bloat, time spent on GC, and lower performance.
40
+
41
+ `JsonSerializer` provides a similar API, with [better performance][benchmarks].
42
+
43
+ Learn more about [how this library achieves its performance][design].
44
+
45
+ ## Features ⚡️
46
+
47
+ - Intuitive declaration syntax, supporting mixins and inheritance
48
+ - Reduced [memory allocation][benchmarks] and [improved performance][benchmarks]
49
+ - Generate [TypeScript interfaces automatically][types_from_serializers]
50
+ - Support for [`has_one`][associations] and [`has_many`][associations], compose with [`flat_one`][compose]
51
+ - Useful development checks to avoid typos and mistakes
52
+ - [Migrate easily from Active Model Serializers][migration guide]
53
+
54
+ ## Installation 💿
55
+
56
+ Add this line to your application's Gemfile:
57
+
58
+ ```ruby
59
+ gem 'json_serializers'
60
+ ```
61
+
62
+ And then run:
63
+
64
+ $ bundle install
65
+
66
+ ## Usage 🚀
67
+
68
+ You can define a serializer by subclassing `JsonSerializer`, and specify which
69
+ attributes should be serialized.
70
+
71
+ ```ruby
72
+ class AlbumSerializer < JsonSerializer
73
+ attributes :name, :genres
74
+
75
+ attribute :release do
76
+ album.release_date.strftime('%B %d, %Y')
77
+ end
78
+
79
+ has_many :songs, serializer: SongSerializer
80
+ end
81
+ ```
82
+
83
+ <details>
84
+ <summary>Example Output</summary>
85
+
86
+ ```ruby
87
+ {
88
+ name: "Abraxas",
89
+ genres: [
90
+ "Pyschodelic Rock",
91
+ "Blues Rock",
92
+ "Jazz Fusion",
93
+ "Latin Rock",
94
+ ],
95
+ release: "September 23, 1970",
96
+ songs: [
97
+ {
98
+ track: 1,
99
+ name: "Sing Winds, Crying Beasts",
100
+ composers: ["Michael Carabello"],
101
+ },
102
+ {
103
+ track: 2,
104
+ name: "Black Magic Woman / Gypsy Queen",
105
+ composers: ["Peter Green", "Gábor Szabó"],
106
+ },
107
+ {
108
+ track: 3,
109
+ name: "Oye como va",
110
+ composers: ["Tito Puente"],
111
+ },
112
+ {
113
+ track: 4,
114
+ name: "Incident at Neshabur",
115
+ composers: ["Alberto Gianquinto", "Carlos Santana"],
116
+ },
117
+ {
118
+ track: 5,
119
+ name: "Se acabó",
120
+ composers: ["José Areas"],
121
+ },
122
+ {
123
+ track: 6,
124
+ name: "Mother's Daughter",
125
+ composers: ["Gregg Rolie"],
126
+ },
127
+ {
128
+ track: 7,
129
+ name: "Samba pa ti",
130
+ composers: ["Santana"],
131
+ },
132
+ {
133
+ track: 8,
134
+ name: "Hope You're Feeling Better",
135
+ composers: ["Rolie"],
136
+ },
137
+ {
138
+ track: 9,
139
+ name: "El Nicoya",
140
+ composers: ["Areas"],
141
+ },
142
+ ],
143
+ }
144
+ ```
145
+ </details>
146
+
147
+ You can then use your new serializer to render an object or collection:
148
+
149
+ ```ruby
150
+ class AlbumsController < ApplicationController
151
+ def show
152
+ render json: AlbumSerializer.one(album)
153
+ end
154
+
155
+ def index
156
+ render json: { albums: AlbumSerializer.many(albums) }
157
+ end
158
+ end
159
+ ```
160
+
161
+ <details>
162
+ <summary>Active Model Serializers style</summary>
163
+
164
+ ```ruby
165
+ require "json_serializers/sugar" # In an initializer
166
+
167
+ class AlbumsController < ApplicationController
168
+ def show
169
+ render json: album, serializer: AlbumSerializer
170
+ end
171
+
172
+ def index
173
+ render json: albums, root: :albums, each_serializer: AlbumSerializer
174
+ end
175
+ end
176
+ ```
177
+ </details>
178
+
179
+ ## Rendering 🖨
180
+
181
+ Use `one` to serialize objects, and `many` to serialize enumerables:
182
+
183
+ ```ruby
184
+ render json: {
185
+ favorite_album: AlbumSerializer.one(album),
186
+ purchased_albums: AlbumSerializer.many(albums),
187
+ }
188
+ ```
189
+
190
+ Serializers can be rendered arrays, hashes, or even inside `ActiveModel::Serializer`
191
+ by using a method in the serializer, making it very easy to combine with other
192
+ libraries and migrate incrementally.
193
+
194
+ `render` is a shortcut for `one` and `many`:
195
+
196
+ ```ruby
197
+ render json: {
198
+ favorite_album: AlbumSerializer.render(album),
199
+ purchased_albums: AlbumSerializer.render(albums),
200
+ }
201
+ ```
202
+
203
+ ## Attributes DSL 🪄
204
+
205
+ Specify which attributes should be rendered by calling a method in the object to serialize.
206
+
207
+ ```ruby
208
+ class PlayerSerializer < JsonSerializer
209
+ attributes :first_name, :last_name, :full_name
210
+ end
211
+ ```
212
+
213
+ You can serialize custom values by specifying that a method is an `attribute`:
214
+
215
+ ```ruby
216
+ class PlayerSerializer < JsonSerializer
217
+ attribute :name do
218
+ "#{player.first_name} #{player.last_name}"
219
+ end
220
+
221
+ # or
222
+
223
+ attribute
224
+ def name
225
+ "#{player.first_name} #{player.last_name}"
226
+ end
227
+ end
228
+ ```
229
+
230
+ > **Note**
231
+ >
232
+ > In this example, `player` was inferred from `PlayerSerializer`.
233
+ >
234
+ > You can customize this by using [`object_as`](#using-a-different-alias-for-the-internal-object).
235
+
236
+
237
+ ### Associations 🔗
238
+
239
+ Use `has_one` to serialize individual objects, and `has_many` to serialize a collection.
240
+
241
+ You must specificy which serializer to use with the `serializer` option.
242
+
243
+ ```ruby
244
+ class SongSerializer < JsonSerializer
245
+ has_one :album, serializer: AlbumSerializer
246
+ has_many :composers, serializer: ComposerSerializer
247
+ end
248
+ ```
249
+
250
+ Specify a different value for the association by providing a block:
251
+
252
+ ```ruby
253
+ class SongSerializer < JsonSerializer
254
+ has_one :album, serializer: AlbumSerializer do
255
+ Album.find_by(song_ids: song.id)
256
+ end
257
+ end
258
+ ```
259
+
260
+ In case you need to pass options, you can call the serializer manually:
261
+
262
+ ```ruby
263
+ class SongSerializer < JsonSerializer
264
+ attribute :album do
265
+ AlbumSerializer.one(song.album, for_song: song)
266
+ end
267
+ end
268
+ ```
269
+
270
+ ### Aliasing or renaming attributes ↔️
271
+
272
+ You can pass `as` when defining an attribute or association to serialize it
273
+ using a different key:
274
+
275
+ ```ruby
276
+ class SongSerializer < JsonSerializer
277
+ has_one :album, as: :first_release, serializer: AlbumSerializer
278
+
279
+ attributes title: {as: :name}
280
+
281
+ # or as a shortcut
282
+ attributes title: :name
283
+ end
284
+ ```
285
+
286
+ ### Conditional attributes ❔
287
+
288
+ You can render attributes and associations conditionally by using `:if`.
289
+
290
+ ```ruby
291
+ class PlayerSerializer < JsonSerializer
292
+ attributes :first_name, :last_name, if: -> { player.display_name? }
293
+
294
+ has_one :album, serializer: AlbumSerializer, if: -> { player.album }
295
+ end
296
+ ```
297
+
298
+ This is useful in cases where you don't want to `null` values to be in the response.
299
+
300
+ ## Advanced Usage 🧙‍♂️
301
+
302
+ ### Using a different alias for the internal object
303
+
304
+ In most cases, the default alias for the `object` will be convenient enough.
305
+
306
+ However, if you would like to specify it manually, use `object_as`:
307
+
308
+ ```ruby
309
+ class DiscographySerializer < JsonSerializer
310
+ object_as :artist
311
+
312
+ # Now we can use `artist` instead of `object` or `discography`.
313
+ attribute
314
+ def latest_albums
315
+ artist.albums.desc(:year)
316
+ end
317
+ end
318
+ ```
319
+
320
+ ### Identifier attributes
321
+
322
+ The `identifier` method allows you to only include an identifier if the record
323
+ or document has been persisted.
324
+
325
+ ```ruby
326
+ class AlbumSerializer < JsonSerializer
327
+ identifier
328
+
329
+ # or if it's a different field
330
+ identifier :uuid
331
+ end
332
+ ```
333
+
334
+ Additionally, identifier fields are always rendered first, even when sorting
335
+ fields alphabetically.
336
+
337
+ ### Transforming attribute keys 🗝
338
+
339
+ When serialized data will be consumed from a client language that has different
340
+ naming conventions, it can be convenient to transform keys accordingly.
341
+
342
+ For example, when rendering an API to be consumed from the browser via JavaScript,
343
+ where properties are traditionally named using camel case.
344
+
345
+ Use `transform_keys` to handle that conversion.
346
+
347
+ ```ruby
348
+ class BaseSerializer < JsonSerializer
349
+ transform_keys :camelize
350
+
351
+ # shortcut for
352
+ transform_keys -> (key) { key.to_s.camelize(:lower) }
353
+ end
354
+ ```
355
+
356
+ This has no performance impact, as keys will be transformed at load time.
357
+
358
+ ### Sorting attributes 📶
359
+
360
+ By default attributes are rendered in the order they are defined.
361
+
362
+ If you would like to sort attributes alphabetically, you can specify it at a
363
+ serializer level:
364
+
365
+ ```ruby
366
+ class BaseSerializer < JsonSerializer
367
+ sort_attributes_by :name # or a Proc
368
+ end
369
+ ```
370
+
371
+ This has no performance impact, as attributes will be sorted at load time.
372
+
373
+ ### Path helpers 🛣
374
+
375
+ In case you need to access path helpers in your serializers, you can use the
376
+ following:
377
+
378
+ ```ruby
379
+ class BaseSerializer < JsonSerializer
380
+ include Rails.application.routes.url_helpers
381
+
382
+ def default_url_options
383
+ Rails.application.routes.default_url_options
384
+ end
385
+ end
386
+ ```
387
+
388
+ One slight variation that might make it easier to maintain in the long term is
389
+ to use a separate singleton service to provide the url helpers and options, and
390
+ make it available as `urls`.
391
+
392
+ ### Generating TypeScript automatically 🤖
393
+
394
+ It's easy for the backend and the frontend to become out of sync. Traditionally, preventing bugs requires writing extensive integration tests.
395
+
396
+ [TypeScript] is a great tool to catch this kind of bugs and mistakes, as it can detect incorrect usages and missing fields, but writing types manually is cumbersome, and they can become stale over time, giving a false sense of confidence.
397
+
398
+ [`types_from_serializers`][types_from_serializers] extends this library to allow embedding type information, as well as inferring types from the SQL schema when available, and uses this information to automatically generate TypeScript interfaces from your serializers.
399
+
400
+ As a result, it's posible to easily detect mismatches between the backend and the frontend, as well as make the fields more discoverable and provide great autocompletion in the frontend, without having to manually write the types.
401
+
402
+ ### Composing serializers 🧱
403
+
404
+ There are three options to [compose serializers](https://github.com/ElMassimo/oj_serializers/discussions/10#discussioncomment-5523921): [inheritance], mixins, and `flat_one`.
405
+
406
+ Use `flat_one` to include all attributes from a different serializer:
407
+
408
+ ```ruby
409
+ class AttachmentSerializer < BaseSerializer
410
+ identifier
411
+
412
+ class BlobSerializer < BaseSerializer
413
+ attributes :filename, :byte_size, :content_type, :created_at
414
+ end
415
+
416
+ flat_one :blob, serializer: BlobSerializer
417
+ end
418
+ ```
419
+
420
+ Think of it as `has_one` without a "root", all the attributes are added directly.
421
+
422
+ <details>
423
+ <summary>Example Output</summary>
424
+
425
+ ```ruby
426
+ {
427
+ id: 5,
428
+ filename: "image.jpg,
429
+ byte_size: 256074,
430
+ content_type: "image/jpeg",
431
+ created_at: "2022-08-04T17:25:12.637-07:00",
432
+ }
433
+ ```
434
+ </details>
435
+
436
+ This is especially convenient when using [`types_from_serializers`][types_from_serializers],
437
+ as it enables automatic type inference for the included attributes.
438
+
439
+ ### Memoization & local state
440
+
441
+ Serializers are designed to be stateless so that an instanced can be reused, but
442
+ sometimes it's convenient to store intermediate calculations.
443
+
444
+ Use `memo` for memoization and storing temporary information.
445
+
446
+ ```ruby
447
+ class DownloadSerializer < JsonSerializer
448
+ attributes :filename, :size
449
+
450
+ attribute
451
+ def progress
452
+ "#{ last_event&.progress || 0 }%"
453
+ end
454
+
455
+ private
456
+
457
+ def last_event
458
+ memo.fetch(:last_event) {
459
+ download.events.desc(:created_at).first
460
+ }
461
+ end
462
+ end
463
+ ```
464
+
465
+ ### `hash_attributes` 🚀
466
+
467
+ Very convenient when serializing Hash-like structures, this strategy uses the `[]` operator.
468
+
469
+ ```ruby
470
+ class PersonSerializer < JsonSerializer
471
+ hash_attributes 'first_name', :last_name
472
+ end
473
+
474
+ PersonSerializer.one('first_name' => 'Mary', :middle_name => 'Jane', :last_name => 'Watson')
475
+ # {first_name: "Mary", last_name: "Watson"}
476
+ ```
477
+
478
+ ### `mongo_attributes` 🚀
479
+
480
+ Reads data directly from `attributes` in a [Mongoid] document.
481
+
482
+ By skipping type casting, coercion, and defaults, it [achieves the best performance][raw_benchmarks].
483
+
484
+ Although there are some downsides, depending on how consistent your schema is,
485
+ and which kind of consumer the API has, it can be really powerful.
486
+
487
+ ```ruby
488
+ class AlbumSerializer < JsonSerializer
489
+ mongo_attributes :id, :name
490
+ end
491
+ ```
492
+
493
+ ### Caching 📦
494
+
495
+ Usually rendering is so fast that __turning caching on can be slower__.
496
+
497
+ However, in cases of deeply nested structures, unpredictable query patterns, or
498
+ methods that take a long time to run, caching can improve performance.
499
+
500
+ To enable caching, use `cached`, which calls `cache_key` in the object:
501
+
502
+ ```ruby
503
+ class CachedUserSerializer < UserSerializer
504
+ cached
505
+ end
506
+ ```
507
+
508
+ You can also provide a lambda to `cached_with_key` to define a custom key:
509
+
510
+ ```ruby
511
+ class CachedUserSerializer < UserSerializer
512
+ cached_with_key ->(user) {
513
+ "#{ user.id }/#{ user.current_sign_in_at }"
514
+ }
515
+ end
516
+ ```
517
+
518
+ It will leverage `fetch_multi` when serializing a collection with `many` or
519
+ `has_many`, to minimize the amount of round trips needed to read and write all
520
+ items to cache.
521
+
522
+ This works specially well if your cache store also supports `write_multi`.
523
+
524
+ ### Writing to JSON
525
+
526
+ In some corner cases it might be faster to serialize using a `Oj::StringWriter`,
527
+ which you can access by using `one_as_json` and `many_as_json`.
528
+
529
+ Alternatively, you can toggle this mode at a serializer level by using
530
+ `default_format :json`, or configure it globally from your base serializer:
531
+
532
+ ```ruby
533
+ class BaseSerializer < JsonSerializer
534
+ default_format :json
535
+ end
536
+ ```
537
+
538
+ This will change the default shortcuts (`render`, `one`, `one_if`, and `many`),
539
+ so that the serializer writes directly to JSON instead of returning a Hash.
540
+
541
+ Even when using this mode, you can still use rendered values inside arrays,
542
+ hashes, and other serializers, thanks to [the `raw_json` extensions][raw_json].
543
+
544
+ <details>
545
+ <summary>Example Output</summary>
546
+
547
+ ```json
548
+ {
549
+ "name": "Abraxas",
550
+ "genres": [
551
+ "Pyschodelic Rock",
552
+ "Blues Rock",
553
+ "Jazz Fusion",
554
+ "Latin Rock"
555
+ ],
556
+ "release": "September 23, 1970",
557
+ "songs": [
558
+ {
559
+ "track": 1,
560
+ "name": "Sing Winds, Crying Beasts",
561
+ "composers": [
562
+ "Michael Carabello"
563
+ ]
564
+ },
565
+ {
566
+ "track": 2,
567
+ "name": "Black Magic Woman / Gypsy Queen",
568
+ "composers": [
569
+ "Peter Green",
570
+ "Gábor Szabó"
571
+ ]
572
+ },
573
+ {
574
+ "track": 3,
575
+ "name": "Oye como va",
576
+ "composers": [
577
+ "Tito Puente"
578
+ ]
579
+ },
580
+ {
581
+ "track": 4,
582
+ "name": "Incident at Neshabur",
583
+ "composers": [
584
+ "Alberto Gianquinto",
585
+ "Carlos Santana"
586
+ ]
587
+ },
588
+ {
589
+ "track": 5,
590
+ "name": "Se acabó",
591
+ "composers": [
592
+ "José Areas"
593
+ ]
594
+ },
595
+ {
596
+ "track": 6,
597
+ "name": "Mother's Daughter",
598
+ "composers": [
599
+ "Gregg Rolie"
600
+ ]
601
+ },
602
+ {
603
+ "track": 7,
604
+ "name": "Samba pa ti",
605
+ "composers": [
606
+ "Santana"
607
+ ]
608
+ },
609
+ {
610
+ "track": 8,
611
+ "name": "Hope You're Feeling Better",
612
+ "composers": [
613
+ "Rolie"
614
+ ]
615
+ },
616
+ {
617
+ "track": 9,
618
+ "name": "El Nicoya",
619
+ "composers": [
620
+ "Areas"
621
+ ]
622
+ }
623
+ ]
624
+ }
625
+ ```
626
+ </details>
627
+
628
+ ## Design 📐
629
+
630
+ Unlike `ActiveModel::Serializer`, which builds a Hash that then gets encoded to
631
+ JSON, this implementation can use `Oj::StringWriter` to write JSON directly,
632
+ greatly reducing the overhead of allocating and garbage collecting the hashes.
633
+
634
+ It also allocates a single instance per serializer class, which makes it easy
635
+ to use, while keeping memory usage under control.
636
+
637
+ The internal design is simple and extensible, and because the library is written
638
+ in Ruby, creating new serialization strategies requires very little code.
639
+ Please open a [Discussion] if you need help 😃
640
+
641
+ ### Comparison with other libraries
642
+
643
+ `ActiveModel::Serializer` instantiates one serializer object per item to be serialized.
644
+
645
+ Other libraries such as [`blueprinter`][blueprinter] [`jsonapi-serializer`][jsonapi]
646
+ evaluate serializers in the context of a `class` instead of an `instance` of a class.
647
+ The downside is that you can't use instance methods or local memoization, and any
648
+ mixins must be applied to the class itself.
649
+
650
+ [`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.
651
+
652
+ `JsonSerializer` 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.
653
+
654
+ Follow [this discussion][raw_json] to find out more about [the `raw_json` extensions][raw_json] that made this high level of interoperability possible.
655
+
656
+ As a result, migrating from `active_model_serializers` is relatively
657
+ straightforward because instance methods, inheritance, and mixins work as usual.
658
+
659
+ ### Benchmarks 📊
660
+
661
+ This library includes some [benchmarks] to compare performance with similar libraries.
662
+
663
+ See [this pull request](https://github.com/ElMassimo/oj_serializers/pull/9) for a quick comparison,
664
+ or check the CI to see the latest results.
665
+
666
+ ### Migrating from other libraries
667
+
668
+ Please refer to the [migration guide] for a full discussion of the compatibility
669
+ modes available to make it easier to migrate from `active_model_serializers` and
670
+ similar libraries.
671
+
672
+ ## Formatting 📏
673
+
674
+ Even though most of the examples above use a single-line style to be succint, I highly recommend writing one attribute per line, sorting them alphabetically (most editors can do it for you), and [always using a trailing comma][trailing_commas].
675
+
676
+ ```ruby
677
+ class AlbumSerializer < JsonSerializer
678
+ attributes(
679
+ :genres,
680
+ :name,
681
+ :release_date,
682
+ )
683
+ end
684
+ ```
685
+
686
+ It will make things clearer, minimize the amount of git conflicts, and keep the history a lot cleaner and more meaningful when using `git blame`.
687
+
688
+ ## Special Thanks 🙏
689
+
690
+ This library wouldn't be possible without the wonderful and performant [`oj`](https://github.com/ohler55/oj) library. Thanks [Peter](https://github.com/ohler55)! 😃
691
+
692
+ Also, thanks to the libraries that inspired this one:
693
+
694
+ - [`active_model_serializers`][ams]: For the DSL
695
+ - [`panko-serializer`][panko]: For validating that using `Oj::StringWriter` was indeed fast
696
+
697
+ ## License
698
+
699
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).