simple_jsonapi 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rubocop.yml +131 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +5 -0
- data/Jenkinsfile +92 -0
- data/LICENSE.txt +22 -0
- data/README.md +532 -0
- data/Rakefile +10 -0
- data/lib/simple_jsonapi.rb +112 -0
- data/lib/simple_jsonapi/definition/attribute.rb +45 -0
- data/lib/simple_jsonapi/definition/base.rb +50 -0
- data/lib/simple_jsonapi/definition/concerns/has_links_object.rb +36 -0
- data/lib/simple_jsonapi/definition/concerns/has_meta_object.rb +36 -0
- data/lib/simple_jsonapi/definition/error.rb +70 -0
- data/lib/simple_jsonapi/definition/error_source.rb +29 -0
- data/lib/simple_jsonapi/definition/link.rb +27 -0
- data/lib/simple_jsonapi/definition/meta.rb +27 -0
- data/lib/simple_jsonapi/definition/relationship.rb +60 -0
- data/lib/simple_jsonapi/definition/resource.rb +104 -0
- data/lib/simple_jsonapi/error_serializer.rb +76 -0
- data/lib/simple_jsonapi/errors/bad_request.rb +11 -0
- data/lib/simple_jsonapi/errors/exception_serializer.rb +6 -0
- data/lib/simple_jsonapi/errors/wrapped_error.rb +35 -0
- data/lib/simple_jsonapi/errors/wrapped_error_serializer.rb +35 -0
- data/lib/simple_jsonapi/helpers/exceptions.rb +39 -0
- data/lib/simple_jsonapi/helpers/serializer_inferrer.rb +136 -0
- data/lib/simple_jsonapi/helpers/serializer_methods.rb +36 -0
- data/lib/simple_jsonapi/node/attributes.rb +51 -0
- data/lib/simple_jsonapi/node/base.rb +91 -0
- data/lib/simple_jsonapi/node/data/collection.rb +25 -0
- data/lib/simple_jsonapi/node/data/singular.rb +26 -0
- data/lib/simple_jsonapi/node/document/base.rb +62 -0
- data/lib/simple_jsonapi/node/document/collection.rb +17 -0
- data/lib/simple_jsonapi/node/document/errors.rb +17 -0
- data/lib/simple_jsonapi/node/document/singular.rb +17 -0
- data/lib/simple_jsonapi/node/error.rb +55 -0
- data/lib/simple_jsonapi/node/error_source.rb +40 -0
- data/lib/simple_jsonapi/node/errors.rb +28 -0
- data/lib/simple_jsonapi/node/included.rb +45 -0
- data/lib/simple_jsonapi/node/object_links.rb +40 -0
- data/lib/simple_jsonapi/node/object_meta.rb +40 -0
- data/lib/simple_jsonapi/node/relationship.rb +79 -0
- data/lib/simple_jsonapi/node/relationship_data/base.rb +53 -0
- data/lib/simple_jsonapi/node/relationship_data/collection.rb +32 -0
- data/lib/simple_jsonapi/node/relationship_data/singular.rb +33 -0
- data/lib/simple_jsonapi/node/relationships.rb +60 -0
- data/lib/simple_jsonapi/node/resource/base.rb +21 -0
- data/lib/simple_jsonapi/node/resource/full.rb +49 -0
- data/lib/simple_jsonapi/node/resource/linkage.rb +25 -0
- data/lib/simple_jsonapi/parameters/fields_spec.rb +45 -0
- data/lib/simple_jsonapi/parameters/include_spec.rb +57 -0
- data/lib/simple_jsonapi/parameters/sort_spec.rb +107 -0
- data/lib/simple_jsonapi/serializer.rb +89 -0
- data/lib/simple_jsonapi/version.rb +3 -0
- data/simple_jsonapi.gemspec +29 -0
- data/test/errors/bad_request_test.rb +34 -0
- data/test/errors/error_serializer_test.rb +229 -0
- data/test/errors/exception_serializer_test.rb +25 -0
- data/test/errors/wrapped_error_serializer_test.rb +91 -0
- data/test/errors/wrapped_error_test.rb +44 -0
- data/test/parameters/fields_spec_test.rb +56 -0
- data/test/parameters/include_spec_test.rb +58 -0
- data/test/parameters/sort_spec_test.rb +65 -0
- data/test/resources/attributes_test.rb +109 -0
- data/test/resources/extras_test.rb +70 -0
- data/test/resources/id_and_type_test.rb +76 -0
- data/test/resources/inclusion_test.rb +134 -0
- data/test/resources/links_test.rb +63 -0
- data/test/resources/meta_test.rb +49 -0
- data/test/resources/relationships_test.rb +262 -0
- data/test/resources/sorting_test.rb +79 -0
- data/test/resources/sparse_fieldset_test.rb +160 -0
- data/test/root_objects_test.rb +165 -0
- data/test/test_helper.rb +31 -0
- metadata +235 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
# Represents a single relationship on a resource
|
2
|
+
#
|
3
|
+
# @!attribute [r] name
|
4
|
+
# @return [Symbol]
|
5
|
+
# @!attribute [r] cardinality
|
6
|
+
# @return [:singular,:collection]
|
7
|
+
# @!attribute [r] serializer_inferrer
|
8
|
+
# @return [SerializerInferrer]
|
9
|
+
# @!attribute [r] description
|
10
|
+
# @return [String]
|
11
|
+
# @!attribute [r] data_definition
|
12
|
+
# @return [Proc]
|
13
|
+
class SimpleJsonapi::Definition::Relationship < SimpleJsonapi::Definition::Base
|
14
|
+
include SimpleJsonapi::Definition::Concerns::HasLinksObject
|
15
|
+
include SimpleJsonapi::Definition::Concerns::HasMetaObject
|
16
|
+
|
17
|
+
attr_reader :name, :cardinality, :serializer_inferrer, :description, :data_definition
|
18
|
+
|
19
|
+
# @param name [Symbol]
|
20
|
+
# @param cardinality [Symbol] +:singular+, +:collection+
|
21
|
+
# @param description [String]
|
22
|
+
# @yieldparam resource [Object]
|
23
|
+
# @yieldreturn [Object,Array<Object>] The related resource or resources
|
24
|
+
# @option (see Definition::Base#initialize)
|
25
|
+
def initialize(name, cardinality:, description: nil, **options, &block)
|
26
|
+
super
|
27
|
+
|
28
|
+
unless %i[singular collection].include?(cardinality)
|
29
|
+
raise ArgumentError, "Cardinality must be :singular or :collection"
|
30
|
+
end
|
31
|
+
|
32
|
+
@name = name.to_sym
|
33
|
+
@cardinality = cardinality.to_sym
|
34
|
+
@description = description.to_s.presence
|
35
|
+
@serializer_inferrer = SimpleJsonapi::SerializerInferrer.wrap(options[:serializer])
|
36
|
+
|
37
|
+
instance_eval(&block) if block_given?
|
38
|
+
|
39
|
+
data { |resource| resource.public_send(name) } unless data_definition
|
40
|
+
end
|
41
|
+
|
42
|
+
private def initialize_dup(new_def)
|
43
|
+
super
|
44
|
+
# name and cardinality are symbols, can't be duped
|
45
|
+
# serializer_inferrer doesn't need to be duped?
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [void]
|
49
|
+
def data(&block)
|
50
|
+
@data_definition = block
|
51
|
+
end
|
52
|
+
|
53
|
+
def singular?
|
54
|
+
cardinality == :singular
|
55
|
+
end
|
56
|
+
|
57
|
+
def collection?
|
58
|
+
cardinality == :collection
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# Represents a single resource object.
|
2
|
+
#
|
3
|
+
# @!attribute [r] id_definition
|
4
|
+
# @return [Proc]
|
5
|
+
# @!attribute [r] type_definition
|
6
|
+
# @return [Proc]
|
7
|
+
# @!attribute [r] attribute_definitions
|
8
|
+
# @return [Hash{Symbol => Attribute}]
|
9
|
+
# @!attribute [r] relationship_definitions
|
10
|
+
# @return [Hash{Symbol => Relationship}]
|
11
|
+
class SimpleJsonapi::Definition::Resource < SimpleJsonapi::Definition::Base
|
12
|
+
include SimpleJsonapi::Definition::Concerns::HasLinksObject
|
13
|
+
include SimpleJsonapi::Definition::Concerns::HasMetaObject
|
14
|
+
|
15
|
+
attr_reader :id_definition, :type_definition, :attribute_definitions, :relationship_definitions
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
super
|
19
|
+
@id_definition = wrap_in_proc(&:id)
|
20
|
+
@type_definition = wrap_in_proc do |resource|
|
21
|
+
resource.class.name.demodulize.underscore.pluralize
|
22
|
+
end
|
23
|
+
|
24
|
+
@attribute_definitions = {}
|
25
|
+
@relationship_definitions = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
private def initialize_dup(new_def)
|
29
|
+
super
|
30
|
+
new_def.instance_variable_set(:@id_definition, @id_definition.dup) unless @id_definition.nil?
|
31
|
+
new_def.instance_variable_set(:@type_definition, @type_definition.dup) unless @type_definition.nil?
|
32
|
+
|
33
|
+
unless @attribute_definitions.nil?
|
34
|
+
new_def.instance_variable_set(:@attribute_definitions, @attribute_definitions.dup)
|
35
|
+
end
|
36
|
+
|
37
|
+
unless @relationship_definitions.nil?
|
38
|
+
new_def.instance_variable_set(:@relationship_definitions, @relationship_definitions.dup)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @overload id(&block)
|
43
|
+
# @overload id(value)
|
44
|
+
# @yieldparam resource [Object]
|
45
|
+
# @yieldreturn [String]
|
46
|
+
# @return [void]
|
47
|
+
def id(*args, &block)
|
48
|
+
@id_definition = wrap_in_proc(*args, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @overload type(&block)
|
52
|
+
# @overload type(value)
|
53
|
+
# @yieldparam resource [Object]
|
54
|
+
# @yieldreturn [String]
|
55
|
+
# @return [void]
|
56
|
+
def type(*args, &block)
|
57
|
+
@type_definition = wrap_in_proc(*args, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @overload attribute(name, type: nil, description: nil, **options)
|
61
|
+
# @overload attribute(name, type: nil, description: nil, **options, &block)
|
62
|
+
# @option (see Definition::Base#initialize)
|
63
|
+
# @option options [Symbol] type data type
|
64
|
+
# @option options [String] description
|
65
|
+
# @yieldparam resource [Object]
|
66
|
+
# @yieldreturn [#to_json] the value
|
67
|
+
# @return [void]
|
68
|
+
def attribute(name, **options, &block)
|
69
|
+
# Allow type attribute to be defined before throwing error to support non-compliant data_comleteness/v1
|
70
|
+
attribute_definitions[name.to_sym] = SimpleJsonapi::Definition::Attribute.new(name, **options, &block)
|
71
|
+
|
72
|
+
if %w[id type].include?(name.to_s)
|
73
|
+
raise ArgumentError, "`#{name}` is not allowed as an attribute name"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @overload has_one(name, description: nil, **options, &block)
|
78
|
+
# @param name [Symbol]
|
79
|
+
# @option (see Definition::Relationship#initialize)
|
80
|
+
# @option options [String] description
|
81
|
+
# @yieldparam (see Definition::Relationship#initialize)
|
82
|
+
# @yieldreturn (see Definition::Relationship#initialize)
|
83
|
+
# @return [void]
|
84
|
+
def has_one(name, **options, &block)
|
85
|
+
relationship(name, cardinality: :singular, **options, &block)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @overload has_many(name, description: nil, **options, &block)
|
89
|
+
# @param name [Symbol]
|
90
|
+
# @option (see Definition::Relationship#initialize)
|
91
|
+
# @option options [String] description
|
92
|
+
# @yieldparam (see Definition::Relationship#initialize)
|
93
|
+
# @yieldreturn (see Definition::Relationship#initialize)
|
94
|
+
# @return [void]
|
95
|
+
def has_many(name, **options, &block)
|
96
|
+
relationship(name, cardinality: :collection, **options, &block)
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def relationship(name, **options, &block)
|
102
|
+
relationship_definitions[name.to_sym] = SimpleJsonapi::Definition::Relationship.new(name, options, &block)
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Subclass {ErrorSerializer} to create serializers for specific types of errors.
|
2
|
+
class SimpleJsonapi::ErrorSerializer
|
3
|
+
include SimpleJsonapi::SerializerMethods
|
4
|
+
|
5
|
+
class << self
|
6
|
+
# @overload (see Definition::Error#id)
|
7
|
+
# @return (see Definition::Error#id)
|
8
|
+
def id(*args, **options, &block)
|
9
|
+
definition.id(*args, **options, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
# @overload (see Definition::Error#status)
|
13
|
+
# @return (see Definition::Error#status)
|
14
|
+
def status(*args, **options, &block)
|
15
|
+
definition.status(*args, **options, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @overload (see Definition::Error#code)
|
19
|
+
# @return (see Definition::Error#code)
|
20
|
+
def code(*args, **options, &block)
|
21
|
+
definition.code(*args, **options, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @overload (see Definition::Error#title)
|
25
|
+
# @return (see Definition::Error#title)
|
26
|
+
def title(*args, **options, &block)
|
27
|
+
definition.title(*args, **options, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @overload (see Definition::Error#detail)
|
31
|
+
# @return (see Definition::Error#detail)
|
32
|
+
def detail(*args, **options, &block)
|
33
|
+
definition.detail(*args, **options, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @overload (see Definition::Error#source)
|
37
|
+
# @return (see Definition::Error#source)
|
38
|
+
def source(*args, &block)
|
39
|
+
definition.source(*args, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @overload (see Definition::Error#about_link)
|
43
|
+
# @return (see Definition::Error#about_link)
|
44
|
+
def about_link(*args, **options, &block)
|
45
|
+
definition.about_link(*args, **options, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
# @overload (see Definition::Concerns::HasMetaObject#meta)
|
49
|
+
# @return (see Definition::Concerns::HasMetaObject#meta)
|
50
|
+
def meta(name, *args, **options, &block)
|
51
|
+
definition.meta(name, *args, **options, &block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
self.definition = SimpleJsonapi::Definition::Error.new
|
56
|
+
|
57
|
+
# @return (see Definition::Error#member_definitions)
|
58
|
+
def member_definitions
|
59
|
+
definition.member_definitions
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return (see Definition::Error#source_definitions)
|
63
|
+
def source_definition
|
64
|
+
definition.source_definition
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return (see Definition::Concerns::HasLinksObject#link_definitions)
|
68
|
+
def link_definitions
|
69
|
+
definition.link_definitions
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return (see Definition::Concerns::HasMetaObject#meta_definitions)
|
73
|
+
def meta_definitions
|
74
|
+
definition.meta_definitions
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative 'wrapped_error'
|
2
|
+
|
3
|
+
class SimpleJsonapi::Errors::BadRequest < SimpleJsonapi::Errors::WrappedError
|
4
|
+
def initialize(_cause = nil, **attributes)
|
5
|
+
super attributes.merge(
|
6
|
+
status: "400",
|
7
|
+
code: "bad_request",
|
8
|
+
title: "Bad request",
|
9
|
+
)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module SimpleJsonapi::Errors
|
2
|
+
# A generic serializable error class.
|
3
|
+
class WrappedError
|
4
|
+
# @!attribute [rw] cause
|
5
|
+
# The original error.
|
6
|
+
# @return [Object]
|
7
|
+
# @!attribute [rw] id
|
8
|
+
# @return [String]
|
9
|
+
# @!attribute [rw] status
|
10
|
+
# @return [String]
|
11
|
+
# @!attribute [rw] code
|
12
|
+
# @return [String]
|
13
|
+
# @!attribute [rw] title
|
14
|
+
# @return [String]
|
15
|
+
# @!attribute [rw] detail
|
16
|
+
# @return [String]
|
17
|
+
# @!attribute [rw] source_pointer
|
18
|
+
# @return [String]
|
19
|
+
# @!attribute [rw] source_parameter
|
20
|
+
# @return [String]
|
21
|
+
# @!attribute [rw] about_link
|
22
|
+
# @return [String]
|
23
|
+
attr_accessor :cause, :id, :status, :code, :title, :detail, :source_pointer, :source_parameter, :about_link
|
24
|
+
|
25
|
+
# @param cause [Object] The underlying error
|
26
|
+
# @param attributes [Hash{Symbol => String}]
|
27
|
+
def initialize(cause = nil, **attributes)
|
28
|
+
self.cause = cause
|
29
|
+
|
30
|
+
attributes.each do |name, value|
|
31
|
+
send("#{name}=", value) if respond_to?("#{name}=")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# A serializer for {WrappedError} objects.
|
2
|
+
class SimpleJsonapi::Errors::WrappedErrorSerializer < SimpleJsonapi::ErrorSerializer
|
3
|
+
id(if: ->(err) { err.id.to_s.present? }) do |err|
|
4
|
+
err.id.to_s.presence
|
5
|
+
end
|
6
|
+
|
7
|
+
status(if: ->(err) { err.status.to_s.present? }) do |err|
|
8
|
+
err.status.to_s.presence
|
9
|
+
end
|
10
|
+
|
11
|
+
code(if: ->(err) { err.code.to_s.present? }) do |err|
|
12
|
+
err.code.to_s.presence
|
13
|
+
end
|
14
|
+
|
15
|
+
title(if: ->(err) { err.title.to_s.present? }) do |err|
|
16
|
+
err.title.to_s.presence
|
17
|
+
end
|
18
|
+
|
19
|
+
detail(if: ->(err) { err.detail.to_s.present? }) do |err|
|
20
|
+
err.detail.to_s.presence
|
21
|
+
end
|
22
|
+
|
23
|
+
source do
|
24
|
+
pointer(if: ->(err) { err.source_pointer.to_s.present? }) do |err|
|
25
|
+
err.source_pointer.to_s.presence
|
26
|
+
end
|
27
|
+
parameter(if: ->(err) { err.source_parameter.to_s.present? }) do |err|
|
28
|
+
err.source_parameter.to_s.presence
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
about_link(if: ->(err) { err.about_link.to_s.present? }) do |err|
|
33
|
+
err.about_link.to_s.presence
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SimpleJsonapi
|
2
|
+
# The error raised when a document does not have a valid JSONAPI structure.
|
3
|
+
class InvalidJsonStructureError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
# The error raised when the same resource is added to the +included+ member twice.
|
7
|
+
class DuplicateResourceError < StandardError
|
8
|
+
attr_reader :type, :id
|
9
|
+
|
10
|
+
# @param type [String]
|
11
|
+
# @param id [String]
|
12
|
+
# @param msg [String]
|
13
|
+
def initialize(type, id, msg = nil)
|
14
|
+
@type = type
|
15
|
+
@id = id
|
16
|
+
@msg = msg
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
@msg.present? ? @msg : "Resource with type #{type} and id #{id} is already included"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# The error raised when a {SerializerInferrer} cannot find the serializer for a resource or error.
|
25
|
+
class SerializerInferenceError < StandardError
|
26
|
+
attr_reader :object
|
27
|
+
|
28
|
+
# @param object [Object] the resource or error
|
29
|
+
# @param msg [String]
|
30
|
+
def initialize(object, msg = nil)
|
31
|
+
@object = object
|
32
|
+
@msg = msg
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
@msg.present? ? @msg : "Unable to infer serializer for #{object.class}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module SimpleJsonapi
|
2
|
+
# Identifies the serializer class that should be used for a resource or error object.
|
3
|
+
class SerializerInferrer
|
4
|
+
# @!attribute [r] namespace
|
5
|
+
# @return [String]
|
6
|
+
attr_reader :namespace
|
7
|
+
|
8
|
+
# # A {SerializerInferrer} that always returns the serializer class provided in the constructor.
|
9
|
+
# class Constant
|
10
|
+
# # @param serializer_class [Serializer,ErrorSerializer]
|
11
|
+
# def initialize(serializer_class)
|
12
|
+
# @serializer_class = case serializer_class
|
13
|
+
# when Class then serializer_class
|
14
|
+
# else serializer_class.to_s.constantize
|
15
|
+
# end
|
16
|
+
|
17
|
+
# super { |resource| @serializer_class }
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
|
21
|
+
# @param serializer [SerializerInferrer,Serializer,ErrorSerializer,nil]
|
22
|
+
# @return [SerializerInferrer]
|
23
|
+
def self.wrap(serializer)
|
24
|
+
if serializer.is_a?(SerializerInferrer)
|
25
|
+
serializer
|
26
|
+
elsif serializer.present?
|
27
|
+
klass = serializer_class(serializer)
|
28
|
+
SerializerInferrer.new { |_resource| klass }
|
29
|
+
else
|
30
|
+
SimpleJsonapi.serializer_inferrer
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param explicit_mappings [Hash{Class => Class}] A mapping of resource classes to serializer classes
|
35
|
+
# @param namespace [String] A namespace in which to search for serializer classes
|
36
|
+
# @yieldparam object [Object] The resource or error
|
37
|
+
# @yieldreturn [Class] A serializer class
|
38
|
+
def initialize(explicit_mappings: nil, namespace: nil, &block)
|
39
|
+
@explicit_mappings = {}
|
40
|
+
@explicit_mappings.merge!(explicit_mappings) if explicit_mappings
|
41
|
+
|
42
|
+
@namespace = namespace
|
43
|
+
@infer_proc = block
|
44
|
+
end
|
45
|
+
|
46
|
+
delegate :each, to: :@explicit_mappings
|
47
|
+
|
48
|
+
# @param explicit_mappings [Hash{Class => Class}]
|
49
|
+
# @return [self]
|
50
|
+
def merge(explicit_mappings)
|
51
|
+
explicit_mappings.each do |resource_class, serializer_class|
|
52
|
+
add(resource_class, serializer_class)
|
53
|
+
end
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param resource_class [Class]
|
58
|
+
# @param serializer_class [Class]
|
59
|
+
# @return [void]
|
60
|
+
def add(resource_class, serializer_class)
|
61
|
+
@explicit_mappings[resource_class.name] = serializer_class
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param resource [Object]
|
65
|
+
# @return [Class] A serializer class
|
66
|
+
# @raise [SerializerInferenceError] if a serializer isn't found
|
67
|
+
def infer(resource)
|
68
|
+
serializer = (@infer_proc || default_infer_proc).call(resource)
|
69
|
+
raise SerializerInferenceError.new(resource) unless serializer
|
70
|
+
serializer
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [Class,nil]
|
74
|
+
def default_serializer
|
75
|
+
unless defined?(@default_serializer)
|
76
|
+
begin
|
77
|
+
@default_serializer = infer(nil)
|
78
|
+
rescue SerializerInferenceError
|
79
|
+
@default_serializer = nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
@default_serializer
|
83
|
+
end
|
84
|
+
|
85
|
+
def default_serializer?
|
86
|
+
default_serializer != nil
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def default_infer_proc
|
92
|
+
@default_infer_proc ||= proc do |resource|
|
93
|
+
serializer = nil
|
94
|
+
|
95
|
+
resource.class.ancestors.find do |ancestor|
|
96
|
+
serializer = find_serializer_by_name(ancestor.name)
|
97
|
+
end
|
98
|
+
|
99
|
+
serializer
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def find_serializer_by_name(name)
|
104
|
+
if @explicit_mappings.key?(name)
|
105
|
+
@explicit_mappings[name]
|
106
|
+
else
|
107
|
+
serializer_name_for_class_name(name).safe_constantize
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def serializer_name_for_class_name(resource_class_name)
|
112
|
+
"#{prefix(resource_class_name)}#{resource_class_name}Serializer"
|
113
|
+
end
|
114
|
+
|
115
|
+
def prefix(resource_class_name)
|
116
|
+
if namespace.blank?
|
117
|
+
""
|
118
|
+
elsif resource_class_name.starts_with?("#{namespace}::")
|
119
|
+
""
|
120
|
+
else
|
121
|
+
"#{namespace}::"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.serializer_class(serializer)
|
126
|
+
case serializer
|
127
|
+
when Class
|
128
|
+
serializer
|
129
|
+
else
|
130
|
+
serializer.to_s.constantize
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private_class_method :serializer_class
|
135
|
+
end
|
136
|
+
end
|