jsonapi-realizer 1.0.0 → 2.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/README.md +53 -47
- data/lib/jsonapi/realizer.rb +30 -8
- data/lib/jsonapi/realizer/action.rb +86 -12
- data/lib/jsonapi/realizer/action/create.rb +13 -8
- data/lib/jsonapi/realizer/action/create_spec.rb +61 -10
- data/lib/jsonapi/realizer/action/index.rb +20 -0
- data/lib/jsonapi/realizer/action/index_spec.rb +42 -0
- data/lib/jsonapi/realizer/action/show.rb +21 -0
- data/lib/jsonapi/realizer/action/show_spec.rb +48 -0
- data/lib/jsonapi/realizer/action/update.rb +12 -12
- data/lib/jsonapi/realizer/action/update_spec.rb +60 -10
- data/lib/jsonapi/realizer/adapter.rb +78 -17
- data/lib/jsonapi/realizer/adapter/active_record.rb +35 -13
- data/lib/jsonapi/realizer/adapter/memory.rb +34 -15
- data/lib/jsonapi/realizer/adapter_spec.rb +6 -2
- data/lib/jsonapi/realizer/resource.rb +35 -53
- data/lib/jsonapi/realizer/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38b5321aa71d6cd99b39ddb9fb9a3e09679239a8eed202403a82fe0191d1eb06
|
4
|
+
data.tar.gz: e0ff4d86639cef636a852196af3cca83d10e335eda4bbaf322e7b79f63da56f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6bfa065c4d7b383210a0be470f5d95e497da056e4df74beb10ced42920d6e66c09d9b9025c2397a98bad14268d330faaa021320b8fbae0d8144be5d77c3d095b
|
7
|
+
data.tar.gz: 9e2c5b864c0b3d067a88863bdc23f3bf839ecb3bb1da93b0423948f02e0b2dc8b8789cecb4bd1d677055ac0c83d5f25eb5c50b6bed7a60e8044dd2c299dfca3e
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ This library handles incoming [json:api](https://www.jsonapi.org) payloads and t
|
|
10
10
|
A successful JSON:API request can be annotated as:
|
11
11
|
|
12
12
|
```
|
13
|
-
JSONAPIRequest -> (
|
13
|
+
JSONAPIRequest -> (BusinessLayer -> JSONAPIRequest -> (Record | Array<Record>)) -> JSONAPIResponse
|
14
14
|
```
|
15
15
|
|
16
16
|
The `jsonapi-serializers` library provides this shape:
|
@@ -22,12 +22,14 @@ JSONAPIRequest -> (Record | Array<Record>) -> JSONAPIResponse
|
|
22
22
|
But it leaves fetching/createing/updating/destroying the records up to you! This is where jsonapi-realizer comes into play, as it provides this shape:
|
23
23
|
|
24
24
|
```
|
25
|
-
|
25
|
+
BusinessLayer -> JSONAPIRequest -> (Record | Array<Record>)
|
26
26
|
```
|
27
27
|
|
28
28
|
|
29
29
|
## Using
|
30
30
|
|
31
|
+
In order to use this library you'll want to have some models:
|
32
|
+
|
31
33
|
``` ruby
|
32
34
|
class Photo < ApplicationRecord
|
33
35
|
belongs_to :photographer, class_name: "Profile"
|
@@ -36,13 +38,15 @@ end
|
|
36
38
|
class Profile < ApplicationRecord
|
37
39
|
has_many :photos
|
38
40
|
end
|
41
|
+
```
|
42
|
+
|
43
|
+
*They don't have to be ActiveRecord* models, but we have built-in support for that library (adapter-based). Second you'll need some realizers:
|
39
44
|
|
45
|
+
``` ruby
|
40
46
|
class PhotoRealizer
|
41
47
|
include JSONAPI::Realizer::Resource
|
42
48
|
|
43
|
-
adapter :active_record
|
44
|
-
|
45
|
-
represents :photos, class_name: "Photo"
|
49
|
+
register :photos, class_name: "Photo", adapter: :active_record
|
46
50
|
|
47
51
|
has_one :photographer, as: :profiles
|
48
52
|
|
@@ -53,9 +57,7 @@ end
|
|
53
57
|
class ProfileRealizer
|
54
58
|
include JSONAPI::Realizer::Resource
|
55
59
|
|
56
|
-
adapter :active_record
|
57
|
-
|
58
|
-
represents :profiles, class_name: "Profile"
|
60
|
+
register :profiles, class_name: "Profile", adapter: :active_record
|
59
61
|
|
60
62
|
has_many :photos, as: :photos
|
61
63
|
|
@@ -63,6 +65,14 @@ class ProfileRealizer
|
|
63
65
|
end
|
64
66
|
```
|
65
67
|
|
68
|
+
You can define special properties on attributes and relationships realizers:
|
69
|
+
|
70
|
+
``` ruby
|
71
|
+
has_many :doctors, as: :users, includable: false
|
72
|
+
|
73
|
+
has :title, selectable: false
|
74
|
+
```
|
75
|
+
|
66
76
|
Once you've designed your resources, we just need to use them! In this example, we'll use controllers from Rails:
|
67
77
|
|
68
78
|
``` ruby
|
@@ -71,11 +81,24 @@ class PhotosController < ApplicationController
|
|
71
81
|
validate_parameters!
|
72
82
|
authenticate_session!
|
73
83
|
|
74
|
-
|
84
|
+
realization = JSONAPI::Realizer.create(params, headers: request.headers)
|
85
|
+
|
86
|
+
ProcessPhotosService.new(realization.model)
|
87
|
+
|
88
|
+
render json: JSONAPI::Serializer.serialize(record)
|
89
|
+
end
|
90
|
+
|
91
|
+
def index
|
92
|
+
validate_parameters!
|
93
|
+
authenticate_session!
|
94
|
+
|
95
|
+
realization = JSONAPI::Realizer.index(params, headers: request.headers, type: :photos)
|
75
96
|
|
76
|
-
|
97
|
+
# See: pundit for `authorize()`
|
98
|
+
authorize realization.models
|
77
99
|
|
78
|
-
|
100
|
+
# See: pundit for `policy_scope()`
|
101
|
+
render json: JSONAPI::Serializer.serialize(policy_scope(record), is_collection: true)
|
79
102
|
end
|
80
103
|
end
|
81
104
|
```
|
@@ -89,9 +112,14 @@ There are two core adapters:
|
|
89
112
|
|
90
113
|
An adapter must provide the following interfaces:
|
91
114
|
|
92
|
-
0. `find_via`,
|
93
|
-
0. `
|
94
|
-
0. `
|
115
|
+
0. `find_via`, describes how to find the model
|
116
|
+
0. `find_many_via`, describes how to find many models
|
117
|
+
0. `assign_attributes_via`, describes how to write a set of properties
|
118
|
+
0. `assign_relationships_via`, describes how to write a set of relationships
|
119
|
+
0. `create_via`, describes how to create the model
|
120
|
+
0. `update_via`, describes how to update the model
|
121
|
+
0. `includes_via`, describes how to eager include related models
|
122
|
+
0. `sparse_fields_via`, describes how to only return certain fields
|
95
123
|
|
96
124
|
You can also provide custom adapter interfaces:
|
97
125
|
|
@@ -99,43 +127,20 @@ You can also provide custom adapter interfaces:
|
|
99
127
|
class PhotoRealizer
|
100
128
|
include JSONAPI::Realizer::Resource
|
101
129
|
|
102
|
-
adapter
|
103
|
-
find_via do |model_class, id|
|
104
|
-
model_class.where { id == id or slug == id }.first
|
105
|
-
end
|
106
|
-
|
107
|
-
write_attributes_via do |model, attributes|
|
108
|
-
model.update_columns(attributes)
|
109
|
-
end
|
130
|
+
register :photos, class_name: "Photo", adapter: :active_record
|
110
131
|
|
111
|
-
|
112
|
-
|
113
|
-
Rails.cache.write(model.cache_key, model)
|
114
|
-
end
|
132
|
+
adapter.find_via do |model_class, id|
|
133
|
+
model_class.where { id == id or slug == id }.first
|
115
134
|
end
|
116
135
|
|
117
|
-
|
118
|
-
|
119
|
-
has_one :photographer, as: :profiles
|
120
|
-
|
121
|
-
has :title
|
122
|
-
has :src
|
123
|
-
end
|
124
|
-
```
|
125
|
-
|
126
|
-
If you want, you can use both the regular adapters and some custom pieces:
|
127
|
-
|
128
|
-
``` ruby
|
129
|
-
class PhotoRealizer
|
130
|
-
include JSONAPI::Realizer::Resource
|
131
|
-
|
132
|
-
adapter :active_record do
|
133
|
-
find_via do |model_class, id|
|
134
|
-
model_class.where { id == id or slug == id }.first
|
135
|
-
end
|
136
|
+
adapter.assign_attributes_via do |model, attributes|
|
137
|
+
model.update_columns(attributes)
|
136
138
|
end
|
137
139
|
|
138
|
-
|
140
|
+
adapter.create_via do |model|
|
141
|
+
model.save!
|
142
|
+
Rails.cache.write(model.cache_key, model)
|
143
|
+
end
|
139
144
|
|
140
145
|
has_one :photographer, as: :profiles
|
141
146
|
|
@@ -144,11 +149,12 @@ class PhotoRealizer
|
|
144
149
|
end
|
145
150
|
```
|
146
151
|
|
152
|
+
|
147
153
|
## Installing
|
148
154
|
|
149
155
|
Add this line to your application's Gemfile:
|
150
156
|
|
151
|
-
gem "jsonapi-realizer", "
|
157
|
+
gem "jsonapi-realizer", "2.0.0"
|
152
158
|
|
153
159
|
And then execute:
|
154
160
|
|
data/lib/jsonapi/realizer.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "ostruct"
|
2
2
|
require "active_support/concern"
|
3
|
+
require "active_support/core_ext/enumerable"
|
3
4
|
|
4
5
|
module JSONAPI
|
5
6
|
module Realizer
|
@@ -8,23 +9,44 @@ module JSONAPI
|
|
8
9
|
require_relative "realizer/adapter"
|
9
10
|
require_relative "realizer/resource"
|
10
11
|
|
11
|
-
def self.register(resource_class
|
12
|
-
@mapping ||=
|
13
|
-
@mapping
|
14
|
-
model_class: model_class,
|
15
|
-
type: type,
|
12
|
+
def self.register(resource_class:, model_class:, adapter:, type:)
|
13
|
+
@mapping ||= Set.new
|
14
|
+
@mapping << OpenStruct.new({
|
16
15
|
resource_class: resource_class,
|
16
|
+
model_class: model_class,
|
17
|
+
adapter: adapter,
|
18
|
+
type: type.dasherize,
|
17
19
|
attributes: OpenStruct.new({}),
|
18
20
|
relationships: OpenStruct.new({})
|
19
21
|
})
|
20
22
|
end
|
21
23
|
|
22
|
-
def self.
|
23
|
-
@mapping
|
24
|
+
def self.resource_mapping
|
25
|
+
@mapping.index_by(&:resource_class)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.type_mapping
|
29
|
+
@mapping.index_by(&:type)
|
24
30
|
end
|
25
31
|
|
26
32
|
def self.create(payload, headers:)
|
27
|
-
Create.new(payload: payload, headers: headers)
|
33
|
+
enact(Create.new(payload: payload, headers: headers))
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.update(payload, headers:)
|
37
|
+
enact(Update.new(payload: payload, headers: headers))
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.show(payload, headers:, type:)
|
41
|
+
enact(Show.new(payload: payload, headers: headers, type: type))
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.index(payload, headers:, type:)
|
45
|
+
enact(Index.new(payload: payload, headers: headers, type: type))
|
46
|
+
end
|
47
|
+
|
48
|
+
private_class_method def self.inact(action)
|
49
|
+
action.tap(&:call)
|
28
50
|
end
|
29
51
|
end
|
30
52
|
end
|
@@ -3,37 +3,111 @@ module JSONAPI
|
|
3
3
|
class Action
|
4
4
|
require_relative "action/create"
|
5
5
|
require_relative "action/update"
|
6
|
+
require_relative "action/show"
|
7
|
+
require_relative "action/index"
|
6
8
|
|
7
|
-
attr_reader :
|
8
|
-
attr_reader :data
|
9
|
-
attr_reader :resource
|
9
|
+
attr_reader :payload
|
10
10
|
|
11
|
-
def initialize
|
11
|
+
def initialize
|
12
12
|
raise NoMethodError, "must implement this function"
|
13
13
|
end
|
14
14
|
|
15
|
-
def call
|
16
|
-
|
15
|
+
def call; end
|
16
|
+
|
17
|
+
private def model_class
|
18
|
+
resource_class.model_class
|
17
19
|
end
|
18
20
|
|
19
21
|
private def resource_class
|
20
|
-
|
22
|
+
configuration.resource_class
|
21
23
|
end
|
22
24
|
|
23
|
-
private def
|
24
|
-
|
25
|
+
private def adapter
|
26
|
+
configuration.adapter
|
27
|
+
end
|
28
|
+
|
29
|
+
private def relation_after_inclusion(relation)
|
30
|
+
if includes.any?
|
31
|
+
resource_class.include_via_call(relation, includes)
|
32
|
+
else
|
33
|
+
relation
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private def relation_after_fields(relation)
|
38
|
+
if includes.any?
|
39
|
+
resource_class.sparse_fields_call(relation, fields)
|
40
|
+
else
|
41
|
+
relation
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private def relation
|
46
|
+
relation_after_fields(
|
47
|
+
relation_after_inclusion(
|
48
|
+
model_class
|
49
|
+
)
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
private def data
|
54
|
+
payload.fetch("data", {})
|
25
55
|
end
|
26
56
|
|
27
57
|
private def id
|
28
|
-
data.fetch("id", nil)
|
58
|
+
data.fetch("id", nil) || payload.fetch("id", nil)
|
29
59
|
end
|
30
60
|
|
31
61
|
private def type
|
32
|
-
data.fetch("type")
|
62
|
+
(@type || data.fetch("type")).to_s.dasherize
|
33
63
|
end
|
34
64
|
|
35
65
|
private def attributes
|
36
|
-
data.
|
66
|
+
data.
|
67
|
+
fetch("attributes", {}).
|
68
|
+
transform_keys(&:underscore).
|
69
|
+
select(&resource_class.method(:valid_attribute?))
|
70
|
+
end
|
71
|
+
|
72
|
+
private def relationships
|
73
|
+
data.
|
74
|
+
fetch("relationships", {}).
|
75
|
+
transform_keys(&:underscore).
|
76
|
+
select(&resource_class.method(:valid_relationship?)).
|
77
|
+
transform_values(&method(:as_relationship))
|
78
|
+
end
|
79
|
+
|
80
|
+
private def as_relationship(value)
|
81
|
+
data = value.fetch("data")
|
82
|
+
mapping = JSONAPI::Realizer.type_mapping.fetch(data.fetch("type"))
|
83
|
+
mapping.adapter.find_via_call(
|
84
|
+
mapping.model_class,
|
85
|
+
data.fetch("id")
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
private def includes
|
90
|
+
payload.
|
91
|
+
fetch("include", []).
|
92
|
+
# "carts.cart-items,carts.cart-items.product,carts.billing-information,payments"
|
93
|
+
map { |path| path.split(/\s*,\s*/) }.
|
94
|
+
# ["carts.cart-items", "carts.cart-items.product", "carts.billing-information", "payments"]
|
95
|
+
map { |path| path.gsub("-", "_") }.
|
96
|
+
# ["carts.cart_items", "carts.cart_items.product", "carts.billing_information", "payments"]
|
97
|
+
map { |path| path.split(".") }.
|
98
|
+
# [["carts", "cart_items"], ["carts", "cart_items", "product"], ["carts", "billing_information"], ["payments"]]
|
99
|
+
select(&resource_class.method(:valid_includes?))
|
100
|
+
end
|
101
|
+
|
102
|
+
private def fields
|
103
|
+
payload.
|
104
|
+
fetch("fields", []).
|
105
|
+
split(/\s*,\s*/).
|
106
|
+
select(&resource_class.method(:valid_sparse_field?))
|
107
|
+
end
|
108
|
+
|
109
|
+
private def configuration
|
110
|
+
JSONAPI::Realizer.type_mapping.fetch(type)
|
37
111
|
end
|
38
112
|
end
|
39
113
|
end
|
@@ -2,18 +2,23 @@ module JSONAPI
|
|
2
2
|
module Realizer
|
3
3
|
class Action
|
4
4
|
class Create < Action
|
5
|
+
attr_accessor :resource
|
6
|
+
|
5
7
|
def initialize(payload:, headers:)
|
6
|
-
@
|
7
|
-
@
|
8
|
+
@payload = payload
|
9
|
+
@headers = headers
|
10
|
+
@resource = resource_class.new(relation.new)
|
8
11
|
end
|
9
12
|
|
10
13
|
def call
|
11
|
-
resource.model
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
adapter.assign_attributes_via_call(resource.model, {id: id}) if id
|
15
|
+
adapter.assign_attributes_via_call(resource.model, attributes)
|
16
|
+
adapter.assign_relationships_via_call(resource.model, relationships)
|
17
|
+
adapter.create_via_call(resource.model)
|
18
|
+
end
|
19
|
+
|
20
|
+
def model
|
21
|
+
resource.model
|
17
22
|
end
|
18
23
|
end
|
19
24
|
end
|
@@ -6,6 +6,10 @@ RSpec.describe JSONAPI::Realizer::Action::Create do
|
|
6
6
|
describe "#call" do
|
7
7
|
subject { action.call }
|
8
8
|
|
9
|
+
context "with no top-level data" do
|
10
|
+
|
11
|
+
end
|
12
|
+
|
9
13
|
context "with a good payload and good headers" do
|
10
14
|
let(:payload) do
|
11
15
|
{
|
@@ -14,16 +18,18 @@ RSpec.describe JSONAPI::Realizer::Action::Create do
|
|
14
18
|
"type" => "photos",
|
15
19
|
"attributes" => {
|
16
20
|
"title" => "Ember Hamster",
|
21
|
+
"alt-text" => "A hamster logo.",
|
17
22
|
"src" => "http://example.com/images/productivity.png"
|
18
23
|
},
|
19
24
|
"relationships" => {
|
20
|
-
"photographer" => {
|
21
|
-
"data" => { "type" => "people", "id" => "4b8a0af6-953d-4729-8b9a-1fa4eb18f3c9" }
|
25
|
+
"active-photographer" => {
|
26
|
+
"data" => { "type" => "photographer-people", "id" => "4b8a0af6-953d-4729-8b9a-1fa4eb18f3c9" }
|
22
27
|
}
|
23
28
|
}
|
24
29
|
}
|
25
30
|
}
|
26
31
|
end
|
32
|
+
|
27
33
|
let(:headers) do
|
28
34
|
{
|
29
35
|
"Content-Type" => "application/vnd.api+json",
|
@@ -38,20 +44,65 @@ RSpec.describe JSONAPI::Realizer::Action::Create do
|
|
38
44
|
}
|
39
45
|
end
|
40
46
|
|
41
|
-
it "
|
42
|
-
|
47
|
+
it "is the right model" do
|
48
|
+
subject
|
49
|
+
|
50
|
+
expect(action.model).to be_a_kind_of(Photo)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "assigns the id attribute" do
|
54
|
+
subject
|
55
|
+
|
56
|
+
expect(action.model).to have_attributes(id: "550e8400-e29b-41d4-a716-446655440000")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "assigns the title attribute" do
|
60
|
+
subject
|
61
|
+
|
62
|
+
expect(action.model).to have_attributes(title: "Ember Hamster")
|
63
|
+
end
|
64
|
+
|
65
|
+
it "assigns the alt_text attribute" do
|
66
|
+
subject
|
67
|
+
|
68
|
+
expect(action.model).to have_attributes(alt_text: "A hamster logo.")
|
69
|
+
end
|
70
|
+
|
71
|
+
it "assigns the src attribute" do
|
72
|
+
subject
|
73
|
+
|
74
|
+
expect(action.model).to have_attributes(src: "http://example.com/images/productivity.png")
|
43
75
|
end
|
44
76
|
|
45
|
-
it "assigns the
|
46
|
-
|
77
|
+
it "assigns the updated_at attribute" do
|
78
|
+
subject
|
79
|
+
|
80
|
+
expect(action.model).to have_attributes(updated_at: a_kind_of(Time))
|
47
81
|
end
|
48
82
|
|
49
|
-
it "
|
50
|
-
|
83
|
+
it "assigns the active_photographer attribute" do
|
84
|
+
subject
|
85
|
+
|
86
|
+
expect(action.model).to have_attributes(active_photographer: a_kind_of(People))
|
51
87
|
end
|
52
88
|
|
53
|
-
it "
|
54
|
-
expect {
|
89
|
+
it "creates the new record" do
|
90
|
+
expect {
|
91
|
+
subject
|
92
|
+
}.to change {
|
93
|
+
Photo::STORE
|
94
|
+
}.from(
|
95
|
+
{}
|
96
|
+
).to(
|
97
|
+
{
|
98
|
+
"550e8400-e29b-41d4-a716-446655440000" => hash_including(
|
99
|
+
id: "550e8400-e29b-41d4-a716-446655440000",
|
100
|
+
title: "Ember Hamster",
|
101
|
+
alt_text: "A hamster logo.",
|
102
|
+
src: "http://example.com/images/productivity.png"
|
103
|
+
)
|
104
|
+
}
|
105
|
+
)
|
55
106
|
end
|
56
107
|
end
|
57
108
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
module Realizer
|
3
|
+
class Action
|
4
|
+
class Index < Action
|
5
|
+
attr_accessor :resources
|
6
|
+
|
7
|
+
def initialize(payload:, headers:, type:)
|
8
|
+
@payload = payload
|
9
|
+
@headers = headers
|
10
|
+
@type = type
|
11
|
+
@resources = adapter.find_many_via_call(relation).map(&resource_class.method(:new))
|
12
|
+
end
|
13
|
+
|
14
|
+
def models
|
15
|
+
resources.map(&:model)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe JSONAPI::Realizer::Action::Index do
|
4
|
+
let(:action) { described_class.new(payload: payload, headers: headers, type: :photos) }
|
5
|
+
|
6
|
+
describe "#models" do
|
7
|
+
subject { action.models }
|
8
|
+
|
9
|
+
context "with no top-level data" do
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
context "with a good payload and good headers" do
|
14
|
+
let(:payload) do
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
let(:headers) do
|
18
|
+
{
|
19
|
+
"Content-Type" => "application/vnd.api+json",
|
20
|
+
"Accept" => "application/vnd.api+json"
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
before do
|
25
|
+
Photo::STORE["550e8400-e29b-41d4-a716-446655440000"] = {
|
26
|
+
id: "550e8400-e29b-41d4-a716-446655440000",
|
27
|
+
title: "Ember Hamster",
|
28
|
+
src: "http://example.com/images/productivity.png"
|
29
|
+
}
|
30
|
+
Photo::STORE["d09ae4c6-0fc3-4c42-8fe8-6029530c3bed"] = {
|
31
|
+
id: "d09ae4c6-0fc3-4c42-8fe8-6029530c3bed",
|
32
|
+
title: "Ember Fox",
|
33
|
+
src: "http://example.com/images/productivity-2.png"
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns a list of photos" do
|
38
|
+
expect(subject).to include(a_kind_of(Photo), a_kind_of(Photo))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
module Realizer
|
3
|
+
class Action
|
4
|
+
class Show < Action
|
5
|
+
|
6
|
+
attr_accessor :resource
|
7
|
+
|
8
|
+
def initialize(payload:, headers:, type:)
|
9
|
+
@payload = payload
|
10
|
+
@headers = headers
|
11
|
+
@type = type
|
12
|
+
@resource = resource_class.new(adapter.find_via_call(relation, id))
|
13
|
+
end
|
14
|
+
|
15
|
+
def model
|
16
|
+
resource.model
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe JSONAPI::Realizer::Action::Show do
|
4
|
+
let(:action) { described_class.new(payload: payload, headers: headers, type: :photos) }
|
5
|
+
|
6
|
+
describe "#model" do
|
7
|
+
subject { action.model }
|
8
|
+
|
9
|
+
context "with no top-level data" do
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
context "with a good payload and good headers" do
|
14
|
+
let(:payload) do
|
15
|
+
{
|
16
|
+
"id" => "d09ae4c6-0fc3-4c42-8fe8-6029530c3bed"
|
17
|
+
}
|
18
|
+
end
|
19
|
+
let(:headers) do
|
20
|
+
{
|
21
|
+
"Content-Type" => "application/vnd.api+json",
|
22
|
+
"Accept" => "application/vnd.api+json"
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
before do
|
27
|
+
Photo::STORE["550e8400-e29b-41d4-a716-446655440000"] = {
|
28
|
+
id: "550e8400-e29b-41d4-a716-446655440000",
|
29
|
+
title: "Ember Hamster",
|
30
|
+
src: "http://example.com/images/productivity.png"
|
31
|
+
}
|
32
|
+
Photo::STORE["d09ae4c6-0fc3-4c42-8fe8-6029530c3bed"] = {
|
33
|
+
id: "d09ae4c6-0fc3-4c42-8fe8-6029530c3bed",
|
34
|
+
title: "Ember Fox",
|
35
|
+
src: "http://example.com/images/productivity-2.png"
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns a photo model" do
|
40
|
+
expect(subject).to be_a_kind_of(Photo)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns the photos attributes" do
|
44
|
+
expect(subject).to have_attributes(title: "Ember Fox", src: "http://example.com/images/productivity-2.png")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -2,22 +2,22 @@ module JSONAPI
|
|
2
2
|
module Realizer
|
3
3
|
class Action
|
4
4
|
class Update < Action
|
5
|
+
attr_accessor :resource
|
6
|
+
|
5
7
|
def initialize(payload:, headers:)
|
6
|
-
@
|
7
|
-
@
|
8
|
-
|
9
|
-
resource_class.model_class,
|
10
|
-
id
|
11
|
-
)
|
12
|
-
)
|
8
|
+
@payload = payload
|
9
|
+
@headers = headers
|
10
|
+
@resource = resource_class.new(adapter.find_via_call(relation, id))
|
13
11
|
end
|
14
12
|
|
15
13
|
def call
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
adapter.assign_attributes_via_call(resource.model, attributes)
|
15
|
+
adapter.assign_relationships_via_call(resource.model, relationships)
|
16
|
+
adapter.update_via_call(resource.model)
|
17
|
+
end
|
18
|
+
|
19
|
+
def model
|
20
|
+
resource.model
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -6,6 +6,10 @@ RSpec.describe JSONAPI::Realizer::Action::Update do
|
|
6
6
|
describe "#call" do
|
7
7
|
subject { action.call }
|
8
8
|
|
9
|
+
context "with no top-level data" do
|
10
|
+
|
11
|
+
end
|
12
|
+
|
9
13
|
context "with a good payload and good headers" do
|
10
14
|
let(:payload) do
|
11
15
|
{
|
@@ -14,11 +18,12 @@ RSpec.describe JSONAPI::Realizer::Action::Update do
|
|
14
18
|
"type" => "photos",
|
15
19
|
"attributes" => {
|
16
20
|
"title" => "Ember Hamster 2",
|
21
|
+
"alt-text" => "A hamster logo.",
|
17
22
|
"src" => "http://example.com/images/productivity-2.png"
|
18
23
|
},
|
19
24
|
"relationships" => {
|
20
|
-
"photographer" => {
|
21
|
-
"data" => { "type" => "people", "id" => "4b8a0af6-953d-4729-8b9a-1fa4eb18f3c9" }
|
25
|
+
"active-photographer" => {
|
26
|
+
"data" => { "type" => "photographer-people", "id" => "4b8a0af6-953d-4729-8b9a-1fa4eb18f3c9" }
|
22
27
|
}
|
23
28
|
}
|
24
29
|
}
|
@@ -43,20 +48,65 @@ RSpec.describe JSONAPI::Realizer::Action::Update do
|
|
43
48
|
}
|
44
49
|
end
|
45
50
|
|
46
|
-
it "
|
47
|
-
|
51
|
+
it "is the right model" do
|
52
|
+
subject
|
53
|
+
|
54
|
+
expect(action.model).to be_a_kind_of(Photo)
|
48
55
|
end
|
49
56
|
|
50
|
-
it "assigns the
|
51
|
-
|
57
|
+
it "assigns the title attribute" do
|
58
|
+
subject
|
59
|
+
|
60
|
+
expect(action.model).to have_attributes(title: "Ember Hamster 2")
|
52
61
|
end
|
53
62
|
|
54
|
-
it "
|
55
|
-
|
63
|
+
it "assigns the alt_text attribute" do
|
64
|
+
subject
|
65
|
+
|
66
|
+
expect(action.model).to have_attributes(alt_text: "A hamster logo.")
|
67
|
+
end
|
68
|
+
|
69
|
+
it "assigns the src attribute" do
|
70
|
+
subject
|
71
|
+
|
72
|
+
expect(action.model).to have_attributes(src: "http://example.com/images/productivity-2.png")
|
56
73
|
end
|
57
74
|
|
58
|
-
it "
|
59
|
-
|
75
|
+
it "assigns the updated_at attribute" do
|
76
|
+
subject
|
77
|
+
|
78
|
+
expect(action.model).to have_attributes(updated_at: a_kind_of(Time))
|
79
|
+
end
|
80
|
+
|
81
|
+
it "assigns the active_photographer attribute" do
|
82
|
+
subject
|
83
|
+
|
84
|
+
expect(action.model).to have_attributes(active_photographer: a_kind_of(People))
|
85
|
+
end
|
86
|
+
|
87
|
+
it "updates the record" do
|
88
|
+
expect {
|
89
|
+
subject
|
90
|
+
}.to change {
|
91
|
+
Photo::STORE
|
92
|
+
}.from(
|
93
|
+
{
|
94
|
+
"550e8400-e29b-41d4-a716-446655440000" => hash_including(
|
95
|
+
id: "550e8400-e29b-41d4-a716-446655440000",
|
96
|
+
title: "Ember Hamster",
|
97
|
+
src: "http://example.com/images/productivity.png"
|
98
|
+
)
|
99
|
+
}
|
100
|
+
).to(
|
101
|
+
{
|
102
|
+
"550e8400-e29b-41d4-a716-446655440000" => hash_including(
|
103
|
+
id: "550e8400-e29b-41d4-a716-446655440000",
|
104
|
+
title: "Ember Hamster 2",
|
105
|
+
alt_text: "A hamster logo.",
|
106
|
+
src: "http://example.com/images/productivity-2.png"
|
107
|
+
)
|
108
|
+
}
|
109
|
+
)
|
60
110
|
end
|
61
111
|
end
|
62
112
|
end
|
@@ -1,32 +1,93 @@
|
|
1
1
|
module JSONAPI
|
2
2
|
module Realizer
|
3
|
-
|
3
|
+
class Adapter
|
4
4
|
require_relative "adapter/active_record"
|
5
5
|
require_relative "adapter/memory"
|
6
6
|
|
7
7
|
MAPPINGS = {
|
8
|
-
memory: JSONAPI::Realizer::Adapter::
|
9
|
-
active_record: JSONAPI::Realizer::Adapter::
|
8
|
+
memory: JSONAPI::Realizer::Adapter::MEMORY,
|
9
|
+
active_record: JSONAPI::Realizer::Adapter::ACTIVE_RECORD,
|
10
10
|
}
|
11
11
|
|
12
|
-
def
|
13
|
-
if
|
14
|
-
|
15
|
-
resource.include(JSONAPI::Realizer::Adapter::MAPPINGS.fetch(interface))
|
16
|
-
else
|
17
|
-
raise ArgumentError, "you've given an invalid adapter alias: #{interface}, we support #{JSONAPI::Realizer::Adapter::MAPPINGS.keys}"
|
18
|
-
end
|
12
|
+
def initialize(interface)
|
13
|
+
if JSONAPI::Realizer::Adapter::MAPPINGS.key?(interface.to_sym)
|
14
|
+
instance_eval(&JSONAPI::Realizer::Adapter::MAPPINGS.fetch(interface.to_sym))
|
19
15
|
else
|
20
|
-
|
16
|
+
raise ArgumentError, "you've given an invalid adapter alias: #{interface}, we support #{JSONAPI::Realizer::Adapter::MAPPINGS.keys}"
|
21
17
|
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
raise ArgumentError, "need to provide a Adapter.find_via interface" unless instance_variable_defined?(:@find_via_call)
|
20
|
+
raise ArgumentError, "need to provide a Adapter.find_many_via_call interface" unless instance_variable_defined?(:@find_many_via_call)
|
21
|
+
raise ArgumentError, "need to provide a Adapter.assign_attributes_via interface" unless instance_variable_defined?(:@assign_attributes_via_call)
|
22
|
+
raise ArgumentError, "need to provide a Adapter.assign_relationships_via interface" unless instance_variable_defined?(:@assign_relationships_via_call)
|
23
|
+
raise ArgumentError, "need to provide a Adapter.create_via interface" unless instance_variable_defined?(:@create_via_call)
|
24
|
+
raise ArgumentError, "need to provide a Adapter.update_via interface" unless instance_variable_defined?(:@update_via_call)
|
25
|
+
raise ArgumentError, "need to provide a Adapter.sparse_fields interface" unless instance_variable_defined?(:@sparse_fields_call)
|
26
|
+
raise ArgumentError, "need to provide a Adapter.include_via interface" unless instance_variable_defined?(:@include_via_call)
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_via(&callback)
|
30
|
+
@find_via_call = callback
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_many_via(&callback)
|
34
|
+
@find_many_via_call = callback
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_via(&callback)
|
38
|
+
@create_via_call = callback
|
39
|
+
end
|
40
|
+
|
41
|
+
def update_via(&callback)
|
42
|
+
@update_via_call = callback
|
43
|
+
end
|
44
|
+
|
45
|
+
def assign_attributes_via(&callback)
|
46
|
+
@assign_attributes_via_call = callback
|
47
|
+
end
|
48
|
+
|
49
|
+
def assign_relationships_via(&callback)
|
50
|
+
@assign_relationships_via_call = callback
|
51
|
+
end
|
52
|
+
|
53
|
+
def sparse_fields(&callback)
|
54
|
+
@sparse_fields_call = callback
|
55
|
+
end
|
56
|
+
|
57
|
+
def include_via(&callback)
|
58
|
+
@include_via_call = callback
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_via_call(model_class, id)
|
62
|
+
@find_via_call.call(model_class, id)
|
63
|
+
end
|
64
|
+
|
65
|
+
def find_many_via_call(model_class)
|
66
|
+
@find_many_via_call.call(model_class)
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_via_call(model)
|
70
|
+
@create_via_call.call(model)
|
71
|
+
end
|
72
|
+
|
73
|
+
def update_via_call(model)
|
74
|
+
@update_via_call.call(model)
|
75
|
+
end
|
76
|
+
|
77
|
+
def assign_attributes_via_call(model, attributes)
|
78
|
+
@assign_attributes_via_call.call(model, attributes)
|
79
|
+
end
|
80
|
+
|
81
|
+
def assign_relationships_via_call(model, relationships)
|
82
|
+
@assign_relationships_via_call.call(model, relationships)
|
83
|
+
end
|
84
|
+
|
85
|
+
def sparse_fields_call(model_class, fields)
|
86
|
+
@sparse_fields_call.call(model_class, fields)
|
87
|
+
end
|
26
88
|
|
27
|
-
|
28
|
-
|
29
|
-
raise ArgumentError, "need to provide a Adapter.save_via interface" unless resource.instance_variable_defined?(:@save_via_call)
|
89
|
+
def include_via_call(model_class, includes)
|
90
|
+
@include_via_call.call(model_class, includes)
|
30
91
|
end
|
31
92
|
end
|
32
93
|
end
|
@@ -1,21 +1,43 @@
|
|
1
1
|
module JSONAPI
|
2
2
|
module Realizer
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
class Adapter
|
4
|
+
ACTIVE_RECORD = Proc.new do
|
5
|
+
find_many_via do |model_class|
|
6
|
+
model_class.all
|
7
|
+
end
|
8
|
+
|
9
|
+
find_via do |model_class, id|
|
10
|
+
model_class.find(id)
|
11
|
+
end
|
6
12
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
13
|
+
assign_attributes_via do |model, attributes|
|
14
|
+
model.assign_attributes(attributes)
|
15
|
+
end
|
16
|
+
|
17
|
+
assign_relationships_via do |model, relationships|
|
18
|
+
model.assign_attributes(relationships)
|
19
|
+
end
|
11
20
|
|
12
|
-
|
13
|
-
|
14
|
-
|
21
|
+
sparse_fields do |model_class, fields|
|
22
|
+
model_class.select(fields)
|
23
|
+
end
|
24
|
+
|
25
|
+
include_via do |model_class, includes|
|
26
|
+
model_class.includes(includes.map(&(recursively_nest = -> (chains) do
|
27
|
+
if chains.size == 1
|
28
|
+
chains.first
|
29
|
+
else
|
30
|
+
{chains.first => recursively_nest.call(chains.drop(1))}
|
31
|
+
end
|
32
|
+
end)))
|
33
|
+
end
|
34
|
+
|
35
|
+
create_via do |model|
|
36
|
+
model.create!
|
37
|
+
end
|
15
38
|
|
16
|
-
|
17
|
-
|
18
|
-
end
|
39
|
+
update_via do |model|
|
40
|
+
model.update!
|
19
41
|
end
|
20
42
|
end
|
21
43
|
end
|
@@ -1,24 +1,43 @@
|
|
1
1
|
module JSONAPI
|
2
2
|
module Realizer
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
class Adapter
|
4
|
+
MEMORY = Proc.new do
|
5
|
+
find_many_via do |model_class|
|
6
|
+
model_class.all
|
7
|
+
end
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
find_via do |model_class, id|
|
10
|
+
model_class.fetch(id)
|
11
|
+
end
|
12
|
+
|
13
|
+
assign_attributes_via do |model, attributes|
|
14
|
+
model.assign_attributes(attributes)
|
15
|
+
end
|
16
|
+
|
17
|
+
assign_relationships_via do |model, relationships|
|
18
|
+
model.assign_attributes(relationships)
|
19
|
+
end
|
20
|
+
|
21
|
+
sparse_fields do |model_class, fields|
|
22
|
+
model_class
|
23
|
+
end
|
11
24
|
|
12
|
-
|
13
|
-
|
25
|
+
include_via do |model_class, includes|
|
26
|
+
model_class
|
27
|
+
end
|
28
|
+
|
29
|
+
create_via do |model|
|
30
|
+
model.assign_attributes(id: model.id || SecureRandom.uuid)
|
31
|
+
model.assign_attributes(updated_at: Time.now)
|
32
|
+
model.class.const_get("STORE")[model.id] = model.class.const_get("ATTRIBUTES").inject({}) do |hash, key|
|
33
|
+
hash.merge({ key => model.public_send(key) })
|
14
34
|
end
|
35
|
+
end
|
15
36
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
hash.merge({ key => model.public_send(key) })
|
21
|
-
end
|
37
|
+
update_via do |model|
|
38
|
+
model.assign_attributes(updated_at: Time.now)
|
39
|
+
model.class.const_get("STORE")[model.id] = model.class.const_get("ATTRIBUTES").inject({}) do |hash, key|
|
40
|
+
hash.merge({ key => model.public_send(key) })
|
22
41
|
end
|
23
42
|
end
|
24
43
|
end
|
@@ -23,11 +23,15 @@ RSpec.describe JSONAPI::Realizer::Adapter do
|
|
23
23
|
|
24
24
|
end
|
25
25
|
|
26
|
-
context "when the
|
26
|
+
context "when the assign_attributes_via interface isn't defined" do
|
27
27
|
|
28
28
|
end
|
29
29
|
|
30
|
-
context "when the
|
30
|
+
context "when the create_via interface isn't defined" do
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when the update_via interface isn't defined" do
|
31
35
|
|
32
36
|
end
|
33
37
|
end
|
@@ -1,19 +1,17 @@
|
|
1
1
|
module JSONAPI
|
2
2
|
module Realizer
|
3
3
|
class Resource
|
4
|
-
extend ActiveSupport::Concern
|
5
|
-
|
6
4
|
attr_reader :model
|
7
5
|
|
8
6
|
def initialize(model)
|
9
7
|
@model = model
|
10
8
|
end
|
11
9
|
|
12
|
-
def
|
13
|
-
|
10
|
+
private def attribute(name)
|
11
|
+
attributes.public_send(name.to_sym)
|
14
12
|
end
|
15
13
|
|
16
|
-
def
|
14
|
+
private def relationship(name)
|
17
15
|
relationships.public_send(name.to_sym)
|
18
16
|
end
|
19
17
|
|
@@ -25,15 +23,6 @@ module JSONAPI
|
|
25
23
|
configuration.relationships
|
26
24
|
end
|
27
25
|
|
28
|
-
private def as_relationship(value)
|
29
|
-
data = value.fetch("data")
|
30
|
-
mapping = JSONAPI::Realizer.mapping.fetch(data.fetch("type"))
|
31
|
-
mapping.resource_class.find_via_call(
|
32
|
-
mapping.model_class,
|
33
|
-
data.fetch("id")
|
34
|
-
)
|
35
|
-
end
|
36
|
-
|
37
26
|
private def model_class
|
38
27
|
configuration.model_class
|
39
28
|
end
|
@@ -42,60 +31,48 @@ module JSONAPI
|
|
42
31
|
self.class.configuration
|
43
32
|
end
|
44
33
|
|
45
|
-
def
|
46
|
-
attributes.
|
47
|
-
end
|
48
|
-
|
49
|
-
def valid_relationship?(name, value)
|
50
|
-
relationships.respond_to?(name.to_sym)
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.represents(type, class_name:)
|
54
|
-
@configuration = JSONAPI::Realizer.register(self, class_name.constantize, type.to_s)
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.adapter(interface, &block)
|
58
|
-
JSONAPI::Realizer::Adapter.adapt(self, interface, &block)
|
34
|
+
def self.attribute(name)
|
35
|
+
attributes.public_send(name.to_sym)
|
59
36
|
end
|
60
37
|
|
61
|
-
def self.
|
62
|
-
|
38
|
+
def self.relationship(name)
|
39
|
+
relationships.public_send(name.to_sym)
|
63
40
|
end
|
64
41
|
|
65
|
-
def self.
|
66
|
-
|
42
|
+
def self.valid_attribute?(name, value)
|
43
|
+
attributes.respond_to?(name.to_sym)
|
67
44
|
end
|
68
45
|
|
69
|
-
def self.
|
70
|
-
|
46
|
+
def self.valid_relationship?(name, value)
|
47
|
+
relationships.respond_to?(name.to_sym)
|
71
48
|
end
|
72
49
|
|
73
|
-
def self.
|
74
|
-
|
50
|
+
def self.valid_sparse_field?(name)
|
51
|
+
attribute(name).fetch(:selectable)
|
75
52
|
end
|
76
53
|
|
77
|
-
def self.
|
78
|
-
|
54
|
+
def self.valid_includes?(name)
|
55
|
+
relationship(name).fetch(:includable)
|
79
56
|
end
|
80
57
|
|
81
|
-
def self.
|
82
|
-
|
58
|
+
def self.has(name, selectable: true)
|
59
|
+
attributes.public_send("#{name}=", OpenStruct.new({name: name, selectable: selectable}))
|
83
60
|
end
|
84
61
|
|
85
|
-
def self.
|
86
|
-
relationships.public_send("#{name}=", OpenStruct.new({name: name, as: as}))
|
62
|
+
def self.has_related(name, as: name, includable: true)
|
63
|
+
relationships.public_send("#{name}=", OpenStruct.new({name: name, as: as, includable: includable}))
|
87
64
|
end
|
88
65
|
|
89
|
-
def self.
|
90
|
-
|
66
|
+
def self.has_one(name, as: name, includable: true)
|
67
|
+
has_related(name, as: name, includable: includable)
|
91
68
|
end
|
92
69
|
|
93
|
-
def self.
|
94
|
-
|
70
|
+
def self.has_many(name, as: name, includable: true)
|
71
|
+
has_related(name, as: name, includable: includable)
|
95
72
|
end
|
96
73
|
|
97
|
-
def self.
|
98
|
-
|
74
|
+
def self.adapter
|
75
|
+
configuration.adapter
|
99
76
|
end
|
100
77
|
|
101
78
|
def self.attributes
|
@@ -110,12 +87,17 @@ module JSONAPI
|
|
110
87
|
configuration.model_class
|
111
88
|
end
|
112
89
|
|
90
|
+
def self.register(type, class_name:, adapter:)
|
91
|
+
JSONAPI::Realizer.register(
|
92
|
+
resource_class: self,
|
93
|
+
model_class: class_name.constantize,
|
94
|
+
adapter: JSONAPI::Realizer::Adapter.new(adapter),
|
95
|
+
type: type.to_s
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
113
99
|
def self.configuration
|
114
|
-
|
115
|
-
@configuration
|
116
|
-
else
|
117
|
-
raise ArgumentError, "you need to have the resource configured"
|
118
|
-
end
|
100
|
+
JSONAPI::Realizer.resource_mapping.fetch(self)
|
119
101
|
end
|
120
102
|
end
|
121
103
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi-realizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kurtis Rainbolt-Greene
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-03-
|
11
|
+
date: 2018-03-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -122,6 +122,10 @@ files:
|
|
122
122
|
- lib/jsonapi/realizer/action.rb
|
123
123
|
- lib/jsonapi/realizer/action/create.rb
|
124
124
|
- lib/jsonapi/realizer/action/create_spec.rb
|
125
|
+
- lib/jsonapi/realizer/action/index.rb
|
126
|
+
- lib/jsonapi/realizer/action/index_spec.rb
|
127
|
+
- lib/jsonapi/realizer/action/show.rb
|
128
|
+
- lib/jsonapi/realizer/action/show_spec.rb
|
125
129
|
- lib/jsonapi/realizer/action/update.rb
|
126
130
|
- lib/jsonapi/realizer/action/update_spec.rb
|
127
131
|
- lib/jsonapi/realizer/adapter.rb
|