tiny-max-kit 0.0.1

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 (70) hide show
  1. checksums.yaml +7 -0
  2. data/active_model_serializers-0.10.16/CHANGELOG.md +793 -0
  3. data/active_model_serializers-0.10.16/MIT-LICENSE +22 -0
  4. data/active_model_serializers-0.10.16/README.md +304 -0
  5. data/active_model_serializers-0.10.16/lib/action_controller/serialization.rb +76 -0
  6. data/active_model_serializers-0.10.16/lib/active_model/serializable_resource.rb +13 -0
  7. data/active_model_serializers-0.10.16/lib/active_model/serializer/adapter/attributes.rb +17 -0
  8. data/active_model_serializers-0.10.16/lib/active_model/serializer/adapter/base.rb +20 -0
  9. data/active_model_serializers-0.10.16/lib/active_model/serializer/adapter/json.rb +17 -0
  10. data/active_model_serializers-0.10.16/lib/active_model/serializer/adapter/json_api.rb +17 -0
  11. data/active_model_serializers-0.10.16/lib/active_model/serializer/adapter/null.rb +17 -0
  12. data/active_model_serializers-0.10.16/lib/active_model/serializer/adapter.rb +26 -0
  13. data/active_model_serializers-0.10.16/lib/active_model/serializer/array_serializer.rb +14 -0
  14. data/active_model_serializers-0.10.16/lib/active_model/serializer/association.rb +73 -0
  15. data/active_model_serializers-0.10.16/lib/active_model/serializer/attribute.rb +27 -0
  16. data/active_model_serializers-0.10.16/lib/active_model/serializer/belongs_to_reflection.rb +13 -0
  17. data/active_model_serializers-0.10.16/lib/active_model/serializer/collection_serializer.rb +99 -0
  18. data/active_model_serializers-0.10.16/lib/active_model/serializer/concerns/caching.rb +305 -0
  19. data/active_model_serializers-0.10.16/lib/active_model/serializer/error_serializer.rb +16 -0
  20. data/active_model_serializers-0.10.16/lib/active_model/serializer/errors_serializer.rb +34 -0
  21. data/active_model_serializers-0.10.16/lib/active_model/serializer/field.rb +92 -0
  22. data/active_model_serializers-0.10.16/lib/active_model/serializer/fieldset.rb +33 -0
  23. data/active_model_serializers-0.10.16/lib/active_model/serializer/has_many_reflection.rb +12 -0
  24. data/active_model_serializers-0.10.16/lib/active_model/serializer/has_one_reflection.rb +9 -0
  25. data/active_model_serializers-0.10.16/lib/active_model/serializer/lazy_association.rb +99 -0
  26. data/active_model_serializers-0.10.16/lib/active_model/serializer/link.rb +23 -0
  27. data/active_model_serializers-0.10.16/lib/active_model/serializer/lint.rb +152 -0
  28. data/active_model_serializers-0.10.16/lib/active_model/serializer/null.rb +19 -0
  29. data/active_model_serializers-0.10.16/lib/active_model/serializer/reflection.rb +212 -0
  30. data/active_model_serializers-0.10.16/lib/active_model/serializer/version.rb +7 -0
  31. data/active_model_serializers-0.10.16/lib/active_model/serializer.rb +430 -0
  32. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/attributes.rb +36 -0
  33. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/base.rb +85 -0
  34. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/json.rb +23 -0
  35. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/json_api/deserialization.rb +215 -0
  36. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/json_api/error.rb +98 -0
  37. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/json_api/jsonapi.rb +51 -0
  38. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/json_api/link.rb +85 -0
  39. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/json_api/meta.rb +39 -0
  40. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/json_api/pagination_links.rb +94 -0
  41. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/json_api/relationship.rb +106 -0
  42. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +68 -0
  43. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/json_api.rb +535 -0
  44. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter/null.rb +11 -0
  45. data/active_model_serializers-0.10.16/lib/active_model_serializers/adapter.rb +100 -0
  46. data/active_model_serializers-0.10.16/lib/active_model_serializers/callbacks.rb +57 -0
  47. data/active_model_serializers-0.10.16/lib/active_model_serializers/deprecate.rb +56 -0
  48. data/active_model_serializers-0.10.16/lib/active_model_serializers/deserialization.rb +17 -0
  49. data/active_model_serializers-0.10.16/lib/active_model_serializers/json_pointer.rb +16 -0
  50. data/active_model_serializers-0.10.16/lib/active_model_serializers/logging.rb +124 -0
  51. data/active_model_serializers-0.10.16/lib/active_model_serializers/lookup_chain.rb +82 -0
  52. data/active_model_serializers-0.10.16/lib/active_model_serializers/model.rb +132 -0
  53. data/active_model_serializers-0.10.16/lib/active_model_serializers/railtie.rb +62 -0
  54. data/active_model_serializers-0.10.16/lib/active_model_serializers/register_jsonapi_renderer.rb +80 -0
  55. data/active_model_serializers-0.10.16/lib/active_model_serializers/serializable_resource.rb +84 -0
  56. data/active_model_serializers-0.10.16/lib/active_model_serializers/serialization_context.rb +41 -0
  57. data/active_model_serializers-0.10.16/lib/active_model_serializers/test/schema.rb +140 -0
  58. data/active_model_serializers-0.10.16/lib/active_model_serializers/test/serializer.rb +127 -0
  59. data/active_model_serializers-0.10.16/lib/active_model_serializers/test.rb +9 -0
  60. data/active_model_serializers-0.10.16/lib/active_model_serializers.rb +63 -0
  61. data/active_model_serializers-0.10.16/lib/generators/rails/USAGE +6 -0
  62. data/active_model_serializers-0.10.16/lib/generators/rails/resource_override.rb +12 -0
  63. data/active_model_serializers-0.10.16/lib/generators/rails/serializer_generator.rb +38 -0
  64. data/active_model_serializers-0.10.16/lib/generators/rails/templates/serializer.rb.erb +8 -0
  65. data/active_model_serializers-0.10.16/lib/grape/active_model_serializers.rb +18 -0
  66. data/active_model_serializers-0.10.16/lib/grape/formatters/active_model_serializers.rb +34 -0
  67. data/active_model_serializers-0.10.16/lib/grape/helpers/active_model_serializers.rb +19 -0
  68. data/active_model_serializers-0.10.16/lib/tasks/rubocop.rake +60 -0
  69. data/tiny-max-kit.gemspec +12 -0
  70. metadata +109 -0
