jsonapi-realizer 6.0.0.rc3 → 6.0.0
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 +4 -4
- data/LICENSE +440 -0
- data/README.md +2 -12
- data/Rakefile +1 -0
- data/lib/jsonapi/realizer/action.rb +80 -74
- data/lib/jsonapi/realizer/adapter/active_record.rb +11 -9
- data/lib/jsonapi/realizer/adapter.rb +7 -7
- data/lib/jsonapi/realizer/adapter_spec.rb +2 -4
- data/lib/jsonapi/realizer/configuration.rb +2 -0
- data/lib/jsonapi/realizer/context.rb +2 -0
- data/lib/jsonapi/realizer/controller.rb +4 -2
- data/lib/jsonapi/realizer/error/include_without_data_property.rb +2 -1
- data/lib/jsonapi/realizer/error/invalid_content_type_header.rb +2 -0
- data/lib/jsonapi/realizer/error/invalid_data_type_property.rb +2 -0
- data/lib/jsonapi/realizer/error/invalid_root_property.rb +2 -0
- data/lib/jsonapi/realizer/error/missing_content_type_header.rb +2 -1
- data/lib/jsonapi/realizer/error/missing_data_type_property.rb +2 -1
- data/lib/jsonapi/realizer/error/missing_root_property.rb +2 -1
- data/lib/jsonapi/realizer/error/resource_attribute_not_found.rb +2 -0
- data/lib/jsonapi/realizer/error/resource_relationship_not_found.rb +2 -0
- data/lib/jsonapi/realizer/error.rb +2 -0
- data/lib/jsonapi/realizer/resource/attribute.rb +2 -0
- data/lib/jsonapi/realizer/resource/configuration.rb +2 -0
- data/lib/jsonapi/realizer/resource/relation.rb +2 -0
- data/lib/jsonapi/realizer/resource.rb +181 -182
- data/lib/jsonapi/realizer/resource_spec.rb +50 -9
- data/lib/jsonapi/realizer/version.rb +3 -1
- data/lib/jsonapi/realizer.rb +7 -5
- data/lib/jsonapi/realizer_spec.rb +2 -28
- data/lib/jsonapi-realizer.rb +2 -0
- metadata +24 -204
- data/lib/jsonapi/realizer/version_spec.rb +0 -7
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module JSONAPI
|
2
4
|
module Realizer
|
3
5
|
module Resource
|
@@ -8,11 +10,11 @@ module JSONAPI
|
|
8
10
|
extend(ActiveSupport::Concern)
|
9
11
|
include(ActiveModel::Model)
|
10
12
|
|
11
|
-
MIXIN_HOOK =
|
13
|
+
MIXIN_HOOK = lambda do |*|
|
12
14
|
@attributes = {}
|
13
15
|
@relations = {}
|
14
16
|
|
15
|
-
unless const_defined?(
|
17
|
+
unless const_defined?(:Context)
|
16
18
|
self::Context = Class.new do
|
17
19
|
include(JSONAPI::Realizer::Context)
|
18
20
|
|
@@ -25,8 +27,8 @@ module JSONAPI
|
|
25
27
|
end
|
26
28
|
|
27
29
|
validates_presence_of(:intent)
|
28
|
-
validates_presence_of(:parameters, :
|
29
|
-
validates_presence_of(:headers, :
|
30
|
+
validates_presence_of(:parameters, allow_empty: true)
|
31
|
+
validates_presence_of(:headers, allow_empty: true)
|
30
32
|
|
31
33
|
identifier(JSONAPI::Realizer.configuration.default_identifier)
|
32
34
|
|
@@ -38,7 +40,7 @@ module JSONAPI
|
|
38
40
|
attr_accessor(:parameters)
|
39
41
|
attr_accessor(:headers)
|
40
42
|
attr_writer(:context)
|
41
|
-
|
43
|
+
attr_writer(:scope)
|
42
44
|
|
43
45
|
def initialize(**keyword_arguments)
|
44
46
|
super(**keyword_arguments)
|
@@ -46,44 +48,30 @@ module JSONAPI
|
|
46
48
|
context.validate!
|
47
49
|
validate!
|
48
50
|
|
49
|
-
if filtering?
|
50
|
-
@scope = adapter.filtering(scope, filters)
|
51
|
-
end
|
51
|
+
@scope = adapter.filtering(scope, filters) if filtering?
|
52
52
|
|
53
|
-
if include?
|
54
|
-
@scope = adapter.include_relationships(scope, includes)
|
55
|
-
end
|
53
|
+
@scope = adapter.include_relationships(scope, includes) if include?
|
56
54
|
|
57
|
-
if sorting?
|
58
|
-
@scope = adapter.sorting(scope, sorts)
|
59
|
-
end
|
55
|
+
@scope = adapter.sorting(scope, sorts) if sorting?
|
60
56
|
|
61
|
-
if paginate?
|
62
|
-
@scope = adapter.paginate(scope, *pagination)
|
63
|
-
end
|
57
|
+
@scope = adapter.paginate(scope, *pagination) if paginate?
|
64
58
|
|
65
|
-
if writing? && data? && attributes?
|
66
|
-
adapter.write_attributes(object, attributes)
|
67
|
-
end
|
59
|
+
adapter.write_attributes(object, attributes) if writing? && data? && attributes?
|
68
60
|
|
69
|
-
|
70
|
-
|
71
|
-
|
61
|
+
return unless writing? && data? && relationships?
|
62
|
+
|
63
|
+
adapter.write_relationships(object, relationships)
|
72
64
|
end
|
73
65
|
|
74
66
|
def to_hash
|
75
|
-
@
|
76
|
-
:pagination
|
77
|
-
:selects
|
78
|
-
:includes
|
79
|
-
:
|
67
|
+
@to_hash ||= {
|
68
|
+
pagination: (pagination if paginate?),
|
69
|
+
selects: (selects if selects?),
|
70
|
+
includes: (includes if include?),
|
71
|
+
object:
|
80
72
|
}.compact
|
81
73
|
end
|
82
74
|
|
83
|
-
private def writing?
|
84
|
-
[:create, :update].include?(intent)
|
85
|
-
end
|
86
|
-
|
87
75
|
def paginate?
|
88
76
|
parameters.key?("page") && (parameters.fetch("page").key?("limit") || parameters.fetch("page").key?("offset"))
|
89
77
|
end
|
@@ -101,19 +89,19 @@ module JSONAPI
|
|
101
89
|
|
102
90
|
def sorts
|
103
91
|
@sorts ||= parameters.
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
92
|
+
# {sort: "name,-age,accounts.created_at,-accounts.updated_at"}
|
93
|
+
fetch("sort").
|
94
|
+
# "name,-age,accounts.created_at,-accounts.updated_at"
|
95
|
+
split(",").
|
96
|
+
# ["name", "-age", "accounts.created_at", "-accounts.updated_at"]
|
97
|
+
map do |token|
|
98
|
+
token.start_with?("-") ? [token.sub(/^-/, "").underscore, "-"] : [token.underscore, "+"]
|
99
|
+
end.
|
100
|
+
# [["name", "+"], ["age", "-"], ["accounts.created_at", "+"], ["accounts.updated_at", "-"]]
|
101
|
+
map do |(path, direction)|
|
102
|
+
[path.include?(".") ? path.split(".") : [self.class.configuration.type, path], direction]
|
103
|
+
end
|
104
|
+
# [[["accounts", "name"], "+"], [["accounts", "age"], "-"], [["accounts", "created_at"], "+"], [["accounts", "updated_at"], "-"]]
|
117
105
|
end
|
118
106
|
|
119
107
|
def filtering?
|
@@ -122,11 +110,11 @@ module JSONAPI
|
|
122
110
|
|
123
111
|
def filters
|
124
112
|
@filters ||= parameters.
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
113
|
+
# {"filter" => {"full-name" => "Abby Marquardt", "email" => "amado@goldner.com"}}
|
114
|
+
fetch("filter").
|
115
|
+
# {"full-name" => "Abby Marquardt", "email" => "amado@goldner.com"}
|
116
|
+
transform_keys(&:underscore)
|
117
|
+
# {"full_name" => "Abby Marquardt", "email" => "amado@goldner.com"}
|
130
118
|
end
|
131
119
|
|
132
120
|
def include?
|
@@ -135,30 +123,30 @@ module JSONAPI
|
|
135
123
|
|
136
124
|
def includes
|
137
125
|
@includes ||= parameters.
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
126
|
+
# {"include" => "active-photographer.photographs,comments,comments.author"}
|
127
|
+
fetch("include").
|
128
|
+
# "active-photographer.photographs,comments,comments.author"
|
129
|
+
split(/\s*,\s*/).
|
130
|
+
# ["active-photographer.photographs", "comments", "comments.author"]
|
131
|
+
map { |chain| chain.split(".") }.
|
132
|
+
# [["active-photographer", "photographs"], ["comments"], ["comments", "author"]]
|
133
|
+
map { |list| list.map(&:underscore) }.
|
134
|
+
# [["active_photographer", "photographs"], ["comments"], ["comments", "author"]]
|
135
|
+
map do |relationship_chain|
|
136
|
+
# This walks down the path of relationships and normalizes thenm to
|
137
|
+
# their defined "as", which lets us expose AccountRealizer#name, but that actually
|
138
|
+
# references Account#full_name.
|
139
|
+
relationship_chain.reduce([[], self.class]) do |(normalized_relationship_chain, realizer_class), relationship_link|
|
140
|
+
[
|
152
141
|
[
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
# [["account", "photographs"], ["comments"], ["comments", "account"]]
|
142
|
+
*normalized_relationship_chain,
|
143
|
+
realizer_class.relation(relationship_link).as
|
144
|
+
],
|
145
|
+
realizer_class.relation(relationship_link).realizer_class
|
146
|
+
]
|
147
|
+
end.first
|
148
|
+
end
|
149
|
+
# [["account", "photographs"], ["comments"], ["comments", "account"]]
|
162
150
|
end
|
163
151
|
|
164
152
|
def selects?
|
@@ -167,107 +155,53 @@ module JSONAPI
|
|
167
155
|
|
168
156
|
def selects
|
169
157
|
@selects ||= parameters.
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
end
|
180
|
-
|
181
|
-
private def data?
|
182
|
-
parameters.key?("data")
|
183
|
-
end
|
184
|
-
|
185
|
-
private def data
|
186
|
-
@data ||= parameters.fetch("data")
|
187
|
-
end
|
188
|
-
|
189
|
-
private def type
|
190
|
-
return unless data.key?("type")
|
191
|
-
|
192
|
-
@type ||= data.fetch("type")
|
193
|
-
end
|
194
|
-
|
195
|
-
private def attributes?
|
196
|
-
data.key?("attributes")
|
158
|
+
# {"fields" => {"articles" => "title,body,sub-text", "people" => "name"}}
|
159
|
+
fetch("fields").
|
160
|
+
# {"articles" => "title,body,sub-text", "people" => "name"}
|
161
|
+
transform_keys(&:underscore).
|
162
|
+
# {"articles" => "title,body,sub-text", "people" => "name"}
|
163
|
+
transform_values { |value| value.split(/\s*,\s*/) }.
|
164
|
+
# {"articles" => ["title", "body", "sub-text"], "people" => ["name"]}
|
165
|
+
transform_values { |value| value.map(&:underscore) }
|
166
|
+
# {"articles" => ["title", "body", "sub_text"], "people" => ["name"]}
|
197
167
|
end
|
198
168
|
|
199
169
|
def attributes
|
200
170
|
return unless data.key?("attributes")
|
201
171
|
|
202
|
-
@attributes ||= data
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
end
|
207
|
-
|
208
|
-
private def relationships?
|
209
|
-
data.key?("relationships")
|
172
|
+
@attributes ||= data
|
173
|
+
.fetch("attributes")
|
174
|
+
.transform_keys(&:underscore)
|
175
|
+
.transform_keys { |key| attribute(key).as }
|
210
176
|
end
|
211
177
|
|
212
178
|
def relationships
|
213
179
|
return unless data.key?("relationships")
|
214
180
|
|
215
|
-
@relationships ||= data
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
end
|
221
|
-
|
222
|
-
private def scope
|
223
|
-
@scope ||= adapter.find_many(@scope || model_class)
|
181
|
+
@relationships ||= data
|
182
|
+
.fetch("relationships")
|
183
|
+
.transform_keys(&:underscore)
|
184
|
+
.map(&method(:as_relationship)).to_h
|
185
|
+
.transform_keys { |key| relation(key).as }
|
224
186
|
end
|
225
187
|
|
226
188
|
def object
|
227
189
|
@object ||= case intent
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
190
|
+
when :create
|
191
|
+
scope.new
|
192
|
+
when :show, :update, :destroy
|
193
|
+
adapter.find_one(scope, parameters.fetch("id"))
|
194
|
+
else
|
195
|
+
scope
|
196
|
+
end
|
235
197
|
end
|
236
198
|
|
237
199
|
def intent
|
238
200
|
@intent.to_sym
|
239
201
|
end
|
240
202
|
|
241
|
-
private def as_relationship(name, value)
|
242
|
-
data = value.fetch("data")
|
243
|
-
|
244
|
-
relation_configuration = relation(name).realizer_class.configuration
|
245
|
-
|
246
|
-
if data.is_a?(Array)
|
247
|
-
[name, relation_configuration.adapter.find_many(relation_configuration.model_class, {id: data.map {|value| value.fetch("id")}})]
|
248
|
-
else
|
249
|
-
[name, relation_configuration.adapter.find_one(relation_configuration.model_class, data.fetch("id"))]
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
private def attribute(name)
|
254
|
-
self.class.attribute(name)
|
255
|
-
end
|
256
|
-
|
257
|
-
private def relation(name)
|
258
|
-
self.class.relation(name)
|
259
|
-
end
|
260
|
-
|
261
|
-
private def adapter
|
262
|
-
self.class.configuration.adapter
|
263
|
-
end
|
264
|
-
|
265
|
-
private def model_class
|
266
|
-
self.class.configuration.model_class
|
267
|
-
end
|
268
|
-
|
269
203
|
def context
|
270
|
-
self.class.const_get(
|
204
|
+
self.class.const_get(:Context).new(**@context || {})
|
271
205
|
end
|
272
206
|
|
273
207
|
included do
|
@@ -276,6 +210,7 @@ module JSONAPI
|
|
276
210
|
|
277
211
|
class_methods do
|
278
212
|
def inherited(object)
|
213
|
+
super
|
279
214
|
object.class_eval(&MIXIN_HOOK) unless object.instance_variable_defined?(:@abstract_class)
|
280
215
|
end
|
281
216
|
|
@@ -284,62 +219,126 @@ module JSONAPI
|
|
284
219
|
end
|
285
220
|
|
286
221
|
def type(value, class_name:, adapter:)
|
287
|
-
@type
|
288
|
-
@model_class
|
289
|
-
@adapter
|
222
|
+
@type = value.to_s
|
223
|
+
@model_class = class_name.constantize
|
224
|
+
@adapter = JSONAPI::Realizer::Adapter.new(interface: adapter)
|
290
225
|
end
|
291
226
|
|
292
227
|
def has(name, as: name)
|
293
228
|
@attributes[name] ||= Attribute.new(
|
294
|
-
|
295
|
-
|
296
|
-
:
|
229
|
+
name:,
|
230
|
+
as:,
|
231
|
+
owner: self
|
297
232
|
)
|
298
233
|
end
|
299
234
|
|
300
|
-
|
235
|
+
# rubocop:disable Naming/PredicateName
|
236
|
+
def has_one(name, class_name:, as: name)
|
301
237
|
@relations[name] ||= Relation.new(
|
302
|
-
:
|
303
|
-
:
|
304
|
-
|
305
|
-
|
306
|
-
:
|
238
|
+
owner: self,
|
239
|
+
type: :one,
|
240
|
+
name:,
|
241
|
+
as:,
|
242
|
+
realizer_class_name: class_name
|
307
243
|
)
|
308
244
|
end
|
245
|
+
# rubocop:enable Naming/PredicateName
|
309
246
|
|
310
|
-
|
247
|
+
# rubocop:disable Naming/PredicateName
|
248
|
+
def has_many(name, class_name:, as: name)
|
311
249
|
@relations[name] ||= Relation.new(
|
312
|
-
:
|
313
|
-
:
|
314
|
-
|
315
|
-
|
316
|
-
:
|
250
|
+
owner: self,
|
251
|
+
type: :many,
|
252
|
+
name:,
|
253
|
+
as:,
|
254
|
+
realizer_class_name: class_name
|
317
255
|
)
|
318
256
|
end
|
257
|
+
# rubocop:enable Naming/PredicateName
|
319
258
|
|
320
259
|
def context
|
321
|
-
const_get(
|
260
|
+
const_get(:Context)
|
322
261
|
end
|
323
262
|
|
324
263
|
def configuration
|
325
|
-
@configuration ||= Configuration.new(
|
326
|
-
:
|
327
|
-
:
|
328
|
-
:
|
329
|
-
:
|
330
|
-
:
|
331
|
-
:
|
332
|
-
|
264
|
+
@configuration ||= Configuration.new(
|
265
|
+
owner: self,
|
266
|
+
type: @type,
|
267
|
+
model_class: @model_class,
|
268
|
+
adapter: @adapter,
|
269
|
+
attributes: @attributes,
|
270
|
+
relations: @relations
|
271
|
+
)
|
333
272
|
end
|
334
273
|
|
335
274
|
def attribute(name)
|
336
|
-
configuration.attributes.fetch(name.to_sym){raise(Error::ResourceAttributeNotFound, name
|
275
|
+
configuration.attributes.fetch(name.to_sym) { raise(Error::ResourceAttributeNotFound, name:, realizer: self) }
|
337
276
|
end
|
338
277
|
|
339
278
|
def relation(name)
|
340
|
-
configuration.relations.fetch(name.to_sym){raise(Error::ResourceRelationshipNotFound, name
|
279
|
+
configuration.relations.fetch(name.to_sym) { raise(Error::ResourceRelationshipNotFound, name:, realizer: self) }
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
private def writing?
|
284
|
+
%i[create update].include?(intent)
|
285
|
+
end
|
286
|
+
|
287
|
+
private def data?
|
288
|
+
parameters.key?("data")
|
289
|
+
end
|
290
|
+
|
291
|
+
private def data
|
292
|
+
@data ||= parameters.fetch("data")
|
293
|
+
end
|
294
|
+
|
295
|
+
private def type
|
296
|
+
return unless data.key?("type")
|
297
|
+
|
298
|
+
@type ||= data.fetch("type")
|
299
|
+
end
|
300
|
+
|
301
|
+
private def attributes?
|
302
|
+
data.key?("attributes")
|
303
|
+
end
|
304
|
+
|
305
|
+
private def relationships?
|
306
|
+
data.key?("relationships")
|
307
|
+
end
|
308
|
+
|
309
|
+
private def scope
|
310
|
+
@scope ||= adapter.find_many(@scope || model_class)
|
311
|
+
end
|
312
|
+
|
313
|
+
private def as_relationship(name, value)
|
314
|
+
return [name, nil] if value.nil?
|
315
|
+
|
316
|
+
data = value.fetch("data")
|
317
|
+
|
318
|
+
relation_configuration = relation(name).realizer_class.configuration
|
319
|
+
|
320
|
+
if data.is_a?(Array)
|
321
|
+
[name, relation_configuration.adapter.find_many(relation_configuration.model_class, { id: data.map { |value| value.fetch("id") } })]
|
322
|
+
else
|
323
|
+
[name, relation_configuration.adapter.find_one(relation_configuration.model_class, data.fetch("id"))]
|
341
324
|
end
|
342
325
|
end
|
326
|
+
|
327
|
+
private def attribute(name)
|
328
|
+
self.class.attribute(name)
|
329
|
+
end
|
330
|
+
|
331
|
+
private def relation(name)
|
332
|
+
self.class.relation(name)
|
333
|
+
end
|
334
|
+
|
335
|
+
private def adapter
|
336
|
+
self.class.configuration.adapter
|
337
|
+
end
|
338
|
+
|
339
|
+
private def model_class
|
340
|
+
self.class.configuration.model_class
|
341
|
+
end
|
343
342
|
end
|
344
343
|
end
|
345
344
|
end
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require("spec_helper")
|
2
4
|
|
3
5
|
RSpec.describe(JSONAPI::Realizer::Resource) do
|
4
|
-
let(:resource_class) {PhotoRealizer}
|
5
|
-
let(:resource) {resource_class.new(intent
|
6
|
+
let(:resource_class) { PhotoRealizer }
|
7
|
+
let(:resource) { resource_class.new(intent:, parameters:, headers:) }
|
6
8
|
|
7
9
|
describe "#as_native" do
|
8
|
-
|
10
|
+
subject { resource }
|
9
11
|
|
10
12
|
context "when accepting the right type, when creating with data, with spares fields, and includes" do
|
11
|
-
let(:intent) {:create}
|
13
|
+
let(:intent) { :create }
|
12
14
|
let(:parameters) do
|
13
15
|
{
|
14
16
|
"include" => "photographer",
|
@@ -41,23 +43,62 @@ RSpec.describe(JSONAPI::Realizer::Resource) do
|
|
41
43
|
end
|
42
44
|
|
43
45
|
before do
|
44
|
-
Account.create!(:
|
46
|
+
Account.create!(id: 9, name: "Dan Gebhardt", twitter: "dgeb")
|
45
47
|
end
|
46
48
|
|
47
49
|
it "object is a Photo" do
|
48
|
-
expect(subject.object).to
|
50
|
+
expect(subject.object).to be_a(Photo)
|
49
51
|
end
|
50
52
|
|
51
53
|
it "object isn't saved" do
|
52
|
-
expect(subject.object).
|
54
|
+
expect(subject.object).not_to be_persisted
|
53
55
|
end
|
54
56
|
|
55
57
|
it "object has the right attributes" do
|
56
58
|
expect(subject.object).to have_attributes(
|
57
|
-
:
|
58
|
-
:
|
59
|
+
title: "Ember Hamster",
|
60
|
+
src: "http://example.com/images/productivity.png"
|
59
61
|
)
|
60
62
|
end
|
63
|
+
|
64
|
+
it "has a photographer" do
|
65
|
+
expect(subject.object.photographer).not_to be_nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when specifying nil relationship" do
|
70
|
+
let(:intent) { :update }
|
71
|
+
let(:parameters) do
|
72
|
+
{
|
73
|
+
"id" => "11",
|
74
|
+
"data" => {
|
75
|
+
"id" => "11",
|
76
|
+
"type" => "photos",
|
77
|
+
"relationships" => {
|
78
|
+
"photographer" => nil
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
end
|
83
|
+
let(:headers) do
|
84
|
+
{
|
85
|
+
"Accept" => "application/vnd.api+json",
|
86
|
+
"Content-Type" => "application/vnd.api+json"
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
before do
|
91
|
+
account = Account.create!(id: 9, name: "Dan Gebhardt", twitter: "dgeb")
|
92
|
+
Photo.create!(id: 11, photographer: account, title: "Ember Hamster", src: "http://example.com/images/productivity.png")
|
93
|
+
end
|
94
|
+
|
95
|
+
it "object is a Photo" do
|
96
|
+
expect(subject.object).to be_a(Photo)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "clears relationship on realizing nil" do
|
100
|
+
expect(subject.object.photographer).to be_nil
|
101
|
+
end
|
61
102
|
end
|
62
103
|
end
|
63
104
|
end
|
data/lib/jsonapi/realizer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require("ostruct")
|
2
4
|
require("addressable")
|
3
5
|
require("active_model")
|
@@ -7,7 +9,7 @@ require("active_support/core_ext/string")
|
|
7
9
|
require("active_support/core_ext/module")
|
8
10
|
|
9
11
|
module JSONAPI
|
10
|
-
MEDIA_TYPE = "application/vnd.api+json" unless const_defined?(
|
12
|
+
MEDIA_TYPE = "application/vnd.api+json" unless const_defined?(:MEDIA_TYPE)
|
11
13
|
|
12
14
|
module Realizer
|
13
15
|
require_relative("realizer/version")
|
@@ -16,10 +18,10 @@ module JSONAPI
|
|
16
18
|
require_relative("realizer/controller")
|
17
19
|
|
18
20
|
@configuration ||= Configuration.new(
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
21
|
+
default_invalid_content_type_exception: JSONAPI::Realizer::Error::InvalidContentTypeHeader,
|
22
|
+
default_missing_content_type_exception: JSONAPI::Realizer::Error::MissingContentTypeHeader,
|
23
|
+
default_identifier: :id,
|
24
|
+
adapter_mappings: {}
|
23
25
|
)
|
24
26
|
|
25
27
|
require_relative("realizer/adapter")
|
@@ -1,29 +1,3 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
let(:parameters) {
|
5
|
-
{
|
6
|
-
"data" => {
|
7
|
-
"type" => "photos",
|
8
|
-
"attributes" => {
|
9
|
-
"title" => "Ember Hamster",
|
10
|
-
"src" => "http://example.com/images/productivity.png"
|
11
|
-
},
|
12
|
-
"relationships" => {
|
13
|
-
"photographer" => {
|
14
|
-
"data" => {
|
15
|
-
"type" => "people",
|
16
|
-
"id" => "9"
|
17
|
-
}
|
18
|
-
}
|
19
|
-
}
|
20
|
-
}
|
21
|
-
}
|
22
|
-
}
|
23
|
-
let(:headers) {
|
24
|
-
{
|
25
|
-
"Accept" => "application/vnd.api+json",
|
26
|
-
"Content-Type" => "application/vnd.api+json"
|
27
|
-
}
|
28
|
-
}
|
29
|
-
end
|
3
|
+
require "spec_helper"
|