active_model_serializers 0.8.3 → 0.10.15

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.
Files changed (97) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +726 -6
  3. data/{MIT-LICENSE.txt → MIT-LICENSE} +3 -2
  4. data/README.md +194 -545
  5. data/lib/action_controller/serialization.rb +53 -38
  6. data/lib/active_model/serializable_resource.rb +13 -0
  7. data/lib/active_model/serializer/adapter/attributes.rb +17 -0
  8. data/lib/active_model/serializer/adapter/base.rb +20 -0
  9. data/lib/active_model/serializer/adapter/json.rb +17 -0
  10. data/lib/active_model/serializer/adapter/json_api.rb +17 -0
  11. data/lib/active_model/serializer/adapter/null.rb +17 -0
  12. data/lib/active_model/serializer/adapter.rb +26 -0
  13. data/lib/active_model/serializer/array_serializer.rb +14 -0
  14. data/lib/active_model/serializer/association.rb +73 -0
  15. data/lib/active_model/serializer/attribute.rb +27 -0
  16. data/lib/active_model/serializer/belongs_to_reflection.rb +13 -0
  17. data/lib/active_model/serializer/collection_serializer.rb +99 -0
  18. data/lib/active_model/serializer/concerns/caching.rb +305 -0
  19. data/lib/active_model/serializer/error_serializer.rb +16 -0
  20. data/lib/active_model/serializer/errors_serializer.rb +34 -0
  21. data/lib/active_model/serializer/field.rb +92 -0
  22. data/lib/active_model/serializer/fieldset.rb +33 -0
  23. data/lib/active_model/serializer/has_many_reflection.rb +12 -0
  24. data/lib/active_model/serializer/has_one_reflection.rb +9 -0
  25. data/lib/active_model/serializer/lazy_association.rb +99 -0
  26. data/lib/active_model/serializer/link.rb +23 -0
  27. data/lib/active_model/serializer/lint.rb +152 -0
  28. data/lib/active_model/serializer/null.rb +19 -0
  29. data/lib/active_model/serializer/reflection.rb +212 -0
  30. data/lib/active_model/serializer/version.rb +7 -0
  31. data/lib/active_model/serializer.rb +354 -442
  32. data/lib/active_model_serializers/adapter/attributes.rb +36 -0
  33. data/lib/active_model_serializers/adapter/base.rb +85 -0
  34. data/lib/active_model_serializers/adapter/json.rb +23 -0
  35. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +215 -0
  36. data/lib/active_model_serializers/adapter/json_api/error.rb +98 -0
  37. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +51 -0
  38. data/lib/active_model_serializers/adapter/json_api/link.rb +85 -0
  39. data/lib/active_model_serializers/adapter/json_api/meta.rb +39 -0
  40. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +94 -0
  41. data/lib/active_model_serializers/adapter/json_api/relationship.rb +106 -0
  42. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +68 -0
  43. data/lib/active_model_serializers/adapter/json_api.rb +535 -0
  44. data/lib/active_model_serializers/adapter/null.rb +11 -0
  45. data/lib/active_model_serializers/adapter.rb +100 -0
  46. data/lib/active_model_serializers/callbacks.rb +57 -0
  47. data/lib/active_model_serializers/deprecate.rb +56 -0
  48. data/lib/active_model_serializers/deserialization.rb +17 -0
  49. data/lib/active_model_serializers/json_pointer.rb +16 -0
  50. data/lib/active_model_serializers/logging.rb +124 -0
  51. data/lib/active_model_serializers/lookup_chain.rb +82 -0
  52. data/lib/active_model_serializers/model.rb +132 -0
  53. data/lib/active_model_serializers/railtie.rb +62 -0
  54. data/lib/active_model_serializers/register_jsonapi_renderer.rb +80 -0
  55. data/lib/active_model_serializers/serializable_resource.rb +84 -0
  56. data/lib/active_model_serializers/serialization_context.rb +41 -0
  57. data/lib/active_model_serializers/test/schema.rb +140 -0
  58. data/lib/active_model_serializers/test/serializer.rb +127 -0
  59. data/lib/active_model_serializers/test.rb +9 -0
  60. data/lib/active_model_serializers.rb +49 -81
  61. data/lib/generators/rails/USAGE +6 -0
  62. data/lib/generators/rails/resource_override.rb +12 -0
  63. data/lib/generators/rails/serializer_generator.rb +38 -0
  64. data/lib/generators/rails/templates/serializer.rb.erb +8 -0
  65. data/lib/grape/active_model_serializers.rb +18 -0
  66. data/lib/grape/formatters/active_model_serializers.rb +34 -0
  67. data/lib/grape/helpers/active_model_serializers.rb +19 -0
  68. data/lib/tasks/rubocop.rake +60 -0
  69. metadata +240 -51
  70. data/.gitignore +0 -18
  71. data/.travis.yml +0 -28
  72. data/DESIGN.textile +0 -586
  73. data/Gemfile +0 -4
  74. data/Gemfile.edge +0 -9
  75. data/Rakefile +0 -18
  76. data/active_model_serializers.gemspec +0 -24
  77. data/bench/perf.rb +0 -43
  78. data/cruft.md +0 -19
  79. data/lib/active_model/array_serializer.rb +0 -104
  80. data/lib/active_model/serializer/associations.rb +0 -233
  81. data/lib/active_model/serializers/version.rb +0 -5
  82. data/lib/active_record/serializer_override.rb +0 -16
  83. data/lib/generators/resource_override.rb +0 -13
  84. data/lib/generators/serializer/USAGE +0 -9
  85. data/lib/generators/serializer/serializer_generator.rb +0 -42
  86. data/lib/generators/serializer/templates/serializer.rb +0 -19
  87. data/test/array_serializer_test.rb +0 -75
  88. data/test/association_test.rb +0 -592
  89. data/test/caching_test.rb +0 -96
  90. data/test/generators_test.rb +0 -85
  91. data/test/no_serialization_scope_test.rb +0 -34
  92. data/test/serialization_scope_name_test.rb +0 -67
  93. data/test/serialization_test.rb +0 -392
  94. data/test/serializer_support_test.rb +0 -51
  95. data/test/serializer_test.rb +0 -1465
  96. data/test/test_fakes.rb +0 -217
  97. data/test/test_helper.rb +0 -32
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class Attributes < Base
6
+ def initialize(*)
7
+ super
8
+ instance_options[:fieldset] ||= ActiveModel::Serializer::Fieldset.new(fields_to_fieldset(instance_options.delete(:fields)))
9
+ end
10
+
11
+ def serializable_hash(options = nil)
12
+ options = serialization_options(options.dup)
13
+ options[:fields] ||= instance_options[:fields]
14
+ serialized_hash = serializer.serializable_hash(instance_options, options, self)
15
+
16
+ self.class.transform_key_casing!(serialized_hash, instance_options)
17
+ end
18
+
19
+ private
20
+
21
+ def fields_to_fieldset(fields)
22
+ return fields if fields.nil?
23
+ resource_fields = []
24
+ relationship_fields = {}
25
+ fields.each do |field|
26
+ case field
27
+ when Symbol, String then resource_fields << field
28
+ when Hash then relationship_fields.merge!(field)
29
+ else fail ArgumentError, "Unknown conversion of fields to fieldset: '#{field.inspect}' in '#{fields.inspect}'"
30
+ end
31
+ end
32
+ relationship_fields.merge!(serializer.json_key.to_sym => resource_fields)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'case_transform'
4
+
5
+ module ActiveModelSerializers
6
+ module Adapter
7
+ class Base
8
+ # Automatically register adapters when subclassing
9
+ def self.inherited(subclass)
10
+ ActiveModelSerializers::Adapter.register(subclass)
11
+ end
12
+
13
+ # Sets the default transform for the adapter.
14
+ #
15
+ # @return [Symbol] the default transform for the adapter
16
+ def self.default_key_transform
17
+ :unaltered
18
+ end
19
+
20
+ # Determines the transform to use in order of precedence:
21
+ # adapter option, global config, adapter default.
22
+ #
23
+ # @param options [Object]
24
+ # @return [Symbol] the transform to use
25
+ def self.transform(options)
26
+ return options[:key_transform] if options && options[:key_transform]
27
+ ActiveModelSerializers.config.key_transform || default_key_transform
28
+ end
29
+
30
+ # Transforms the casing of the supplied value.
31
+ #
32
+ # @param value [Object] the value to be transformed
33
+ # @param options [Object] serializable resource options
34
+ # @return [Symbol] the default transform for the adapter
35
+ def self.transform_key_casing!(value, options)
36
+ CaseTransform.send(transform(options), value)
37
+ end
38
+
39
+ def self.cache_key
40
+ @cache_key ||= ActiveModelSerializers::Adapter.registered_name(self)
41
+ end
42
+
43
+ def self.fragment_cache(cached_hash, non_cached_hash)
44
+ non_cached_hash.merge cached_hash
45
+ end
46
+
47
+ attr_reader :serializer, :instance_options
48
+
49
+ def initialize(serializer, options = {})
50
+ @serializer = serializer
51
+ @instance_options = options
52
+ end
53
+
54
+ # Subclasses that implement this method must first call
55
+ # options = serialization_options(options)
56
+ def serializable_hash(_options = nil)
57
+ fail NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
58
+ end
59
+
60
+ def as_json(options = nil)
61
+ serializable_hash(options)
62
+ end
63
+
64
+ def cache_key
65
+ self.class.cache_key
66
+ end
67
+
68
+ def fragment_cache(cached_hash, non_cached_hash)
69
+ self.class.fragment_cache(cached_hash, non_cached_hash)
70
+ end
71
+
72
+ private
73
+
74
+ # see https://github.com/rails-api/active_model_serializers/pull/965
75
+ # When <tt>options</tt> is +nil+, sets it to +{}+
76
+ def serialization_options(options)
77
+ options ||= {} # rubocop:disable Lint/UselessAssignment
78
+ end
79
+
80
+ def root
81
+ serializer.json_key.to_sym if serializer.json_key
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class Json < Base
6
+ def serializable_hash(options = nil)
7
+ options = serialization_options(options)
8
+ serialized_hash = { root => Attributes.new(serializer, instance_options).serializable_hash(options) }
9
+ serialized_hash[meta_key] = meta unless meta.blank?
10
+
11
+ self.class.transform_key_casing!(serialized_hash, instance_options)
12
+ end
13
+
14
+ def meta
15
+ instance_options.fetch(:meta, nil)
16
+ end
17
+
18
+ def meta_key
19
+ instance_options.fetch(:meta_key, 'meta'.freeze)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,215 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class JsonApi
6
+ # NOTE(Experimental):
7
+ # This is an experimental feature. Both the interface and internals could be subject
8
+ # to changes.
9
+ module Deserialization
10
+ InvalidDocument = Class.new(ArgumentError)
11
+
12
+ module_function
13
+
14
+ # Transform a JSON API document, containing a single data object,
15
+ # into a hash that is ready for ActiveRecord::Base.new() and such.
16
+ # Raises InvalidDocument if the payload is not properly formatted.
17
+ #
18
+ # @param [Hash|ActionController::Parameters] document
19
+ # @param [Hash] options
20
+ # only: Array of symbols of whitelisted fields.
21
+ # except: Array of symbols of blacklisted fields.
22
+ # keys: Hash of translated keys (e.g. :author => :user).
23
+ # polymorphic: Array of symbols of polymorphic fields.
24
+ # @return [Hash]
25
+ #
26
+ # @example
27
+ # document = {
28
+ # data: {
29
+ # id: 1,
30
+ # type: 'post',
31
+ # attributes: {
32
+ # title: 'Title 1',
33
+ # date: '2015-12-20'
34
+ # },
35
+ # associations: {
36
+ # author: {
37
+ # data: {
38
+ # type: 'user',
39
+ # id: 2
40
+ # }
41
+ # },
42
+ # second_author: {
43
+ # data: nil
44
+ # },
45
+ # comments: {
46
+ # data: [{
47
+ # type: 'comment',
48
+ # id: 3
49
+ # },{
50
+ # type: 'comment',
51
+ # id: 4
52
+ # }]
53
+ # }
54
+ # }
55
+ # }
56
+ # }
57
+ #
58
+ # parse(document) #=>
59
+ # # {
60
+ # # title: 'Title 1',
61
+ # # date: '2015-12-20',
62
+ # # author_id: 2,
63
+ # # second_author_id: nil
64
+ # # comment_ids: [3, 4]
65
+ # # }
66
+ #
67
+ # parse(document, only: [:title, :date, :author],
68
+ # keys: { date: :published_at },
69
+ # polymorphic: [:author]) #=>
70
+ # # {
71
+ # # title: 'Title 1',
72
+ # # published_at: '2015-12-20',
73
+ # # author_id: '2',
74
+ # # author_type: 'people'
75
+ # # }
76
+ #
77
+ def parse!(document, options = {})
78
+ parse(document, options) do |invalid_payload, reason|
79
+ fail InvalidDocument, "Invalid payload (#{reason}): #{invalid_payload}"
80
+ end
81
+ end
82
+
83
+ # Same as parse!, but returns an empty hash instead of raising InvalidDocument
84
+ # on invalid payloads.
85
+ def parse(document, options = {})
86
+ document = document.dup.permit!.to_h if document.is_a?(ActionController::Parameters)
87
+
88
+ validate_payload(document) do |invalid_document, reason|
89
+ yield invalid_document, reason if block_given?
90
+ return {}
91
+ end
92
+
93
+ primary_data = document['data']
94
+ attributes = primary_data['attributes'] || {}
95
+ attributes['id'] = primary_data['id'] if primary_data['id']
96
+ relationships = primary_data['relationships'] || {}
97
+
98
+ filter_fields(attributes, options)
99
+ filter_fields(relationships, options)
100
+
101
+ hash = {}
102
+ hash.merge!(parse_attributes(attributes, options))
103
+ hash.merge!(parse_relationships(relationships, options))
104
+
105
+ hash
106
+ end
107
+
108
+ # Checks whether a payload is compliant with the JSON API spec.
109
+ #
110
+ # @api private
111
+ # rubocop:disable Metrics/CyclomaticComplexity
112
+ def validate_payload(payload)
113
+ unless payload.is_a?(Hash)
114
+ yield payload, 'Expected hash'
115
+ return
116
+ end
117
+
118
+ primary_data = payload['data']
119
+ unless primary_data.is_a?(Hash)
120
+ yield payload, { data: 'Expected hash' }
121
+ return
122
+ end
123
+
124
+ attributes = primary_data['attributes'] || {}
125
+ unless attributes.is_a?(Hash)
126
+ yield payload, { data: { attributes: 'Expected hash or nil' } }
127
+ return
128
+ end
129
+
130
+ relationships = primary_data['relationships'] || {}
131
+ unless relationships.is_a?(Hash)
132
+ yield payload, { data: { relationships: 'Expected hash or nil' } }
133
+ return
134
+ end
135
+
136
+ relationships.each do |(key, value)|
137
+ unless value.is_a?(Hash) && value.key?('data')
138
+ yield payload, { data: { relationships: { key => 'Expected hash with :data key' } } }
139
+ end
140
+ end
141
+ end
142
+ # rubocop:enable Metrics/CyclomaticComplexity
143
+
144
+ # @api private
145
+ def filter_fields(fields, options)
146
+ if (only = options[:only])
147
+ fields.slice!(*Array(only).map(&:to_s))
148
+ elsif (except = options[:except])
149
+ fields.except!(*Array(except).map(&:to_s))
150
+ end
151
+ end
152
+
153
+ # @api private
154
+ def field_key(field, options)
155
+ (options[:keys] || {}).fetch(field.to_sym, field).to_sym
156
+ end
157
+
158
+ # @api private
159
+ def parse_attributes(attributes, options)
160
+ transform_keys(attributes, options)
161
+ .map { |(k, v)| { field_key(k, options) => v } }
162
+ .reduce({}, :merge)
163
+ end
164
+
165
+ # Given an association name, and a relationship data attribute, build a hash
166
+ # mapping the corresponding ActiveRecord attribute to the corresponding value.
167
+ #
168
+ # @example
169
+ # parse_relationship(:comments, [{ 'id' => '1', 'type' => 'comments' },
170
+ # { 'id' => '2', 'type' => 'comments' }],
171
+ # {})
172
+ # # => { :comment_ids => ['1', '2'] }
173
+ # parse_relationship(:author, { 'id' => '1', 'type' => 'users' }, {})
174
+ # # => { :author_id => '1' }
175
+ # parse_relationship(:author, nil, {})
176
+ # # => { :author_id => nil }
177
+ # @param [Symbol] assoc_name
178
+ # @param [Hash] assoc_data
179
+ # @param [Hash] options
180
+ # @return [Hash{Symbol, Object}]
181
+ #
182
+ # @api private
183
+ def parse_relationship(assoc_name, assoc_data, options)
184
+ prefix_key = field_key(assoc_name, options).to_s.singularize
185
+ hash =
186
+ if assoc_data.is_a?(Array)
187
+ { "#{prefix_key}_ids".to_sym => assoc_data.map { |ri| ri['id'] } }
188
+ else
189
+ { "#{prefix_key}_id".to_sym => assoc_data ? assoc_data['id'] : nil }
190
+ end
191
+
192
+ polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym)
193
+ if polymorphic
194
+ hash["#{prefix_key}_type".to_sym] = assoc_data.present? ? assoc_data['type'].classify : nil
195
+ end
196
+
197
+ hash
198
+ end
199
+
200
+ # @api private
201
+ def parse_relationships(relationships, options)
202
+ transform_keys(relationships, options)
203
+ .map { |(k, v)| parse_relationship(k, v['data'], options) }
204
+ .reduce({}, :merge)
205
+ end
206
+
207
+ # @api private
208
+ def transform_keys(hash, options)
209
+ transform = options[:key_transform] || :underscore
210
+ CaseTransform.send(transform, hash)
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class JsonApi < Base
6
+ module Error
7
+ # rubocop:disable Style/AsciiComments
8
+ UnknownSourceTypeError = Class.new(ArgumentError)
9
+
10
+ # Builds a JSON API Errors Object
11
+ # {http://jsonapi.org/format/#errors JSON API Errors}
12
+ #
13
+ # @param [ActiveModel::Serializer::ErrorSerializer] error_serializer
14
+ # @return [Array<Symbol, Array<String>>] i.e. attribute_name, [attribute_errors]
15
+ def self.resource_errors(error_serializer, options)
16
+ error_serializer.as_json.flat_map do |attribute_name, attribute_errors|
17
+ attribute_name = JsonApi.send(:transform_key_casing!, attribute_name,
18
+ options)
19
+ attribute_error_objects(attribute_name, attribute_errors)
20
+ end
21
+ end
22
+
23
+ # definition:
24
+ # JSON Object
25
+ #
26
+ # properties:
27
+ # ☐ id : String
28
+ # ☐ status : String
29
+ # ☐ code : String
30
+ # ☐ title : String
31
+ # ☑ detail : String
32
+ # ☐ links
33
+ # ☐ meta
34
+ # ☑ error_source
35
+ #
36
+ # description:
37
+ # id : A unique identifier for this particular occurrence of the problem.
38
+ # status : The HTTP status code applicable to this problem, expressed as a string value
39
+ # code : An application-specific error code, expressed as a string value.
40
+ # title : A short, human-readable summary of the problem. It **SHOULD NOT** change from
41
+ # occurrence to occurrence of the problem, except for purposes of localization.
42
+ # detail : A human-readable explanation specific to this occurrence of the problem.
43
+ # structure:
44
+ # {
45
+ # title: 'SystemFailure',
46
+ # detail: 'something went terribly wrong',
47
+ # status: '500'
48
+ # }.merge!(errorSource)
49
+ def self.attribute_error_objects(attribute_name, attribute_errors)
50
+ attribute_errors.map do |attribute_error|
51
+ {
52
+ source: error_source(:pointer, attribute_name),
53
+ detail: attribute_error
54
+ }
55
+ end
56
+ end
57
+
58
+ # errorSource
59
+ # description:
60
+ # oneOf
61
+ # ☑ pointer : String
62
+ # ☑ parameter : String
63
+ #
64
+ # description:
65
+ # pointer: A JSON Pointer RFC6901 to the associated entity in the request document e.g. "/data"
66
+ # for a primary data object, or "/data/attributes/title" for a specific attribute.
67
+ # https://tools.ietf.org/html/rfc6901
68
+ #
69
+ # parameter: A string indicating which query parameter caused the error
70
+ # structure:
71
+ # if is_attribute?
72
+ # {
73
+ # pointer: '/data/attributes/red-button'
74
+ # }
75
+ # else
76
+ # {
77
+ # parameter: 'pres'
78
+ # }
79
+ # end
80
+ def self.error_source(source_type, attribute_name)
81
+ case source_type
82
+ when :pointer
83
+ {
84
+ pointer: ActiveModelSerializers::JsonPointer.new(:attribute, attribute_name)
85
+ }
86
+ when :parameter
87
+ {
88
+ parameter: attribute_name
89
+ }
90
+ else
91
+ fail UnknownSourceTypeError, "Unknown source type '#{source_type}' for attribute_name '#{attribute_name}'"
92
+ end
93
+ end
94
+ # rubocop:enable Style/AsciiComments
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class JsonApi < Base
6
+ # {http://jsonapi.org/format/#document-jsonapi-object Jsonapi Object}
7
+
8
+ # toplevel_jsonapi
9
+ # definition:
10
+ # JSON Object
11
+ #
12
+ # properties:
13
+ # version : String
14
+ # meta
15
+ #
16
+ # description:
17
+ # An object describing the server's implementation
18
+ # structure:
19
+ # {
20
+ # version: ActiveModelSerializers.config.jsonapi_version,
21
+ # meta: ActiveModelSerializers.config.jsonapi_toplevel_meta
22
+ # }.reject! { |_, v| v.blank? }
23
+ # prs:
24
+ # https://github.com/rails-api/active_model_serializers/pull/1050
25
+ module Jsonapi
26
+ module_function
27
+
28
+ def add!(hash)
29
+ hash.merge!(object) if include_object?
30
+ end
31
+
32
+ def include_object?
33
+ ActiveModelSerializers.config.jsonapi_include_toplevel_object
34
+ end
35
+
36
+ # TODO: see if we can cache this
37
+ def object
38
+ object = {
39
+ jsonapi: {
40
+ version: ActiveModelSerializers.config.jsonapi_version,
41
+ meta: ActiveModelSerializers.config.jsonapi_toplevel_meta
42
+ }
43
+ }
44
+ object[:jsonapi].reject! { |_, v| v.blank? }
45
+
46
+ object
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class JsonApi
6
+ # link
7
+ # definition:
8
+ # oneOf
9
+ # linkString
10
+ # linkObject
11
+ #
12
+ # description:
13
+ # A link **MUST** be represented as either: a string containing the link's URL or a link
14
+ # object."
15
+ # structure:
16
+ # if href?
17
+ # linkString
18
+ # else
19
+ # linkObject
20
+ # end
21
+ #
22
+ # linkString
23
+ # definition:
24
+ # URI
25
+ #
26
+ # description:
27
+ # A string containing the link's URL.
28
+ # structure:
29
+ # 'http://example.com/link-string'
30
+ #
31
+ # linkObject
32
+ # definition:
33
+ # JSON Object
34
+ #
35
+ # properties:
36
+ # href (required) : URI
37
+ # meta
38
+ # structure:
39
+ # {
40
+ # href: 'http://example.com/link-object',
41
+ # meta: meta,
42
+ # }.reject! {|_,v| v.nil? }
43
+ class Link
44
+ include SerializationContext::UrlHelpers
45
+
46
+ def initialize(serializer, value)
47
+ @_routes ||= nil # handles warning
48
+ # actionpack-4.0.13/lib/action_dispatch/routing/route_set.rb:417: warning: instance variable @_routes not initialized
49
+ @object = serializer.object
50
+ @scope = serializer.scope
51
+ # Use the return value of the block unless it is nil.
52
+ if value.respond_to?(:call)
53
+ @value = instance_eval(&value)
54
+ else
55
+ @value = value
56
+ end
57
+ end
58
+
59
+ def href(value)
60
+ @href = value
61
+ nil
62
+ end
63
+
64
+ def meta(value)
65
+ @meta = value
66
+ nil
67
+ end
68
+
69
+ def as_json
70
+ return @value if @value
71
+
72
+ hash = {}
73
+ hash[:href] = @href if defined?(@href)
74
+ hash[:meta] = @meta if defined?(@meta)
75
+
76
+ hash.any? ? hash : nil
77
+ end
78
+
79
+ protected
80
+
81
+ attr_reader :object, :scope
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class JsonApi
6
+ # meta
7
+ # definition:
8
+ # JSON Object
9
+ #
10
+ # description:
11
+ # Non-standard meta-information that can not be represented as an attribute or relationship.
12
+ # structure:
13
+ # {
14
+ # attitude: 'adjustable'
15
+ # }
16
+ class Meta
17
+ def initialize(serializer)
18
+ @object = serializer.object
19
+ @scope = serializer.scope
20
+
21
+ # Use the return value of the block unless it is nil.
22
+ if serializer._meta.respond_to?(:call)
23
+ @value = instance_eval(&serializer._meta)
24
+ else
25
+ @value = serializer._meta
26
+ end
27
+ end
28
+
29
+ def as_json
30
+ @value
31
+ end
32
+
33
+ protected
34
+
35
+ attr_reader :object, :scope
36
+ end
37
+ end
38
+ end
39
+ end