@@ -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
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class JsonApi < Base
6
+ class PaginationLinks
7
+ MissingSerializationContextError = Class.new(KeyError)
8
+ FIRST_PAGE = 1
9
+
10
+ attr_reader :collection, :context
11
+
12
+ def initialize(collection, adapter_options)
13
+ @collection = collection
14
+ @adapter_options = adapter_options
15
+ @context = adapter_options.fetch(:serialization_context) do
16
+ fail MissingSerializationContextError, <<-EOF.freeze
17
+ JsonApi::PaginationLinks requires a ActiveModelSerializers::SerializationContext.
18
+ Please pass a ':serialization_context' option or
19
+ override CollectionSerializer#paginated? to return 'false'.
20
+ EOF
21
+ end
22
+ end
23
+
24
+ def as_json
25
+ {
26
+ self: location_url,
27
+ first: first_page_url,
28
+ prev: prev_page_url,
29
+ next: next_page_url,
30
+ last: last_page_url
31
+ }
32
+ end
33
+
34
+ protected
35
+
36
+ attr_reader :adapter_options
37
+
38
+ private
39
+
40
+ def location_url
41
+ url_for_page(collection.current_page)
42
+ end
43
+
44
+ def first_page_url
45
+ url_for_page(1)
46
+ end
47
+
48
+ def last_page_url
49
+ if collection.total_pages == 0
50
+ url_for_page(FIRST_PAGE)
51
+ else
52
+ url_for_page(collection.total_pages)
53
+ end
54
+ end
55
+
56
+ def prev_page_url
57
+ return nil if collection.current_page == FIRST_PAGE
58
+ if collection.current_page > collection.total_pages
59
+ return url_for_page(collection.total_pages)
60
+ end
61
+ url_for_page(collection.current_page - FIRST_PAGE)
62
+ end
63
+
64
+ def next_page_url
65
+ return nil if collection.total_pages == 0 ||
66
+ collection.current_page >= collection.total_pages
67
+ url_for_page(collection.next_page)
68
+ end
69
+
70
+ def url_for_page(number)
71
+ params = query_parameters.dup
72
+ params[:page] = { size: per_page, number: number }
73
+ "#{url(adapter_options)}?#{params.to_query}"
74
+ end
75
+
76
+ def url(options = {})
77
+ @url ||= options.fetch(:links, {}).fetch(:self, nil) || request_url
78
+ end
79
+
80
+ def request_url
81
+ @request_url ||= context.request_url
82
+ end
83
+
84
+ def query_parameters
85
+ @query_parameters ||= context.query_parameters
86
+ end
87
+
88
+ def per_page
89
+ @per_page ||= collection.try(:per_page) || collection.try(:limit_value) || collection.size
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class JsonApi
6
+ class Relationship
7
+ # {http://jsonapi.org/format/#document-resource-object-related-resource-links Document Resource Object Related Resource Links}
8
+ # {http://jsonapi.org/format/#document-links Document Links}
9
+ # {http://jsonapi.org/format/#document-resource-object-linkage Document Resource Relationship Linkage}
10
+ # {http://jsonapi.org/format/#document-meta Document Meta}
11
+ def initialize(parent_serializer, serializable_resource_options, association)
12
+ @parent_serializer = parent_serializer
13
+ @association = association
14
+ @serializable_resource_options = serializable_resource_options
15
+ end
16
+
17
+ def as_json
18
+ hash = {}
19
+
20
+ hash[:data] = data_for(association) if association.include_data?
21
+
22
+ links = links_for(association)
23
+ hash[:links] = links if links.any?
24
+
25
+ meta = meta_for(association)
26
+ hash[:meta] = meta if meta
27
+ hash[:meta] = {} if hash.empty?
28
+
29
+ hash
30
+ end
31
+
32
+ protected
33
+
34
+ attr_reader :parent_serializer, :serializable_resource_options, :association
35
+
36
+ private
37
+
38
+ # TODO(BF): Avoid db hit on belong_to_ releationship by using foreign_key on self
39
+ def data_for(association)
40
+ if association.collection?
41
+ data_for_many(association)
42
+ else
43
+ data_for_one(association)
44
+ end
45
+ end
46
+
47
+ def data_for_one(association)
48
+ if belongs_to_id_on_self?(association)
49
+ id = parent_serializer.read_attribute_for_serialization(association.reflection.foreign_key)
50
+ type =
51
+ if association.polymorphic?
52
+ # We can't infer resource type for polymorphic relationships from the serializer.
53
+ # We can ONLY know a polymorphic resource type by inspecting each resource.
54
+ association.lazy_association.serializer.json_key
55
+ else
56
+ association.reflection.type.to_s
57
+ end
58
+ ResourceIdentifier.for_type_with_id(type, id, serializable_resource_options)
59
+ else
60
+ # TODO(BF): Process relationship without evaluating lazy_association
61
+ serializer = association.lazy_association.serializer
62
+ if (virtual_value = association.virtual_value)
63
+ virtual_value
64
+ elsif serializer && association.object
65
+ ResourceIdentifier.new(serializer, serializable_resource_options).as_json
66
+ else
67
+ nil
68
+ end
69
+ end
70
+ end
71
+
72
+ def data_for_many(association)
73
+ # TODO(BF): Process relationship without evaluating lazy_association
74
+ collection_serializer = association.lazy_association.serializer
75
+ if collection_serializer.respond_to?(:each)
76
+ collection_serializer.map do |serializer|
77
+ ResourceIdentifier.new(serializer, serializable_resource_options).as_json
78
+ end
79
+ elsif (virtual_value = association.virtual_value)
80
+ virtual_value
81
+ else
82
+ []
83
+ end
84
+ end
85
+
86
+ def links_for(association)
87
+ association.links.each_with_object({}) do |(key, value), hash|
88
+ result = Link.new(parent_serializer, value).as_json
89
+ hash[key] = result if result
90
+ end
91
+ end
92
+
93
+ def meta_for(association)
94
+ meta = association.meta
95
+ meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
96
+ end
97
+
98
+ def belongs_to_id_on_self?(association)
99
+ parent_serializer.config.jsonapi_use_foreign_key_on_belongs_to_relationship &&
100
+ association.belongs_to? &&
101
+ parent_serializer.object.respond_to?(association.reflection.foreign_key)
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end