jsonapionify 0.0.1.pre → 0.9.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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +35 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +0 -2
  5. data/Guardfile +1 -1
  6. data/README.md +13 -8
  7. data/Rakefile +10 -0
  8. data/config.ru +3 -3
  9. data/index.html +1 -0
  10. data/jsonapionify.gemspec +13 -8
  11. data/lib/jsonapionify/api/action.rb +60 -50
  12. data/lib/jsonapionify/api/attribute.rb +13 -2
  13. data/lib/jsonapionify/api/base/app_builder.rb +17 -2
  14. data/lib/jsonapionify/api/base/class_methods.rb +33 -17
  15. data/lib/jsonapionify/api/base/delegation.rb +4 -1
  16. data/lib/jsonapionify/api/base/doc_helper.rb +13 -4
  17. data/lib/jsonapionify/api/base/resource_definitions.rb +13 -2
  18. data/lib/jsonapionify/api/base.rb +22 -6
  19. data/lib/jsonapionify/api/context_delegate.rb +2 -2
  20. data/lib/jsonapionify/api/errors.rb +7 -2
  21. data/lib/jsonapionify/api/errors_object.rb +1 -1
  22. data/lib/jsonapionify/api/header_options.rb +6 -5
  23. data/lib/jsonapionify/api/param_options.rb +49 -7
  24. data/lib/jsonapionify/api/relationship/many.rb +0 -5
  25. data/lib/jsonapionify/api/relationship/one.rb +10 -9
  26. data/lib/jsonapionify/api/relationship.rb +17 -5
  27. data/lib/jsonapionify/api/resource/builders.rb +39 -10
  28. data/lib/jsonapionify/api/resource/class_methods.rb +17 -6
  29. data/lib/jsonapionify/api/resource/defaults/actions.rb +0 -1
  30. data/lib/jsonapionify/api/resource/defaults/errors.rb +11 -11
  31. data/lib/jsonapionify/api/resource/defaults/options.rb +53 -0
  32. data/lib/jsonapionify/api/resource/defaults/params.rb +9 -0
  33. data/lib/jsonapionify/api/resource/defaults/request_contexts.rb +17 -11
  34. data/lib/jsonapionify/api/resource/definitions/actions.rb +51 -45
  35. data/lib/jsonapionify/api/resource/definitions/attributes.rb +2 -2
  36. data/lib/jsonapionify/api/resource/definitions/helpers.rb +18 -0
  37. data/lib/jsonapionify/api/resource/definitions/pagination.rb +183 -53
  38. data/lib/jsonapionify/api/resource/definitions/params.rb +43 -12
  39. data/lib/jsonapionify/api/resource/definitions/request_headers.rb +1 -67
  40. data/lib/jsonapionify/api/resource/definitions/scopes.rb +2 -13
  41. data/lib/jsonapionify/api/resource/definitions/sorting.rb +71 -58
  42. data/lib/jsonapionify/api/resource/error_handling.rb +2 -2
  43. data/lib/jsonapionify/api/resource/includer.rb +6 -0
  44. data/lib/jsonapionify/api/resource.rb +14 -3
  45. data/lib/jsonapionify/api/response.rb +2 -2
  46. data/lib/jsonapionify/api/server/mock_response.rb +2 -2
  47. data/lib/jsonapionify/api/server/request.rb +11 -7
  48. data/lib/jsonapionify/api/server.rb +1 -1
  49. data/lib/jsonapionify/api/sort_field.rb +59 -0
  50. data/lib/jsonapionify/api/sort_field_set.rb +36 -0
  51. data/lib/jsonapionify/callbacks.rb +3 -3
  52. data/lib/jsonapionify/continuation.rb +1 -0
  53. data/lib/jsonapionify/deep_sort_collection.rb +22 -0
  54. data/lib/jsonapionify/documentation/template.erb +196 -77
  55. data/lib/jsonapionify/documentation.rb +9 -9
  56. data/lib/jsonapionify/indented_string.rb +1 -0
  57. data/lib/jsonapionify/inherited_attributes.rb +4 -3
  58. data/lib/jsonapionify/structure/collections/base.rb +2 -1
  59. data/lib/jsonapionify/structure/helpers/errors.rb +1 -1
  60. data/lib/jsonapionify/structure/helpers/object_defaults.rb +2 -1
  61. data/lib/jsonapionify/structure/helpers/validations.rb +2 -1
  62. data/lib/jsonapionify/structure/objects/base.rb +4 -3
  63. data/lib/jsonapionify/structure/objects/top_level.rb +1 -1
  64. data/lib/jsonapionify/types/boolean_type.rb +2 -2
  65. data/lib/jsonapionify/types/date_string_type.rb +1 -1
  66. data/lib/jsonapionify/types/time_string_type.rb +1 -1
  67. data/lib/jsonapionify/version.rb +1 -1
  68. data/lib/jsonapionify.rb +16 -2
  69. metadata +69 -10
  70. data/fixtures/documentation.json +0 -364
  71. data/lib/jsonapionify/api/resource/http.rb +0 -11
  72. data/lib/jsonapionify/enumerable_observer.rb +0 -91
  73. data/lib/jsonapionify/unstrict_proc.rb +0 -28
