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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +61 -0
- data/README.md +699 -0
- data/lib/json_serializers/compat.rb +66 -0
- data/lib/json_serializers/controller_serialization.rb +32 -0
- data/lib/json_serializers/json_string_encoder.rb +41 -0
- data/lib/json_serializers/json_value.rb +34 -0
- data/lib/json_serializers/memo.rb +20 -0
- data/lib/json_serializers/serializer.rb +726 -0
- data/lib/json_serializers/setup.rb +31 -0
- data/lib/json_serializers/sugar.rb +17 -0
- data/lib/json_serializers/version.rb +5 -0
- data/lib/json_serializers.rb +6 -0
- metadata +61 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model_serializers'
|
4
|
+
|
5
|
+
# Extensions: To ensure JsonStringEncoder can process ActiveModel::Serializer
|
6
|
+
# as well.
|
7
|
+
class ActiveModel::Serializer
|
8
|
+
# JsonStringEncoder: Used internally to write a single object to JSON.
|
9
|
+
def self.one(object, options = nil)
|
10
|
+
new(object, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
# JsonStringEncoder: Used internally to write an array of objects to JSON.
|
14
|
+
def self.many(array, options = nil)
|
15
|
+
array.map { |object| new(object, options) }
|
16
|
+
end
|
17
|
+
|
18
|
+
# JsonSerializer: Used internally to write a single object association in :hash mode.
|
19
|
+
#
|
20
|
+
# Returns nothing.
|
21
|
+
def self.one_as_hash(object)
|
22
|
+
new(object)
|
23
|
+
end
|
24
|
+
|
25
|
+
# JsonSerializer: Used internally to write an association in :hash mode.
|
26
|
+
#
|
27
|
+
# Returns nothing.
|
28
|
+
def self.many_as_hash(array)
|
29
|
+
array.map { |object| new(object) }
|
30
|
+
end
|
31
|
+
|
32
|
+
# JsonSerializer: Used internally to write a single object association in :json mode.
|
33
|
+
#
|
34
|
+
# Returns nothing.
|
35
|
+
def self.write_one(writer, object)
|
36
|
+
writer.push_value(new(object))
|
37
|
+
end
|
38
|
+
|
39
|
+
# JsonSerializer: Used internally to write an association in :json mode.
|
40
|
+
#
|
41
|
+
# Returns nothing.
|
42
|
+
def self.write_many(writer, array)
|
43
|
+
writer.push_array
|
44
|
+
array.each do |object|
|
45
|
+
write_one(writer, object)
|
46
|
+
end
|
47
|
+
writer.pop
|
48
|
+
end
|
49
|
+
|
50
|
+
module JsonSerializerOptionsCompat
|
51
|
+
def add_attribute(value_from, key: nil, **options)
|
52
|
+
options[:as] ||= key if key
|
53
|
+
|
54
|
+
if (unless_proc = options.delete(:unless))
|
55
|
+
options[:if] = -> { !instance_exec(&unless_proc) }
|
56
|
+
end
|
57
|
+
|
58
|
+
super(value_from, **options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
require 'json_serializers'
|
64
|
+
require 'json_serializers/sugar'
|
65
|
+
|
66
|
+
JsonSerializer.singleton_class.prepend(ActiveModel::Serializer::JsonSerializerOptionsCompat)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json_serializers/json_string_encoder'
|
4
|
+
|
5
|
+
# Internal: Allows to pass JSON serializers as options in `render`.
|
6
|
+
module JsonSerializers::ControllerSerialization
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
include ActionController::Renderers
|
9
|
+
|
10
|
+
# Internal: Allows to use JsonSerializer as `serializer` and `each_serializer`
|
11
|
+
# as with ActiveModelSerializers.
|
12
|
+
#
|
13
|
+
# render json: items, each_serializer: ItemSerializer
|
14
|
+
# render json: item, serializer: ItemSerializer
|
15
|
+
#
|
16
|
+
# NOTE: In practice, it should be preferable to simply do:
|
17
|
+
#
|
18
|
+
# render json: ItemSerializer.many(items)
|
19
|
+
# render json: ItemSerializer.one(item)
|
20
|
+
#
|
21
|
+
# which is more performant.
|
22
|
+
%i[_render_option_json _render_with_renderer_json].each do |renderer_method|
|
23
|
+
define_method renderer_method do |resource, options = {}|
|
24
|
+
serializer_class = options[:serializer] || options[:each_serializer]
|
25
|
+
if serializer_class && serializer_class < JsonSerializers::Serializer
|
26
|
+
super(JsonSerializers::JsonStringEncoder.encode_to_json(resource, **options), options.except(:root, :serializer, :each_serializer))
|
27
|
+
else
|
28
|
+
super(resource, **options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Public: Contains utility functions to render objects to JSON.
|
4
|
+
#
|
5
|
+
# Useful to instantiate a single `JsonWriter` when rendering new serializers.
|
6
|
+
module JsonSerializers::JsonStringEncoder
|
7
|
+
class << self
|
8
|
+
# Public: Allows to use JsonSerializer in `serializer` and `each_serializer`
|
9
|
+
# as with ActiveModelSerializers.
|
10
|
+
# render json: items, each_serializer: ItemSerializer
|
11
|
+
# render json: item, serializer: ItemSerializer
|
12
|
+
#
|
13
|
+
# NOTE: Unlike the default encoder, this one will use the `root` option
|
14
|
+
# regardless of whether a serializer is specified or not.
|
15
|
+
#
|
16
|
+
# Returns a JSON string.
|
17
|
+
def encode_to_json(object, root: nil, serializer: nil, each_serializer: nil, **options)
|
18
|
+
result = if serializer
|
19
|
+
serializer.one(object, options)
|
20
|
+
elsif each_serializer
|
21
|
+
each_serializer.many(object, options)
|
22
|
+
elsif object.is_a?(String)
|
23
|
+
JsonSerializers::JsonValue.new(object)
|
24
|
+
else
|
25
|
+
object
|
26
|
+
end
|
27
|
+
Oj.dump(root ? { root => result } : result)
|
28
|
+
end
|
29
|
+
|
30
|
+
if JsonSerializers::Serializer::DEV_MODE
|
31
|
+
alias actual_encode_to_json encode_to_json
|
32
|
+
# Internal: Allows to detect misusage of the options during development.
|
33
|
+
def encode_to_json(object, root: nil, serializer: nil, each_serializer: nil, **options)
|
34
|
+
raise ArgumentError, 'You must use `each_serializer` when serializing collections' if serializer && serializer < JsonSerializers::Serializer && object.respond_to?(:map)
|
35
|
+
raise ArgumentError, 'You must use `serializer` when serializing a single object' if each_serializer && each_serializer < JsonSerializers::Serializer && !object.respond_to?(:map)
|
36
|
+
|
37
|
+
actual_encode_to_json(object, root: root, serializer: serializer, each_serializer: each_serializer, **options)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Public: Allows to prevent double encoding an existing JSON string.
|
4
|
+
#
|
5
|
+
# NOTE: Oj's raw_json option means there's no performance overhead, as it would
|
6
|
+
# occur with the previous alternative of parsing the JSON string.
|
7
|
+
class JsonSerializers::JsonValue
|
8
|
+
# Public: Expects json to be a JSON-encoded string.
|
9
|
+
def initialize(json)
|
10
|
+
@json = json
|
11
|
+
end
|
12
|
+
|
13
|
+
# Public: Expects an Array of JSON-encoded strings and wraps them in a JSON array.
|
14
|
+
#
|
15
|
+
# Returns a JsonValue representing a JSON-encoded array.
|
16
|
+
def self.array(json_rows)
|
17
|
+
new("[#{json_rows.join(',')}]")
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: Return the internal json when using string interpolation.
|
21
|
+
def to_s
|
22
|
+
@json
|
23
|
+
end
|
24
|
+
|
25
|
+
# Internal: Used by Oj::Rails::Encoder because we use the `raw_json` option.
|
26
|
+
def raw_json(*)
|
27
|
+
@json
|
28
|
+
end
|
29
|
+
|
30
|
+
# Internal: Used by Oj::Rails::Encoder when found inside a Hash or Array.
|
31
|
+
def as_json(_options = nil)
|
32
|
+
self
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Internal: Provides a simple API on top of Hash for memoization purposes.
|
4
|
+
class JsonSerializers::Memo
|
5
|
+
def initialize
|
6
|
+
@cache = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
# Internal: Allows to clear the cache when binding the serializer to a
|
10
|
+
# different object.
|
11
|
+
def clear
|
12
|
+
@cache.clear
|
13
|
+
end
|
14
|
+
|
15
|
+
# Public: Allows to use a simple memoization pattern that also works for
|
16
|
+
# falsey values.
|
17
|
+
def fetch(key)
|
18
|
+
@cache.fetch(key) { @cache[key] = yield }
|
19
|
+
end
|
20
|
+
end
|