oj_serializers 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +14 -3
- data/lib/oj_serializers/serializer.rb +149 -119
- data/lib/oj_serializers/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8fd87262444d89cfbbe11a1b6ae6201de6d71ed996edcd8a4a9ae224320ad7c
|
4
|
+
data.tar.gz: bfe507b6e02c01087d99381ab8ef971547e0062085eb4bfb9c9551ceb2796c18
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 060b791a7d8c2f204adab9313873b950b89eb1a80400b26355b1382ab6e08d0efa3e48efeef1ececc96cb8f42729f8f4cea50b37d579ab1acc333b393b060c0d
|
7
|
+
data.tar.gz: a7ea8370d3155f8bfd99f9d1cffe67fe05f896930277a0e69a81b3e4eda2b0088a8901730ec2fb910e564dcf3f29ea3200f5cc70d8321dd1892b72c9f9b75bf4
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## Oj Serializers 2.0.1 (2023-04-02)
|
2
|
+
|
3
|
+
### Features ✨
|
4
|
+
|
5
|
+
- [Automatically mark `id` as an identifier (rendered first)](https://github.com/ElMassimo/oj_serializers/commit/c4c6de7)
|
6
|
+
- [Fail on typos in attribute and association options](https://github.com/ElMassimo/oj_serializers/commit/afd80ac)
|
7
|
+
|
8
|
+
### Fixes 🐞
|
9
|
+
|
10
|
+
- [Aliased attributes should be sorted by the output key](https://github.com/ElMassimo/oj_serializers/commit/fc6f4c1)
|
11
|
+
|
1
12
|
## [Oj Serializers 2.0.0 (2023-03-27)](https://github.com/ElMassimo/oj_serializers/pull/9)
|
2
13
|
|
3
14
|
### Features ✨
|
data/README.md
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
Oj Serializers
|
3
3
|
<p align="center">
|
4
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://inch-ci.org/github/ElMassimo/oj_serializers"><img alt="Inline docs" src="https://inch-ci.org/github/ElMassimo/oj_serializers.svg"/></a>
|
6
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>
|
7
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>
|
8
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>
|
@@ -28,6 +27,8 @@ Faster JSON serializers for Ruby, built on top of the powerful [`oj`][oj] librar
|
|
28
27
|
[render dsl]: https://github.com/ElMassimo/oj_serializers#render-dsl-
|
29
28
|
[sorbet]: https://sorbet.org/
|
30
29
|
[Discussion]: https://github.com/ElMassimo/oj_serializers/discussions
|
30
|
+
[TypeScript]: https://www.typescriptlang.org/
|
31
|
+
[types_from_serializers]: https://github.com/ElMassimo/types_from_serializers
|
31
32
|
|
32
33
|
## Why? 🤔
|
33
34
|
|
@@ -45,6 +46,7 @@ Learn more about [how this library achieves its performance][design].
|
|
45
46
|
- Support for `has_one` and `has_many`, compose with `flat_one`
|
46
47
|
- Useful development checks to avoid typos and mistakes
|
47
48
|
- Integrates nicely with Rails controllers
|
49
|
+
- [Generate TypeScript interfaces automatically][types_from_serializers]
|
48
50
|
|
49
51
|
## Installation 💿
|
50
52
|
|
@@ -67,8 +69,7 @@ attributes should be serialized.
|
|
67
69
|
class AlbumSerializer < Oj::Serializer
|
68
70
|
attributes :name, :genres
|
69
71
|
|
70
|
-
|
71
|
-
def release
|
72
|
+
attribute :release do
|
72
73
|
album.release_date.strftime('%B %d, %Y')
|
73
74
|
end
|
74
75
|
|
@@ -367,6 +368,16 @@ One slight variation that might make it easier to maintain in the long term is
|
|
367
368
|
to use a separate singleton service to provide the url helpers and options, and
|
368
369
|
make it available as `urls`.
|
369
370
|
|
371
|
+
### Generating TypeScript automatically 🤖
|
372
|
+
|
373
|
+
It's easy for the backend and the frontend to become out of sync. Traditionally, preventing bugs requires writing extensive integration tests.
|
374
|
+
|
375
|
+
[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.
|
376
|
+
|
377
|
+
[`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.
|
378
|
+
|
379
|
+
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.
|
380
|
+
|
370
381
|
### Memoization & local state
|
371
382
|
|
372
383
|
Serializers are designed to be stateless so that an instanced can be reused, but
|
@@ -19,7 +19,22 @@ require 'oj_serializers/json_value'
|
|
19
19
|
class OjSerializers::Serializer
|
20
20
|
# Public: Used to validate incorrect memoization during development. Users of
|
21
21
|
# this library might add additional options as needed.
|
22
|
-
ALLOWED_INSTANCE_VARIABLES = %w[
|
22
|
+
ALLOWED_INSTANCE_VARIABLES = %w[
|
23
|
+
memo
|
24
|
+
object
|
25
|
+
options
|
26
|
+
_routes
|
27
|
+
]
|
28
|
+
|
29
|
+
KNOWN_ATTRIBUTE_OPTIONS = %i[
|
30
|
+
attribute
|
31
|
+
association
|
32
|
+
identifier
|
33
|
+
if
|
34
|
+
optional
|
35
|
+
type
|
36
|
+
serializer
|
37
|
+
].to_set
|
23
38
|
|
24
39
|
CACHE = (defined?(Rails) && Rails.cache) ||
|
25
40
|
(defined?(ActiveSupport::Cache::MemoryStore) ? ActiveSupport::Cache::MemoryStore.new : OjSerializers::Memo.new)
|
@@ -85,30 +100,40 @@ protected
|
|
85
100
|
class << self
|
86
101
|
# Public: Allows the user to specify `default_format :json`, as a simple
|
87
102
|
# way to ensure that `.one` and `.many` work as in Version 1.
|
88
|
-
|
89
|
-
|
103
|
+
#
|
104
|
+
# This setting is inherited from parent classes.
|
105
|
+
def default_format(format)
|
106
|
+
define_singleton_method(:_default_format) { format }
|
90
107
|
define_serialization_shortcuts
|
91
108
|
end
|
92
109
|
|
93
|
-
# Public: Allows to sort fields by name instead
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
110
|
+
# Public: Allows to sort fields by name instead of by definition order, or
|
111
|
+
# pass a Proc to apply a custom order.
|
112
|
+
#
|
113
|
+
# This setting is inherited from parent classes.
|
114
|
+
def sort_attributes_by(strategy)
|
115
|
+
case strategy
|
116
|
+
when :name, :definition, Proc
|
117
|
+
define_singleton_method(:_sort_attributes_by) { strategy }
|
98
118
|
else
|
99
|
-
raise ArgumentError, "Unknown sorting option: #{
|
119
|
+
raise ArgumentError, "Unknown sorting option: #{strategy.inspect}"
|
100
120
|
end
|
101
121
|
end
|
102
122
|
|
103
|
-
# Public: Allows to
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
123
|
+
# Public: Allows to transform the JSON keys to camelCase, or pass a Proc
|
124
|
+
# to apply a custom transformation.
|
125
|
+
#
|
126
|
+
# This setting is inherited from parent classes.
|
127
|
+
def transform_keys(strategy = nil, &block)
|
128
|
+
transformer = case (strategy ||= block)
|
129
|
+
when :camelize, :camel_case then ->(key) { key.camelize(:lower) }
|
130
|
+
when :none then nil
|
131
|
+
when Symbol then strategy.to_proc
|
132
|
+
when Proc then strategy
|
109
133
|
else
|
110
|
-
raise(ArgumentError, "Expected transform_keys to be callable, got: #{
|
134
|
+
raise(ArgumentError, "Expected transform_keys to be callable, got: #{strategy.inspect}")
|
111
135
|
end
|
136
|
+
define_singleton_method(:_transform_keys) { transformer }
|
112
137
|
end
|
113
138
|
|
114
139
|
# Public: Creates an alias for the internal object.
|
@@ -318,15 +343,15 @@ protected
|
|
318
343
|
|
319
344
|
# Public: Specify a collection of objects that should be serialized using
|
320
345
|
# the specified serializer.
|
321
|
-
def has_many(name, serializer:,
|
346
|
+
def has_many(name, serializer:, **options, &block)
|
322
347
|
define_method(name, &block) if block
|
323
|
-
add_attribute(name, association: :many,
|
348
|
+
add_attribute(name, association: :many, serializer: serializer, **options)
|
324
349
|
end
|
325
350
|
|
326
351
|
# Public: Specify an object that should be serialized using the serializer.
|
327
|
-
def has_one(name, serializer:,
|
352
|
+
def has_one(name, serializer:, **options, &block)
|
328
353
|
define_method(name, &block) if block
|
329
|
-
add_attribute(name, association: :one,
|
354
|
+
add_attribute(name, association: :one, serializer: serializer, **options)
|
330
355
|
end
|
331
356
|
# Alias: From a serializer perspective, the association type does not matter.
|
332
357
|
alias_method :belongs_to, :has_one
|
@@ -340,9 +365,8 @@ protected
|
|
340
365
|
|
341
366
|
# Public: Specify which attributes are going to be obtained from indexing
|
342
367
|
# the object.
|
343
|
-
def hash_attributes(*
|
344
|
-
|
345
|
-
method_names.each { |name| _attributes[name] = options }
|
368
|
+
def hash_attributes(*attr_names, **options)
|
369
|
+
attributes(*attr_names, **options, attribute: :hash)
|
346
370
|
end
|
347
371
|
|
348
372
|
# Public: Specify which attributes are going to be obtained from indexing
|
@@ -351,31 +375,31 @@ protected
|
|
351
375
|
# Automatically renames `_id` to `id` for Mongoid models.
|
352
376
|
#
|
353
377
|
# See ./benchmarks/document_benchmark.rb
|
354
|
-
def mongo_attributes(*
|
355
|
-
identifier(:_id, as: :id, attribute: :mongoid, **options) if
|
356
|
-
attributes(*
|
378
|
+
def mongo_attributes(*attr_names, **options)
|
379
|
+
identifier(:_id, as: :id, attribute: :mongoid, **options) if attr_names.delete(:id)
|
380
|
+
attributes(*attr_names, **options, attribute: :mongoid)
|
357
381
|
end
|
358
382
|
|
359
383
|
# Public: Specify which attributes are going to be obtained by calling a
|
360
384
|
# method in the object.
|
361
|
-
def attributes(*
|
385
|
+
def attributes(*attr_names, **methods_with_options)
|
362
386
|
attr_options = methods_with_options.extract!(:if, :as, :attribute)
|
363
387
|
attr_options[:attribute] ||= :method
|
364
388
|
|
365
|
-
|
366
|
-
add_attribute(
|
389
|
+
attr_names.each do |attr_name|
|
390
|
+
add_attribute(attr_name, **attr_options)
|
367
391
|
end
|
368
392
|
|
369
|
-
methods_with_options.each do |
|
393
|
+
methods_with_options.each do |attr_name, options|
|
370
394
|
options = { as: options } if options.is_a?(Symbol)
|
371
|
-
add_attribute(
|
395
|
+
add_attribute(attr_name, **options)
|
372
396
|
end
|
373
397
|
end
|
374
398
|
|
375
399
|
# Public: Specify which attributes are going to be obtained by calling a
|
376
400
|
# method in the serializer.
|
377
|
-
def serializer_attributes(*
|
378
|
-
attributes(*
|
401
|
+
def serializer_attributes(*attr_names, **options)
|
402
|
+
attributes(*attr_names, **options, attribute: :serializer)
|
379
403
|
end
|
380
404
|
|
381
405
|
# Syntax Sugar: Allows to use it before a method name.
|
@@ -389,7 +413,7 @@ protected
|
|
389
413
|
options[:attribute] = :serializer
|
390
414
|
if name
|
391
415
|
define_method(name, &block) if block
|
392
|
-
add_attribute(name, options)
|
416
|
+
add_attribute(name, **options)
|
393
417
|
else
|
394
418
|
@_current_attribute_options = options
|
395
419
|
end
|
@@ -401,7 +425,7 @@ protected
|
|
401
425
|
def method_added(name)
|
402
426
|
super(name)
|
403
427
|
if @_current_attribute_options
|
404
|
-
add_attribute(name,
|
428
|
+
add_attribute(name, **@_current_attribute_options)
|
405
429
|
@_current_attribute_options = nil
|
406
430
|
end
|
407
431
|
end
|
@@ -410,45 +434,34 @@ protected
|
|
410
434
|
# calling a method in the serializer, or using `read_attribute_for_serialization`.
|
411
435
|
#
|
412
436
|
# NOTE: Prefer to use `attributes` or `serializer_attributes` explicitly.
|
413
|
-
def ams_attributes(*
|
414
|
-
|
415
|
-
define_method(
|
437
|
+
def ams_attributes(*attr_names, **options)
|
438
|
+
attr_names.each do |attr_name|
|
439
|
+
define_method(attr_name) { @object.read_attribute_for_serialization(attr_name) } unless method_defined?(attr_name)
|
416
440
|
end
|
417
|
-
attributes(*
|
441
|
+
attributes(*attr_names, **options, attribute: :serializer)
|
418
442
|
end
|
419
443
|
|
420
|
-
|
421
|
-
def _default_format
|
422
|
-
@_default_format = superclass.try(:_default_format) || :hash unless defined?(@_default_format)
|
423
|
-
@_default_format
|
424
|
-
end
|
444
|
+
private
|
425
445
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
def _sort_attributes_by
|
430
|
-
@_sort_attributes_by = superclass.try(:_sort_attributes_by) unless defined?(@_sort_attributes_by)
|
431
|
-
@_sort_attributes_by
|
432
|
-
end
|
446
|
+
def add_attribute(value_from, root: nil, as: nil, **options)
|
447
|
+
# Because it's so common, automatically mark id as an identifier.
|
448
|
+
options[:identifier] = true if value_from == :id && !options.key?(:identifier)
|
433
449
|
|
434
|
-
|
435
|
-
|
436
|
-
# This setting is inherited from parent classes.
|
437
|
-
def _transform_keys
|
438
|
-
@_transform_keys = superclass.try(:_transform_keys) unless defined?(@_transform_keys)
|
439
|
-
@_transform_keys
|
440
|
-
end
|
450
|
+
# Hash attributes could be numbers or symbols.
|
451
|
+
value_from = value_from.to_s unless options[:attribute] == :hash
|
441
452
|
|
442
|
-
|
453
|
+
# Obtain the JSON key to use for the attribute.
|
454
|
+
key = (root || as || value_from).to_s
|
443
455
|
|
444
|
-
|
445
|
-
_attributes
|
446
|
-
end
|
456
|
+
# Should be able to add "duplicate" flat associations.
|
457
|
+
key += _attributes.count.to_s if options[:association] == :flat
|
447
458
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
459
|
+
# Check for typos in options.
|
460
|
+
if DEV_MODE && (option, = options.find { |option, _value| !KNOWN_ATTRIBUTE_OPTIONS.include?(option) })
|
461
|
+
raise ArgumentError, "Unknown option #{option.inspect} for attribute #{value_from.inspect} in #{name}. Please check for typos."
|
462
|
+
end
|
463
|
+
|
464
|
+
_attributes[key.freeze] = { value_from: value_from, **options }.freeze
|
452
465
|
end
|
453
466
|
|
454
467
|
# Internal: Whether the object should be serialized as a collection.
|
@@ -464,19 +477,19 @@ protected
|
|
464
477
|
#
|
465
478
|
# As a result, the performance is the same as writing the most efficient
|
466
479
|
# code by hand.
|
467
|
-
def code_to_write_to_json
|
480
|
+
def code_to_write_to_json(attributes)
|
468
481
|
<<~WRITE_TO_JSON
|
469
482
|
# Public: Writes this serializer content to a provided Oj::StringWriter.
|
470
483
|
def write_to_json(writer, item, options = nil)
|
471
484
|
@object = item
|
472
485
|
@options = options
|
473
486
|
@memo.clear if defined?(@memo)
|
474
|
-
#{
|
475
|
-
|
487
|
+
#{ attributes.map { |key, options|
|
488
|
+
code_to_write_conditionally(options) {
|
476
489
|
if options[:association]
|
477
|
-
code_to_write_association(
|
490
|
+
code_to_write_association(key, options)
|
478
491
|
else
|
479
|
-
code_to_write_attribute(
|
492
|
+
code_to_write_attribute(key, options)
|
480
493
|
end
|
481
494
|
}
|
482
495
|
}.join("\n ") }#{code_to_rescue_no_method if DEV_MODE}
|
@@ -490,7 +503,7 @@ protected
|
|
490
503
|
#
|
491
504
|
# As a result, the performance is the same as writing the most efficient
|
492
505
|
# code by hand.
|
493
|
-
def code_to_render_as_hash
|
506
|
+
def code_to_render_as_hash(attributes)
|
494
507
|
<<~RENDER_AS_HASH
|
495
508
|
# Public: Writes this serializer content to a Hash.
|
496
509
|
def render_as_hash(item, options = nil)
|
@@ -498,12 +511,12 @@ protected
|
|
498
511
|
@options = options
|
499
512
|
@memo.clear if defined?(@memo)
|
500
513
|
{
|
501
|
-
#{
|
502
|
-
code_to_render_conditionally(
|
514
|
+
#{attributes.map { |key, options|
|
515
|
+
code_to_render_conditionally(options) {
|
503
516
|
if options[:association]
|
504
|
-
code_to_render_association(
|
517
|
+
code_to_render_association(key, options)
|
505
518
|
else
|
506
|
-
code_to_render_attribute(
|
519
|
+
code_to_render_attribute(key, options)
|
507
520
|
end
|
508
521
|
}
|
509
522
|
}.join(",\n ")}
|
@@ -518,7 +531,7 @@ protected
|
|
518
531
|
rescue NoMethodError => e
|
519
532
|
key = e.name.to_s.inspect
|
520
533
|
message = if respond_to?(e.name)
|
521
|
-
raise e, "Perhaps you meant to call \#{key} in \#{self.class} instead?\nTry using `
|
534
|
+
raise e, "Perhaps you meant to call \#{key} in \#{self.class} instead?\nTry using `attribute :\#{key} do` or `attribute def \#{key}`.\n\#{e.message}"
|
522
535
|
elsif @object.respond_to?(e.name)
|
523
536
|
raise e, "Perhaps you meant to call \#{key} in \#{@object.class} instead?\nTry using `attributes :\#{key}`.\n\#{e.message}"
|
524
537
|
else
|
@@ -531,8 +544,9 @@ protected
|
|
531
544
|
|
532
545
|
# Internal: Detects any include methods defined in the serializer, or defines
|
533
546
|
# one by using the lambda passed in the `if` option, if any.
|
534
|
-
def check_conditional_method(
|
535
|
-
|
547
|
+
def check_conditional_method(options)
|
548
|
+
value_from = options.fetch(:value_from)
|
549
|
+
include_method_name = "include_#{value_from}#{'?' unless value_from.to_s.ends_with?('?')}"
|
536
550
|
if render_if = options[:if]
|
537
551
|
if render_if.is_a?(Symbol)
|
538
552
|
alias_method(include_method_name, render_if)
|
@@ -548,8 +562,8 @@ protected
|
|
548
562
|
#
|
549
563
|
# NOTE: Detects any include methods defined in the serializer, or defines
|
550
564
|
# one by using the lambda passed in the `if` option, if any.
|
551
|
-
def
|
552
|
-
if (include_method_name = check_conditional_method(
|
565
|
+
def code_to_write_conditionally(options)
|
566
|
+
if (include_method_name = check_conditional_method(options))
|
553
567
|
"if #{include_method_name};#{yield};end\n"
|
554
568
|
else
|
555
569
|
yield
|
@@ -557,50 +571,52 @@ protected
|
|
557
571
|
end
|
558
572
|
|
559
573
|
# Internal: Returns the code for the association method.
|
560
|
-
def code_to_write_attribute(
|
561
|
-
|
574
|
+
def code_to_write_attribute(key, options)
|
575
|
+
value_from = options.fetch(:value_from)
|
562
576
|
|
563
|
-
case strategy = options.fetch(:attribute)
|
577
|
+
value = case (strategy = options.fetch(:attribute))
|
564
578
|
when :serializer
|
565
579
|
# Obtains the value by calling a method in the serializer.
|
566
|
-
|
580
|
+
value_from
|
567
581
|
when :method
|
568
582
|
# Obtains the value by calling a method in the object, and writes it.
|
569
|
-
"
|
583
|
+
"@object.#{value_from}"
|
570
584
|
when :hash
|
571
585
|
# Writes a Hash value to JSON, works with String or Symbol keys.
|
572
|
-
"
|
586
|
+
"@object[#{value_from.inspect}]"
|
573
587
|
when :mongoid
|
574
588
|
# Writes an Mongoid attribute to JSON, this is the fastest strategy.
|
575
|
-
"
|
589
|
+
"@object.attributes['#{value_from}']"
|
576
590
|
else
|
577
591
|
raise ArgumentError, "Unknown attribute strategy: #{strategy.inspect}"
|
578
592
|
end
|
593
|
+
|
594
|
+
"writer.push_value(#{value}, #{key.inspect})"
|
579
595
|
end
|
580
596
|
|
581
597
|
# Internal: Returns the code for the association method.
|
582
|
-
def code_to_write_association(
|
598
|
+
def code_to_write_association(key, options)
|
583
599
|
# Use a serializer method if defined, else call the association in the object.
|
584
|
-
|
585
|
-
|
600
|
+
value_from = options.fetch(:value_from)
|
601
|
+
value = method_defined?(value_from) ? value_from : "@object.#{value_from}"
|
586
602
|
serializer_class = options.fetch(:serializer)
|
587
603
|
|
588
604
|
case type = options.fetch(:association)
|
589
605
|
when :one
|
590
606
|
<<~WRITE_ONE
|
591
|
-
if
|
607
|
+
if __value = #{value}
|
592
608
|
writer.push_key('#{key}')
|
593
|
-
#{serializer_class}.write_one(writer,
|
609
|
+
#{serializer_class}.write_one(writer, __value)
|
594
610
|
end
|
595
611
|
WRITE_ONE
|
596
612
|
when :many
|
597
613
|
<<~WRITE_MANY
|
598
614
|
writer.push_key('#{key}')
|
599
|
-
#{serializer_class}.write_many(writer, #{
|
615
|
+
#{serializer_class}.write_many(writer, #{value})
|
600
616
|
WRITE_MANY
|
601
617
|
when :flat
|
602
618
|
<<~WRITE_FLAT
|
603
|
-
#{serializer_class}.write_to_json(writer, #{
|
619
|
+
#{serializer_class}.write_to_json(writer, #{value})
|
604
620
|
WRITE_FLAT
|
605
621
|
else
|
606
622
|
raise ArgumentError, "Unknown association type: #{type.inspect}"
|
@@ -612,8 +628,8 @@ protected
|
|
612
628
|
#
|
613
629
|
# NOTE: Detects any include methods defined in the serializer, or defines
|
614
630
|
# one by using the lambda passed in the `if` option, if any.
|
615
|
-
def code_to_render_conditionally(
|
616
|
-
if (include_method_name = check_conditional_method(
|
631
|
+
def code_to_render_conditionally(options)
|
632
|
+
if (include_method_name = check_conditional_method(options))
|
617
633
|
"**(#{include_method_name} ? {#{yield}} : {})"
|
618
634
|
else
|
619
635
|
yield
|
@@ -621,36 +637,39 @@ protected
|
|
621
637
|
end
|
622
638
|
|
623
639
|
# Internal: Returns the code for the attribute method.
|
624
|
-
def code_to_render_attribute(
|
625
|
-
|
626
|
-
|
640
|
+
def code_to_render_attribute(key, options)
|
641
|
+
value_from = options.fetch(:value_from)
|
642
|
+
|
643
|
+
value = case (strategy = options.fetch(:attribute))
|
627
644
|
when :serializer
|
628
|
-
|
645
|
+
value_from
|
629
646
|
when :method
|
630
|
-
"
|
647
|
+
"@object.#{value_from}"
|
631
648
|
when :hash
|
632
|
-
"
|
649
|
+
"@object[#{value_from.inspect}]"
|
633
650
|
when :mongoid
|
634
|
-
"
|
651
|
+
"@object.attributes['#{value_from}']"
|
635
652
|
else
|
636
653
|
raise ArgumentError, "Unknown attribute strategy: #{strategy.inspect}"
|
637
654
|
end
|
655
|
+
|
656
|
+
"#{key}: #{value}"
|
638
657
|
end
|
639
658
|
|
640
659
|
# Internal: Returns the code for the association method.
|
641
|
-
def code_to_render_association(
|
660
|
+
def code_to_render_association(key, options)
|
642
661
|
# Use a serializer method if defined, else call the association in the object.
|
643
|
-
|
644
|
-
|
662
|
+
value_from = options.fetch(:value_from)
|
663
|
+
value = method_defined?(value_from) ? value_from : "@object.#{value_from}"
|
645
664
|
serializer_class = options.fetch(:serializer)
|
646
665
|
|
647
666
|
case type = options.fetch(:association)
|
648
667
|
when :one
|
649
|
-
"#{key}: (
|
668
|
+
"#{key}: (__value = #{value}) ? #{serializer_class}.one_as_hash(__value) : nil"
|
650
669
|
when :many
|
651
|
-
"#{key}: #{serializer_class}.many_as_hash(#{
|
670
|
+
"#{key}: #{serializer_class}.many_as_hash(#{value})"
|
652
671
|
when :flat
|
653
|
-
"**#{serializer_class}.one_as_hash(#{
|
672
|
+
"**#{serializer_class}.one_as_hash(#{value})"
|
654
673
|
else
|
655
674
|
raise ArgumentError, "Unknown association type: #{type.inspect}"
|
656
675
|
end
|
@@ -671,25 +690,36 @@ protected
|
|
671
690
|
@instance_key ||= begin
|
672
691
|
# We take advantage of the fact that this method will always be called
|
673
692
|
# before instantiating a serializer, to apply last minute adjustments.
|
674
|
-
|
693
|
+
prepare_serializer
|
675
694
|
"#{name.underscore}_instance_#{object_id}".to_sym
|
676
695
|
end
|
677
696
|
end
|
678
697
|
|
679
698
|
# Internal: Generates write_to_json and render_as_hash methods optimized for
|
680
699
|
# the specified configuration.
|
681
|
-
def
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
700
|
+
def prepare_serializer
|
701
|
+
attributes = prepare_attributes
|
702
|
+
class_eval(code_to_write_to_json(attributes))
|
703
|
+
class_eval(code_to_render_as_hash(attributes))
|
704
|
+
end
|
705
|
+
|
706
|
+
# Internal: Returns attributes sorted and with keys transformed using
|
707
|
+
# the specified strategies.
|
708
|
+
def prepare_attributes(transform_keys: try(:_transform_keys), sort_by: try(:_sort_attributes_by))
|
709
|
+
attributes = _attributes
|
710
|
+
attributes = attributes.transform_keys(&transform_keys) if transform_keys
|
711
|
+
|
712
|
+
if sort_by == :name
|
713
|
+
sort_by = ->(name, options, _) { options[:identifier] ? "__#{name}" : name }
|
714
|
+
elsif !sort_by || sort_by == :definition
|
715
|
+
sort_by = ->(name, options, index) { options[:identifier] ? "__#{name}" : "zzz#{index}" }
|
686
716
|
end
|
687
|
-
|
688
|
-
|
717
|
+
|
718
|
+
attributes.sort_by.with_index { |(name, options), index| sort_by.call(name, options, index) }.to_h
|
689
719
|
end
|
690
720
|
end
|
691
721
|
|
692
|
-
|
722
|
+
default_format :hash
|
693
723
|
end
|
694
724
|
|
695
725
|
Oj::Serializer = OjSerializers::Serializer unless defined?(Oj::Serializer)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oj_serializers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maximo Mussini
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-04-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oj
|