data/lib/jsonapionify.rb CHANGED
@@ -10,6 +10,14 @@ module JSONAPIonify
10
10
  extend JSONAPIonify::Autoload
11
11
  autoload_all 'jsonapionify'
12
12
 
13
+ TRUTHY_STRINGS = %w(t true y yes 1).flat_map do |str|
14
+ [str.downcase, str.upcase, str.capitalize]
15
+ end.uniq
16
+
17
+ FALSEY_STRINGS = %w(f false n no 0).flat_map do |str|
18
+ [str.downcase, str.upcase, str.capitalize]
19
+ end.uniq
20
+
13
21
  def self.path
14
22
  __dir__
15
23
  end
@@ -19,14 +27,20 @@ module JSONAPIonify
19
27
  Structure::Objects::TopLevel.from_hash(hash)
20
28
  end
21
29
 
22
- def self.new_object
23
- Structure::Objects::TopLevel.new
30
+ def self.new_object(*args)
31
+ Structure::Objects::TopLevel.new(*args)
24
32
  end
25
33
 
26
34
  def self.cache(store, *args)
27
35
  self.cache_store = ActiveSupport::Cache.lookup_store(store, *args)
28
36
  end
29
37
 
38
+ def self.digest
39
+ @digest ||= Digest::SHA2.hexdigest(
40
+ Dir.glob(File.join __dir__, '**/*.rb').map { |f| File.read f }.join
41
+ )
42
+ end
43
+
30
44
  def self.cache_store=(store)
31
45
  @cache_store = store
32
46
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapionify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.pre
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Waldrip
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-12-17 00:00:00.000000000 Z
11
+ date: 2016-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -108,6 +108,48 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: possessive
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: unstrict_proc
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: enumerable_observer
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
111
153
  - !ruby/object:Gem::Dependency
112
154
  name: pry
113
155
  requirement: !ruby/object:Gem::Requirement
@@ -374,6 +416,20 @@ dependencies:
374
416
  - - ">="
375
417
  - !ruby/object:Gem::Version
376
418
  version: '0'
419
+ - !ruby/object:Gem::Dependency
420
+ name: memory_model
421
+ requirement: !ruby/object:Gem::Requirement
422
+ requirements:
423
+ - - ">="
424
+ - !ruby/object:Gem::Version
425
+ version: '0'
426
+ type: :development
427
+ prerelease: false
428
+ version_requirements: !ruby/object:Gem::Requirement
429
+ requirements:
430
+ - - ">="
431
+ - !ruby/object:Gem::Version
432
+ version: '0'
377
433
  description:
378
434
  email:
