jsonapi_parameters 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3245b44f39068e448c0acbf8ad822ebe3891133cfde009c4b6411d3b9a50f1b
4
- data.tar.gz: cfc79502f187a3f9453f9b3ba952128e00114a7cc47ea5aaf6047ba5484c49cc
3
+ metadata.gz: 02a88091cd1b68f0f385e97308b9b5ed213fe640f5f4831d0e83227cbb24fc98
4
+ data.tar.gz: 56dc70b94dac08359d21703245e1f5a8b7864bf9a0ec4b487b68057121fcac60
5
5
  SHA512:
6
- metadata.gz: 1f2504172943defbcd2bfbd86eca1134ca2da0193f6f89e0f315ba931372864b545e9005266251982c0e6b167e004a8202a1ca95954837b1b1796e7f4148897b
7
- data.tar.gz: f13ad5d8f443e1cd010c133152b50791d876c44b32385d4009318b94e4f29c437d55a02882737bc57237d171e68f8f1802d52976596d13bd0cf60167cc9403ea
6
+ metadata.gz: b2ec520d45378a5c44c59f6eb37fd2d809e200eb0203d200d3b388a94dbfacfe0dfab9c1578f5ea446aba52de63deeb618e7baf9263c38573ac406cdedadf9bf
7
+ data.tar.gz: e9a193c2d58b04be662ae0481d9dc1ff451dbd49b44f7d4b18f0698b99c1958e843c66cb8659bf304db8ed649ac996643738dc6041f466ebc15492f2b330d5cf
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # JsonApi::Parameters
2
2
  Simple JSON:API compliant parameters translator.
3
3
 