379
435
  - jason@waldrip.net
@@ -383,6 +439,7 @@ extra_rdoc_files: []
383
439
  files:
384
440
  - ".codeclimate.yml"
385
441
  - ".csslintrc"
442
+ - ".editorconfig"
386
443
  - ".gitignore"
387
444
  - ".rspec"
388
445
  - ".rubocop.yml"
@@ -398,7 +455,7 @@ files:
398
455
  - bin/console
399
456
  - bin/setup
400
457
  - config.ru
401
- - fixtures/documentation.json
458
+ - index.html
402
459
  - jsonapionify.gemspec
403
460
  - lib/core_ext/boolean.rb
404
461
  - lib/jsonapionify.rb
@@ -428,6 +485,8 @@ files:
428
485
  - lib/jsonapionify/api/resource/defaults.rb
429
486
  - lib/jsonapionify/api/resource/defaults/actions.rb
430
487
  - lib/jsonapionify/api/resource/defaults/errors.rb
488
+ - lib/jsonapionify/api/resource/defaults/options.rb
489
+ - lib/jsonapionify/api/resource/defaults/params.rb
431
490
  - lib/jsonapionify/api/resource/defaults/request_contexts.rb
432
491
  - lib/jsonapionify/api/resource/defaults/response_contexts.rb
433
492
  - lib/jsonapionify/api/resource/definitions.rb
@@ -443,20 +502,21 @@ files:
443
502
  - lib/jsonapionify/api/resource/definitions/scopes.rb
444
503
  - lib/jsonapionify/api/resource/definitions/sorting.rb
445
504
  - lib/jsonapionify/api/resource/error_handling.rb
446
- - lib/jsonapionify/api/resource/http.rb
447
505
  - lib/jsonapionify/api/resource/includer.rb
448
506
  - lib/jsonapionify/api/response.rb
449
507
  - lib/jsonapionify/api/server.rb
450
508
  - lib/jsonapionify/api/server/mock_response.rb
451
509
  - lib/jsonapionify/api/server/request.rb
510
+ - lib/jsonapionify/api/sort_field.rb
511
+ - lib/jsonapionify/api/sort_field_set.rb
452
512
  - lib/jsonapionify/api/test_helper.rb
453
513
  - lib/jsonapionify/autoload.rb
454
514
  - lib/jsonapionify/callbacks.rb
455
515
  - lib/jsonapionify/character_range.rb
456
516
  - lib/jsonapionify/continuation.rb
517
+ - lib/jsonapionify/deep_sort_collection.rb
457
518
  - lib/jsonapionify/documentation.rb
458
519
  - lib/jsonapionify/documentation/template.erb
459
- - lib/jsonapionify/enumerable_observer.rb
460
520
  - lib/jsonapionify/indented_string.rb
461
521
  - lib/jsonapionify/inherited_attributes.rb
462
522
  - lib/jsonapionify/structure.rb
@@ -501,7 +561,6 @@ files:
501
561
  - lib/jsonapionify/types/object_type.rb
502
562
  - lib/jsonapionify/types/string_type.rb
503
563
  - lib/jsonapionify/types/time_string_type.rb
504
- - lib/jsonapionify/unstrict_proc.rb
505
564
  - lib/jsonapionify/version.rb
506
565
  homepage: https://github.com/brandfolder/jsonapionify
507
566
  licenses:
@@ -515,15 +574,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
515
574
  requirements:
516
575
  - - "~>"
517
576
  - !ruby/object:Gem::Version
518
- version: '2.2'
577
+ version: 2.3.0
519
578
  required_rubygems_version: !ruby/object:Gem::Requirement
520
579
  requirements:
521
- - - ">"
580
+ - - ">="
522
581
  - !ruby/object:Gem::Version
523
- version: 1.3.1
582
+ version: '0'
524
583
  requirements: []
525
584
  rubyforge_project:
526
- rubygems_version: 2.4.8
585
+ rubygems_version: 2.5.1
527
586
  signing_key:
528
587
  specification_version: 4
529
588
  summary: Ruby object structure conforming to the JSON API spec.
@@ -1,364 +0,0 @@
1
- {
2
- "title": "Brandfolder API",
3
- "base_url": "https://api.brandfolder.com",
4
- "description": "Our API is awesome.",
5
- "resources": {
6
- "brandfolders": {
7
- "actions": {
8
-
9
- },
10
- "description": "Good stuff here.",
11
- "attributes": {
12
- "name": {
13
- "description": "The name of the Brandfolder.",
14
- "required": true,
15
- "allow": [
16
- "read",
17
- "write"
18
- ]
19
- },
20
- "slug": {
21
- "description": "The url of the Brandfolder on Branfolder.com.",
22
- "required": true,
23
- "allow": [
24
- "read",
25
- "write"
26
- ]
27
- },
28
- "tagline": {
29
- "description": "Tagline of the Brandfolder.",
30
- "required": false,
31
- "allow": [
32
- "read",
33
- "write"
34
- ]
35
- },
36
- "privacy": {
37
- "description": "The privacy of the Brandfolder, this can be `public`, `private` or `stealth`.",
38
- "required": false,
39
- "allow": [
40
- "read",
41
- "write"
42
- ]
43
- }
44
- },
45
- "relationships": {
46
- "organization": {
47
- "resource": "organizations",
48
- "type": "one",
49
- "allow": [
50
- "read"
51
- ]
52
- },
53
- "assets": {
54
- "resource": "assets",
55
- "type": "many",
56
- "allow": [
57
- "read"
58
- ]
59
- },
60
- "sections": {
61
- "resource": "sections",
62
- "type": "many",
63
- "allow": [
64
- "read",
65
- "write"
66
- ]
67
- },
68
- "collections": {
69
- "resource": "collections",
70
- "type": "many",
71
- "allow": [
72
- "read",
73
- "write"
74
- ]
75
- },
76
- "admins": {
77
- "resource": "users",
78
- "type": "many",
79
- "allow": [
80
- "read",
81
- "write"
82
- ]
83
- },
84
- "collaborators": {
85
- "resource": "users",
86
- "type": "many",
87
- "allow": [
88
- "read",
89
- "write"
90
- ]
91
- },
92
- "guests": {
93
- "resource": "users",
94
- "type": "many",
95
- "allow": [
96
- "read",
97
- "write"
98
- ]
99
- }
100
- }
101
- },
102
- "users": {
103
- "attributes": {
104
- "first_name": {
105
- "description": "The user's first name.",
106
- "required": false,
107
- "allow": [
108
- "read",
109
- "write"
110
- ]
111
- },
112
- "last_name": {
113
- "description": "The user's last name.",
114
- "required": false,
115
- "allow": [
116
- "read",
117
- "write"
118
- ]
119
- },
120
- "email_address": {
121
- "description": "The user's email address.",
122
- "required": true,
123
- "allow": [
124
- "read",
125
- "write"
126
- ]
127
- },
128
- "password": {
129
- "description": "The user's password.",
130
- "required": true,
131
- "allow": [
132
- "write"
133
- ]
134
- }
135
- },
136
- "relationships": {
137
- "brandfolders": {
138
- "resource": "brandfolders",
139
- "type": "many",
140
- "allow": [
141
- "read"
142
- ]
143
- }
144
- }
145
- },
146
- "sections": {
147
- "attributes": {
148
- "name": {
149
- "description": "The name of the section.",
150
- "required": true,
151
- "allow": [
152
- "read",
153
- "write"
154
- ]
155
- },
156
- "slug": {
157
- "description": "The url of the section on Branfolder.com.",
158
- "required": true,
159
- "allow": [
160
- "read",
161
- "write"
162
- ]
163
- }
164
- },
165
- "relationships": {
166
- "brandfolder": {
167
- "resource": "brandfolders",
168
- "type": "one",
169
- "allow": [
170
- "read"
171
- ]
172
- },
173
- "assets": {
174
- "resource": "assets",
175
- "type": "many",
176
- "allow": [
177
- "read",
178
- "write"
179
- ]
180
- },
181
- "sections": {
182
- "resource": "sections",
183
- "type": "many",
184
- "allow": [
185
- "read",
186
- "write"
187
- ]
188
- },
189
- "admins": {
190
- "resource": "users",
191
- "type": "many",
192
- "allow": [
193
- "read",
194
- "write"
195
- ]
196
- },
197
- "collaborators": {
198
- "resource": "users",
199
- "type": "many",
200
- "allow": [
201
- "read",
202
- "write"
203
- ]
204
- },
205
- "guests": {
206
- "resource": "users",
207
- "type": "many",
208
- "allow": [
209
- "read",
210
- "write"
211
- ]
212
- }
213
- }
214
- },
215
- "collections": {
216
- "attributes": {
217
- "name": {
218
- "description": "The name of the Collection.",
219
- "required": true,
220
- "allow": [
221
- "read",
222
- "write"
223
- ]
224
- },
225
- "slug": {
226
- "description": "The url of the collection on Branfolder.com.",
227
- "required": true,
228
- "allow": [
229
- "read",
230
- "write"
231
- ]
232
- },
233
- "tagline": {
234
- "description": "Tagline of the Brandfolder.",
235
- "required": false,
236
- "allow": [
237
- "read",
238
- "write"
239
- ]
240
- },
241
- "privacy": {
242
- "description": "The privacy of the Brandfolder, this can be `public`, `private` or `stealth`.",
243
- "required": false,
244
- "allow": [
245
- "read",
246
- "write"
247
- ]
248
- }
249
- },
250
- "relationships": {
251
- "brandfolder": {
252
- "resource": "brandfolders",
253
- "type": "one",
254
- "allow": [
255
- "read"
256
- ]
257
- },
258
- "assets": {
259
- "resource": "assets",
260
- "type": "many",
261
- "allow": [
262
- "read",
263
- "write"
264
- ]
265
- },
266
- "sections": {
267
- "resource": "sections",
268
- "type": "many",
269
- "allow": [
270
- "read",
271
- "write"
272
- ]
273
- },
274
- "admins": {
275
- "resource": "users",
276
- "type": "many",
277
- "allow": [
278
- "read",
279
- "write"
280
- ]
281
- },
282
- "collaborators": {
283
- "resource": "users",
284
- "type": "many",
285
- "allow": [
286
- "read",
287
- "write"
288
- ]
289
- },
290
- "guests": {
291
- "resource": "users",
292
- "type": "many",
293
- "allow": [
294
- "read",
295
- "write"
296
- ]
297
- }
298
- }
299
- },
300
- "organizations": {
301
- "attributes": {
302
- "name": {
303
- "description": "The name of the organization.",
304
- "required": true,
305
- "allow": [
306
- "read",
307
- "write"
308
- ]
309
- },
310
- "slug": {
311
- "description": "The url of the organization on Branfolder.com.",
312
- "required": true,
313
- "allow": [
314
- "read",
315
- "write"
316
- ]
317
- }
318
- },
319
- "relationships": {
320
- "brandfolders": {
321
- "resource": "brandfolders",
322
- "type": "many",
323
- "allow": [
324
- "read",
325
- "write"
326
- ]
327
- },
328
- "owners": {
329
- "resource": "users",
330
- "type": "many",
331
- "allow": [
332
- "read",
333
- "write"
334
- ]
335
- },
336
- "admins": {
337
- "resource": "users",
338
- "type": "many",
339
- "allow": [
340
- "read",
341
- "write"
342
- ]
343
- },
344
- "collaborators": {
345
- "resource": "users",
346
- "type": "many",
347
- "allow": [
348
- "read",
349
- "write"
350
- ]
351
- },
352
- "guests": {
353
- "resource": "users",
354
- "type": "many",
355
- "allow": [
356
- "read",
357
- "write"
358
- ]
359
- }
360
- }
361
- }
362
- }
363
- }
364
-
@@ -1,11 +0,0 @@
1
- module JSONAPIonify::Api
2
- class Resource::Http < Resource
3
-
4
- Rack::Utils::SYMBOL_TO_STATUS_CODE.each do |symbol, code|
5
- define_action(symbol).response status: code do
6
- error_now symbol
7
- end
8
- end
9
-
10
- end
11
- end
@@ -1,91 +0,0 @@
1
- require 'jsonapionify/unstrict_proc'
2
-
3
- module JSONAPIonify
4
- module EnumerableObserver
5
- using UnstrictProc
6
-
7
- def self.observe(obj)
8
- Observer.new(obj)
9
- end
10
-
11
- def observe(obj = self)
12
- Observer.new(obj)
13
- end
14
-
15
- class Observer
16
- using UnstrictProc
17
-
18
- UNSAFE_METHODS = %i{
19
- instance_variable_set
20
- remove_instance_variable
21
- define_singleton_method
22
- instance_eval
23
- instance_exec
24
- }
25
- SAFE_METHODS = (%i{each} + Object.instance_methods) - UNSAFE_METHODS
26
-
27
- private def observer_method_proc(existing = [])
28
- array = [self, *existing].freeze
29
- lambda do
30
- array
31
- end
32
- end
33
-
34
- def initialize(obj)
35
- @object = obj
36
- observers_proc =
37
- observer_method_proc(obj.respond_to?(:observers) ? obj.observers : [])
38
- obj.define_singleton_method(:observers, &observers_proc)
39
- obj.extend mod
40
- end
41
-
42
- def mod
43
- @mod ||= begin
44
- obj = @object
45
- blocks = self.blocks
46
- Module.new do
47
- (obj.methods - SAFE_METHODS).each do |meth|
48
- old = obj.method(meth).unbind.bind(obj)
49
- define_method(meth) do |*args, &block|
50
- before = each.to_a
51
- val = old.call(*args, &block)
52
- after = each.to_a
53
- added = after - before
54
- removed = before - after
55
- blocks[:add].unstrict.call(added) unless added.empty? || !blocks[:add]
56
- blocks[:remove].unstrict.call(removed) unless removed.empty? || !blocks[:remove]
57
- val
58
- end
59
- end
60
- end
61
- end
62
- end
63
-
64
- def unobserve
65
- mod.instance_methods.each do |method_name|
66
- mod.module_eval do
67
- remove_method method_name
68
- end
69
- end
70
- @unobserved = true
71
- end
72
-
73
- def added(&block)
74
- blocks[:add] = block
75
- self
76
- end
77
-
78
- def removed(&block)
79
- blocks[:remove] = block
80
- self
81
- end
82
-
83
- protected
84
-
85
- def blocks
86
- @blocks ||= {}
87
- end
88
-
89
- end
90
- end
91
- end
@@ -1,28 +0,0 @@
1
- module JSONAPIonify
2
- module UnstrictProc
3
- refine Proc do
4
- def unstrict
5
- return self unless lambda?
6
- req_count = required_parameters.count
7
- Proc.new do |*arguments|
8
- if req_count > arguments.count
9
- (req_count - arguments.count).times { arguments << nil }
10
- elsif req_count < arguments.count
11
- arguments = req_count == 0 ? [] : arguments[0..(req_count - 1)]
12
- end
13
- call(*arguments)
14
- end
15
- end
16
-
17
- private
18
-
19
- def parameters_hash
20
- parameters.each_with_object({}) { |(k, v), h| (h[k] ||= []) << v }
21
- end
22
-
23
- def required_parameters
24
- parameters_hash[:req] || []
25
- end
26
- end
27
- end
28
- end