4
+ [![Gem Version](https://badge.fury.io/rb/jsonapi_parameters.svg)](https://badge.fury.io/rb/jsonapi_parameters)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/84fd5b548eea8d7e18af/maintainability)](https://codeclimate.com/github/visualitypl/jsonapi_parameters/maintainability)
6
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/84fd5b548eea8d7e18af/test_coverage)](https://codeclimate.com/github/visualitypl/jsonapi_parameters/test_coverage)
7
+
4
8
  #### The problem
5
9
 
6
10
  JSON:API standard specifies not only responses (that can be handled nicely, using gems like [fast_jsonapi from Netflix](https://github.com/Netflix/fast_jsonapi)), but also the request structure.
@@ -60,6 +64,209 @@ def create_params
60
64
  end
61
65
  ```
62
66
 
67
+ #### Relationships
68
+
69
+ JsonApi::Parameters supports ActiveRecord relationship parameters, including [nested attributes](https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html).
70
+
71
+ Relationship parameters are being read from two optional trees:
72
+
73
+ * `relationships`,
74
+ * `included`
75
+
76
+ If you provide any related resources in the `relationships` table, this gem will also look for corresponding, `included` resources and their attributes. Thanks to that this gem supports nested attributes, and will try to translate these included resources and pass them along.
77
+
78
+ ##### belongs_to
79
+
80
+ Passing a resource that is a single entity in relationships tree will make JsonApi::Parameters assume that it is a `belongs_to` relationship.
81
+
82
+ ###### Without included entity
83
+ Example:
84
+
85
+ ```
86
+ class Movie < ActiveRecord::Model
87
+ belongs_to :director
88
+ end
89
+ ```
90
+
91
+ Request body:
92
+
93
+ ```
94
+ {
95
+ data: {
96
+ type: 'movies',
97
+ attributes: {
98
+ title: 'The Terminator',
99
+ },
100
+ relationships: {
101
+ director: {
102
+ data: {
103
+ id: 682, type: 'directors'
104
+ }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ Will translate to:
112
+
113
+ ```
114
+ {
115
+ movie: {
116
+ title: 'The Terminator',
117
+ director_id: 682
118
+ }
119
+ }
120
+ ```
121
+
122
+
123
+ ###### With included entity:
124
+ Example:
125
+
126
+ ```
127
+ class Movie < ActiveRecord::Model
128
+ belongs_to :director
129
+
130
+ accepts_nested_attributes_for :director
131
+ end
132
+ ```
133
+
134
+ Request body:
135
+ ```
136
+ {
137
+ data: {
138
+ type: 'movies',
139
+ attributes: {
140
+ title: 'The Terminator',
141
+ },
142
+ relationships: {
143
+ director: {
144
+ data: {
145
+ id: 682, type: 'directors'
146
+ }
147
+ }
148
+ }
149
+ },
150
+ included: [
151
+ {
152
+ type: 'directors',
153
+ id: 682,
154
+ attributes: {
155
+ name: 'Some guy'
156
+ }
157
+ }
158
+ ]
159
+ }
160
+ ```
161
+
162
+ Will translate to:
163
+ ```
164
+ {
165
+ movie: {
166
+ title: 'The Terminator',
167
+ director_attributes: { id: 682, name: 'Some guy' }
168
+ }
169
+ }
170
+ ```
171
+
172
+ ##### has_many
173
+
174
+ Passing a resource that is a an array of entities in relationships tree will make JsonApi::Parameters assume that it is a `has_many` relationship.
175
+
176
+ ###### Without included entity
177
+ Example:
178
+
179
+ ```
180
+ class Movie < ActiveRecord::Model
181
+ has_many :genres
182
+ end
183
+ ```
184
+
185
+ Request body:
186
+
187
+ ```
188
+ {
189
+ data: {
190
+ type: 'movies',
191
+ attributes: {
192
+ title: 'The Terminator',
193
+ },
194
+ relationships: {
195
+ genres: [{
196
+ data: {
197
+ id: 1, type: 'genres'
198
+ },
199
+ data: {
200
+ id: 2, type: 'genres'
201
+ },
202
+ }]
203
+ }
204
+ }
205
+ }
206
+ ```
207
+
208
+ Will translate to:
209
+
210
+ ```
211
+ {
212
+ movie: {
213
+ title: 'The Terminator',
214
+ genre_ids: [1, 2]
215
+ }
216
+ }
217
+ ```
218
+
219
+
220
+ ###### With included entity:
221
+ Example:
222
+
223
+ ```
224
+ class Movie < ActiveRecord::Model
225
+ has_many :genres
226
+
227
+ accepts_nested_attributes_for :genres
228
+ end
229
+ ```
230
+
231
+ Request body:
232
+ ```
233
+ {
234
+ data: {
235
+ type: 'movies',
236
+ attributes: {
237
+ title: 'The Terminator',
238
+ },
239
+ relationships: {
240
+ genres: [{
241
+ data: {
242
+ id: 1, type: 'genres'
243
+ }
244
+ }]
245
+ }
246
+ },
247
+ included: [
248
+ {
249
+ type: 'genre',
250
+ id: 1,
251
+ attributes: {
252
+ name: 'Genre one'
253
+ }
254
+ }
255
+ ]
256
+ }
257
+ ```
258
+
259
+ Will translate to:
260
+ ```
261
+ {
262
+ movie: {
263
+ title: 'The Terminator',
264
+ genres_attributes: [{ id: 1, name: 'Genre one' }]
265
+ }
266
+ }
267
+ ```
268
+
269
+
63
270
  #### Casing
64
271
 
65
272
  If the input is in a different convention than `:snake`, you should specify that.
data/Rakefile CHANGED
@@ -15,13 +15,6 @@ RDoc::Task.new(:rdoc) do |rdoc|
15
15
  end
16
16
 
17
17
  require 'bundler/gem_tasks'
18
+ require 'rspec/core/rake_task'
18
19
 
19
- require 'rake/testtask'
20
-
21
- Rake::TestTask.new(:test) do |t|
22
- t.libs << 'test'
23
- t.pattern = 'test/**/*_test.rb'
24
- t.verbose = false
25
- end
26
-
27
- task default: :test
20
+ task :default => :spec
@@ -2,3 +2,5 @@ require 'jsonapi_parameters/parameters'
2
2
  require 'jsonapi_parameters/translator'
3
3
  require 'jsonapi_parameters/core_ext'
4
4
  require 'jsonapi_parameters/version'
5
+
6
+ require 'active_support/inflector'
@@ -1,2 +1,2 @@
1
- require_relative 'core_ext/action_controller/parameters'
2
- require_relative 'core_ext/action_dispatch/http/mime_type'
1
+ require 'jsonapi_parameters/core_ext/action_controller/parameters'
2
+ require 'jsonapi_parameters/core_ext/action_dispatch/http/mime_type'
@@ -1,4 +1,6 @@
1
1
  module JsonApi::Parameters
2
+ include ActiveSupport::Inflector
3
+
2
4
  def jsonapify(params, naming_convention: :snake)
3
5
  jsonapi_translate(params, naming_convention: naming_convention)
4
6
  end
@@ -60,9 +62,9 @@ module JsonApi::Parameters
60
62
  end
61
63
 
62
64
  def handle_to_many_relation(relationship_key, relationship_value)
63
- key = "#{relationship_key.to_s.pluralize}_attributes".to_sym
65
+ with_inclusion = true
64
66
 
65
- val = relationship_value.map do |relationship_value|
67
+ vals = relationship_value.map do |relationship_value|
66
68
  related_id = relationship_value.dig(:id)
67
69
  related_type = relationship_value.dig(:type)
68
70
 
@@ -70,26 +72,46 @@ module JsonApi::Parameters
70
72
  related_id: related_id, related_type: related_type
71
73
  ) || {}
72
74
 
73
- included_object.delete(:type)
75
+ # If at least one related object has not been found in `included` tree,
76
+ # we should not attempt to "#{relationship_key}_attributes" but
77
+ # "#{relationship_key}_ids" instead.
78
+ with_inclusion = with_inclusion ? !included_object.empty? : with_inclusion
79
+
80
+ if with_inclusion
81
+ included_object.delete(:type)
82
+ included_object[:attributes].merge(id: related_id)
83
+ else
84
+ relationship_value.dig(:id)
85
+ end
86
+ end
74
87
 
75
- included_object[:attributes].merge(id: related_id)
88
+ # We may have smells in our value array as `with_inclusion` may have been changed at some point
89
+ # but not in the beginning.
90
+ # Because of that we should clear it and make sure the results are unified (e.g. array of ids)
91
+ unless with_inclusion
92
+ vals.map do |val|
93
+ val.dig(:attributes, :id) if val.is_a?(Hash)
94
+ end
76
95
  end
77
96
 
78
- [key, val]
97
+ key = with_inclusion ? "#{pluralize(relationship_key)}_attributes".to_sym : "#{singularize(relationship_key)}_ids".to_sym
98
+
99
+ [key, vals]
79
100
  end
80
101
 
81
102
  def handle_to_one_relation(relationship_key, relationship_value)
82
103
  related_id = relationship_value.dig(:id)
83
- related_type = relationship_key.to_s
104
+ related_type = relationship_value.dig(:type)
84
105
 
85
106
  included_object = find_included_object(
86
107
  related_id: related_id, related_type: related_type
87
- )
108
+ ) || {}
88
109
 
89
- return ["#{related_type.singularize}_id".to_sym, related_id] if included_object.nil?
110
+ return ["#{singularize(relationship_key)}_id".to_sym, related_id] if included_object.empty?
90
111
 
91
112
  included_object.delete(:type)
92
- ["#{related_type.singularize}_attributes".to_sym, included_object]
113
+ included_object = included_object[:attributes].merge(id: related_id)
114
+ ["#{singularize(relationship_key)}_attributes".to_sym, included_object]
93
115
  end
94
116
 
95
117
  def find_included_object(related_id:, related_type:)
@@ -1,3 +1,5 @@
1
- module JsonApi::Parameters
2
- VERSION = '0.2.0'.freeze
1
+ module JsonApi
2
+ module Parameters
3
+ VERSION = '0.3.0'.freeze
4
+ end
3
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapi_parameters
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Visuality
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-04-24 00:00:00.000000000 Z
12
+ date: 2019-05-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -235,6 +235,34 @@ dependencies:
235
235
  - - "~>"
236
236
  - !ruby/object:Gem::Version
237
237
  version: 0.3.8
238
+ - !ruby/object:Gem::Dependency
239
+ name: simplecov
240
+ requirement: !ruby/object:Gem::Requirement
241
+ requirements:
242
+ - - "~>"
243
+ - !ruby/object:Gem::Version
244
+ version: 0.16.1
245
+ type: :development
246
+ prerelease: false
247
+ version_requirements: !ruby/object:Gem::Requirement
248
+ requirements:
249
+ - - "~>"
250
+ - !ruby/object:Gem::Version
251
+ version: 0.16.1
252
+ - !ruby/object:Gem::Dependency
253
+ name: simplecov-console
254
+ requirement: !ruby/object:Gem::Requirement
255
+ requirements:
256
+ - - "~>"
257
+ - !ruby/object:Gem::Version
258
+ version: 0.4.2
259
+ type: :development
260
+ prerelease: false
261
+ version_requirements: !ruby/object:Gem::Requirement
262
+ requirements:
263
+ - - "~>"
264
+ - !ruby/object:Gem::Version
265
+ version: 0.4.2
238
266
  description: JsonApi::Parameters allows you to easily translate JSON:API compliant
239
267
  parameters to a structure expected by Rails.
240
268
  email: