sinja 1.2.2 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: adc1346572a9c1a9ae1d2b85c70931cd329cdd1e
4
- data.tar.gz: 73db963c1a496a98de718d1ea17ebda739d80ad9
3
+ metadata.gz: 4606d9daabb3be7a7aca0c603f36134f86cf2885
4
+ data.tar.gz: 210cc332302ab5b34140b14bd74e24c2a72fb93a
5
5
  SHA512:
6
- metadata.gz: 55dc7ba3bc974625bb2cf65b943ccf8cf57541226f39eb0d66c7e56320c507cf3f8d76d0fddba5605698677f459958ede489477ebc9baed97d82a50c6a1e76f2
7
- data.tar.gz: f75638978ade62140b1306644e4a81a62b18d1a8ce09b388ffa183c6c4a5859d531ce108b71299e5b82e39b3759e6d3c0b381799dc12be0f845a63784ec2280a
6
+ metadata.gz: c5627d6ea47b5a62945a49b42e69f165ca10b100723b17cd3efd9b04fdc1e8b67b84e334a98b8814b90961c38958287fb1c86fce5389f9250c72cf58c234d961
7
+ data.tar.gz: f1dda55b7f38c2e09f7ccb4d00fce2a09ef2342df085cd847e2937d58ccf5c5acfae2d9d004594bc12174ddf0abf0b8129e2823cbdc1124793395708e9850299
data/README.md CHANGED
@@ -20,13 +20,14 @@ DSL to enable resource-, relationship-, and role-centric API development, and
20
20
  it configures Sinatra with the proper settings, MIME-types, filters,
21
21
  conditions, and error-handling.
22
22
 
23
- There are [many][31] parsing (deserializing) and rendering (serializing) and
24
- so-called "JSON API" libraries available for Ruby, but relatively few that
25
- attempt to correctly implement the entire {json:api} specification, including
23
+ There are [many][31] parsing (deserializing), rendering (serializing), and
24
+ other "JSON API" libraries available for Ruby, but relatively few that attempt
25
+ to correctly implement the entire {json:api} server specification, including
26
26
  routing, request header and query parameter checking, and relationship
27
- side-loading. Sinja lets you focus on the business logic of your applications
27
+ side-loading. Sinja lets you focus on the business logic of your applications
28
28
  without worrying about the specification, and without pulling in a heavy
29
- framework like [Rails][16]. It's lightweight and ORM-agnostic!
29
+ framework like [Rails][16]. It's lightweight, ORM-agnostic, and
30
+ [Ember.js][32]-friendly!
30
31
 
31
32
  <!-- START doctoc generated TOC please keep comment here to allow auto update -->
32
33
  <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
@@ -224,7 +225,7 @@ class App < Sinatra::Base
224
225
  show do
225
226
  headers 'X-ISBN'=>resource.isbn
226
227
  last_modified resource.updated_at
227
- next resource, include: %w[author]
228
+ next resource, include: ['author']
228
229
  end
229
230
 
230
231
  has_one :author do
@@ -318,12 +319,12 @@ configure_jsonapi do |c|
318
319
 
319
320
  # You can't set this directly; see "Query Parameters" below
320
321
  #c.query_params = {
321
- # :include=>[], :fields=>{}, :filter=>{}, :page=>{}, :sort=>[]
322
+ # :include=>Array, :fields=>Hash, :filter=>Hash, :page=>Hash, :sort=>Array
322
323
  #}
323
324
 
324
325
  #c.page_using = {} # see "Paging" below
325
326
 
326
- # Set the error logger used by Sinja
327
+ # Set the error logger used by Sinja (set to `nil' to disable)
327
328
  #c.error_logger = ->(error_hash) { logger.error('sinja') { error_hash } }
328
329
 
329
330
  # A hash of options to pass to JSONAPI::Serializer.serialize
@@ -394,6 +395,40 @@ simply returns `resource`.
394
395
 
395
396
  ### Action Helpers
396
397
 
398
+ <table>
399
+ <thead>
400
+ <tr>
401
+ <th><a href="#resource"><code>resource</code></a></th>
402
+ <th><a href="#has_one"><code>has_one</code></a></th>
403
+ <th><a href="#has_many"><code>has_many</code></a></th>
404
+ </tr>
405
+ </thead>
406
+ <tbody>
407
+ <tr valign="top">
408
+ <td><ul>
409
+ <li><a href="#index---array"><code>index</code></a></li>
410
+ <li><code>show</code> <a href="#show---object">w/ resource locator</a> or <a href="#show-id---object">w/o</a></li>
411
+ <li><a href="#show_many-ids---array"><code>show_many</code></a></li>
412
+ <li><code>create</code> <a href="#create-attr-id---id-object">w/ client-generated IDs</a> or <a href="#create-attr---id-object">w/o</a></li>
413
+ <li><a href="#update-attr---object"><code>update</code></a></li>
414
+ <li><a href="#destroy-"><code>destroy</code></a></li>
415
+ </ul></td>
416
+ <td><ul>
417
+ <li><a href="#pluck---object"><code>pluck</code></a></li>
418
+ <li><a href="#prune---trueclass"><code>prune</code></a></li>
419
+ <li><a href="#graft-rio---trueclass"><code>graft</code></a></li>
420
+ </ul></td>
421
+ <td><ul>
422
+ <li><a href="#fetch---array"><code>fetch</code></a></li>
423
+ <li><a href="#clear---trueclass"><code>clear</code></a></li>
424
+ <li><a href="#replace-rios---trueclass"><code>replace</code></a></li>
425
+ <li><a href="#merge-rios---trueclass"><code>merge</code></a></li>
426
+ <li><a href="#subtract-rios---trueclass"><code>subtract</code></a></li>
427
+ </ul></td>
428
+ </tr>
429
+ </tbody>
430
+ </table>
431
+
397
432
  Action helpers should be defined within the appropriate block contexts
398
433
  (`resource`, `has_one`, or `has_many`) using the given keywords and arguments
399
434
  below. Implicitly return the expected values as described below (as an array if
@@ -730,10 +765,8 @@ end
730
765
  resource :foos do
731
766
  helpers do
732
767
  def role
733
- if resource&.owner == logged_in_user
734
- [*super].push(:owner)
735
- else
736
- super
768
+ super.tap do |a|
769
+ a << :owner if resource&.owner == logged_in_user
737
770
  end
738
771
  end
739
772
  end
@@ -796,12 +829,6 @@ resource :posts do
796
829
  index(filter_by: [:title, :type]) do
797
830
  Foo # return a Sequel::Dataset (instead of an array of Sequel::Model instances)
798
831
  end
799
-
800
- has_many :comments do
801
- fetch(filter_by: :status) do
802
- resource.comments_dataset # return a Sequel::Dataset
803
- end
804
- end
805
832
  end
806
833
  ```
807
834
 
@@ -1411,8 +1438,8 @@ Sinja applications might grow overly large with a block for each resource. I am
1411
1438
  still working on a better way to handle this (as well as a way to provide
1412
1439
  standalone resource controllers for e.g. cloud functions), but for the time
1413
1440
  being you can store each resource block as its own Proc, and pass it to the
1414
- `resource` keyword in lieu of a block. The migration to some future solution
1415
- should be relatively painless. For example:
1441
+ `resource` keyword as a block. The migration to some future solution should be
1442
+ relatively painless. For example:
1416
1443
 
1417
1444
  ```ruby
1418
1445
  # controllers/foo_controller.rb
@@ -1437,7 +1464,7 @@ require_relative 'controllers/foo_controller'
1437
1464
  class App < Sinatra::Base
1438
1465
  register Sinatra::JSONAPI
1439
1466
 
1440
- resource :foos, FooController
1467
+ resource :foos, &FooController
1441
1468
 
1442
1469
  freeze_jsonapi
1443
1470
  end
@@ -1544,3 +1571,4 @@ License](http://opensource.org/licenses/MIT).
1544
1571
  [29]: https://github.com/brynary/rack-test
1545
1572
  [30]: https://github.com/mwpastore/sinja-sequel
1546
1573
  [31]: http://jsonapi.org/implementations/#server-libraries-ruby
1574
+ [32]: http://emberjs.com
@@ -21,16 +21,17 @@ helpers Sinja::Sequel::Helpers do
21
21
  end
22
22
 
23
23
  def role
24
- [].tap do |a|
25
- a << :logged_in if current_user
26
- a << :superuser if current_user&.admin?
24
+ return unless current_user
25
+
26
+ [:logged_in].tap do |a|
27
+ a << :superuser if current_user.admin?
27
28
  end
28
29
  end
29
30
  end
30
31
 
31
- resource :authors, AuthorController
32
- resource :comments, CommentController
33
- resource :posts, PostController
34
- resource :tags, TagController
32
+ resource :authors, &AuthorController
33
+ resource :comments, &CommentController
34
+ resource :posts, &PostController
35
+ resource :tags, &TagController
35
36
 
36
37
  freeze_jsonapi
@@ -40,7 +40,7 @@ AuthorController = proc do
40
40
  end
41
41
 
42
42
  def role
43
- [*super].tap do |a|
43
+ Array(super).tap do |a|
44
44
  a << :myself if resource == current_user
45
45
  end
46
46
  end
@@ -36,7 +36,7 @@ CommentController = proc do
36
36
  end
37
37
 
38
38
  def role
39
- [*super].tap do |a|
39
+ Array(super).tap do |a|
40
40
  a << :owner if resource&.author == current_user
41
41
  end
42
42
  end
@@ -49,7 +49,7 @@ PostController = proc do
49
49
  end
50
50
 
51
51
  def role
52
- [*super].tap do |a|
52
+ Array(super).tap do |a|
53
53
  a << :owner if resource&.author == current_user
54
54
  end
55
55
  end
@@ -48,7 +48,7 @@ module Sinja
48
48
  index.to_h.all? do |key, subkeys|
49
49
  key = key.to_s
50
50
 
51
- Hash === params[key] && params[key].any? && [*subkeys].all? do |subkey|
51
+ params[key].is_a?(Hash) && params[key].any? && Array(subkeys).all? do |subkey|
52
52
  subkey = subkey.to_s
53
53
 
54
54
  # TODO: What if deleting one is successful, but not another?
@@ -75,29 +75,29 @@ module Sinja
75
75
  # Ignore interal Sinatra query parameters (e.g. :captures) and any
76
76
  # "known" query parameter set to `nil' in the configurable.
77
77
  next if !env['rack.request.query_hash'].key?(key.to_s) ||
78
- settings._sinja.query_params.fetch(key, :__NOT_FOUND__).nil?
78
+ settings._sinja.query_params.fetch(key, BasicObject).nil?
79
79
 
80
80
  raise BadRequestError, "`#{key}' query parameter not allowed" \
81
81
  unless allow_params.include?(key)
82
82
 
83
83
  next if env['sinja.normalized'] == params.object_id
84
84
 
85
- if !(String === settings._sinja.query_params[key]) && String === value
85
+ if value.instance_of?(String) && settings._sinja.query_params[key] != String
86
86
  params[key.to_s] = value.split(',')
87
- elsif !(settings._sinja.query_params[key].class === value)
87
+ elsif !value.is_a?(settings._sinja.query_params[key])
88
88
  raise BadRequestError, "`#{key}' query parameter malformed"
89
89
  end
90
90
  end
91
91
 
92
92
  return true if env['sinja.normalized'] == params.object_id
93
93
 
94
- settings._sinja.query_params.each do |key, default_value|
95
- next if default_value.nil?
94
+ settings._sinja.query_params.each do |key, klass|
95
+ next if klass.nil?
96
96
 
97
97
  if respond_to?("normalize_#{key}_params")
98
- params[key.to_s] = send("normalize_#{key}_params", default_value)
98
+ params[key.to_s] = send("normalize_#{key}_params")
99
99
  else
100
- params[key.to_s] ||= default_value
100
+ params[key.to_s] ||= klass.new
101
101
  end
102
102
  end
103
103
 
@@ -118,7 +118,7 @@ module Sinja
118
118
  def allow(h={})
119
119
  s = Set.new
120
120
  h.each do |method, actions|
121
- s << method if [*actions].all? { |action| respond_to?(action) }
121
+ s << method if Array(actions).all?(&method(:respond_to?))
122
122
  end
123
123
  headers 'Allow'=>s.map(&:upcase).join(',')
124
124
  end
@@ -128,7 +128,7 @@ module Sinja
128
128
  end
129
129
 
130
130
  if method_defined?(:bad_request?)
131
- # This screws up our error-handling logic in Sinatra 2.0, so monkeypatch it.
131
+ # This screws up our error-handling logic in Sinatra 2.0, so override it.
132
132
  # https://github.com/sinatra/sinatra/issues/1211
133
133
  # https://github.com/sinatra/sinatra/pull/1212
134
134
  def bad_request?
@@ -138,7 +138,7 @@ module Sinja
138
138
 
139
139
  def can?(action)
140
140
  roles = settings._resource_config[:resource].fetch(action, {})[:roles]
141
- roles.nil? || roles.empty? || roles === memoized_role
141
+ roles.nil? || roles.empty? || roles.intersect?(memoized_role)
142
142
  end
143
143
 
144
144
  def content?
@@ -154,8 +154,8 @@ module Sinja
154
154
  end
155
155
  end
156
156
 
157
- def normalize_filter_params(default_value)
158
- return default_value unless params[:filter]&.any?
157
+ def normalize_filter_params
158
+ return {} unless params[:filter]&.any?
159
159
 
160
160
  raise BadRequestError, "Unsupported `filter' query parameter(s)" \
161
161
  unless respond_to?(:filter)
@@ -175,7 +175,7 @@ module Sinja
175
175
  raise BadRequestError, "Invalid `filter' query parameter(s)"
176
176
  end
177
177
 
178
- def normalize_sort_params(_default_value)
178
+ def normalize_sort_params
179
179
  return {} unless params[:sort]&.any?
180
180
 
181
181
  raise BadRequestError, "Unsupported `sort' query parameter(s)" \
@@ -197,8 +197,8 @@ module Sinja
197
197
  raise BadRequestError, "Invalid `sort' query parameter(s)"
198
198
  end
199
199
 
200
- def normalize_page_params(default_value)
201
- return default_value unless params[:page]&.any?
200
+ def normalize_page_params
201
+ return {} unless params[:page]&.any?
202
202
 
203
203
  raise BadRequestError, "Unsupported `page' query parameter(s)" \
204
204
  unless respond_to?(:page)
@@ -244,7 +244,7 @@ module Sinja
244
244
  end
245
245
 
246
246
  def memoized_role
247
- @role ||= role
247
+ @role ||= Roles[*role]
248
248
  end
249
249
 
250
250
  def sideloaded?
@@ -256,7 +256,7 @@ module Sinja
256
256
  end
257
257
 
258
258
  def role?(*roles)
259
- Roles[*roles] === memoized_role
259
+ Roles[*roles].intersect?(memoized_role)
260
260
  end
261
261
 
262
262
  def sanity_check!(resource_name, id=nil)
@@ -288,34 +288,37 @@ module Sinja
288
288
  end
289
289
 
290
290
  app.not_found do
291
- serialize_errors(&settings._sinja.error_logger)
291
+ serialize_errors
292
292
  end
293
293
 
294
294
  app.error 400...600 do
295
- serialize_errors(&settings._sinja.error_logger)
295
+ serialize_errors
296
296
  end
297
297
 
298
298
  app.error StandardError do
299
299
  env['sinatra.error'].tap do |e|
300
300
  boom =
301
- if settings._sinja.not_found_exceptions.any? { |c| c === e }
302
- NotFoundError.new(e.message) unless NotFoundError === e
303
- elsif settings._sinja.conflict_exceptions.any? { |c| c === e }
304
- ConflictError.new(e.message) unless ConflictError === e
305
- elsif settings._sinja.validation_exceptions.any? { |c| c === e }
306
- UnprocessibleEntityError.new(settings._sinja.validation_formatter.(e)) unless UnprocessibleEntityError === e
301
+ if settings._sinja.not_found_exceptions.any?(&e.method(:is_a?))
302
+ NotFoundError.new(e.message) unless e.instance_of?(NotFoundError)
303
+ elsif settings._sinja.conflict_exceptions.any?(&e.method(:is_a?))
304
+ ConflictError.new(e.message) unless e.instance_of?(ConflictError)
305
+ elsif settings._sinja.validation_exceptions.any?(&e.method(:is_a?))
306
+ UnprocessibleEntityError.new(settings._sinja.validation_formatter.(e)) unless e.instance_of?(UnprocessibleEntityError)
307
307
  end
308
308
 
309
309
  handle_exception!(boom) if boom # re-throw the re-packaged exception
310
310
  end
311
311
 
312
- serialize_errors(&settings._sinja.error_logger)
312
+ serialize_errors
313
313
  end
314
314
  end
315
315
 
316
316
  def resource(resource_name, konst=nil, &block)
317
317
  abort "Must supply proc constant or block for `resource'" \
318
- unless block = (konst if konst.is_a?(Proc)) || block
318
+ unless block = (konst if konst.instance_of?(Proc)) || block
319
+
320
+ warn "DEPRECATED: Pass a block to `resource'; the ability to pass a Proc " \
321
+ 'will be removed in a future version of Sinja.' if konst.instance_of?(Proc)
319
322
 
320
323
  resource_name = resource_name.to_s
321
324
  .pluralize
@@ -352,7 +355,7 @@ module Sinja
352
355
  end
353
356
  end
354
357
 
355
- alias_method :resources, :resource
358
+ alias resources resource
356
359
 
357
360
  def sinja
358
361
  if block_given?
@@ -362,7 +365,7 @@ module Sinja
362
365
  end
363
366
  end
364
367
 
365
- alias_method :configure_jsonapi, :sinja
368
+ alias configure_jsonapi sinja
366
369
  def freeze_jsonapi
367
370
  _sinja.freeze
368
371
  end
@@ -21,8 +21,8 @@ module Sinja
21
21
  end
22
22
 
23
23
  if c.respond_to?(:values)
24
- c.values.each do |i|
25
- if Hash === i
24
+ c.each_value do |i|
25
+ if i.is_a?(Hash)
26
26
  deep_freeze(i)
27
27
  else
28
28
  i.freeze
@@ -60,11 +60,11 @@ module Sinja
60
60
 
61
61
  def initialize
62
62
  @query_params = {
63
- :include=>[], # passthru to JAS
64
- :fields=>{}, # passthru to JAS
65
- :filter=>{},
66
- :page=>{},
67
- :sort=>[]
63
+ :include=>Array, # passthru to JAS
64
+ :fields=>Hash, # passthru to JAS
65
+ :filter=>Hash,
66
+ :page=>Hash,
67
+ :sort=>Array
68
68
  }
69
69
 
70
70
  @error_logger = ->(h) { logger.error('sinja') { h } }
@@ -93,14 +93,14 @@ module Sinja
93
93
  @validation_exceptions = Set.new
94
94
  @validation_formatter = ->{ Array.new }
95
95
 
96
- @opts = deep_copy(DEFAULT_OPTS)
96
+ @opts = DEFAULT_OPTS.dup
97
97
  @page_using = Hash.new
98
98
  @serializer_opts = deep_copy(DEFAULT_SERIALIZER_OPTS)
99
99
  end
100
100
 
101
101
  def error_logger=(f)
102
102
  fail "Invalid error formatter #{f}" \
103
- unless f.respond_to?(:call)
103
+ unless f.respond_to?(:call) || f.nil?
104
104
 
105
105
  fail "Can't modify frozen proc" \
106
106
  if @error_logger.frozen?
@@ -109,15 +109,15 @@ module Sinja
109
109
  end
110
110
 
111
111
  def conflict_exceptions=(e=[])
112
- @conflict_exceptions.replace([*e])
112
+ @conflict_exceptions.replace(Array(e))
113
113
  end
114
114
 
115
115
  def not_found_exceptions=(e=[])
116
- @not_found_exceptions.replace([*e])
116
+ @not_found_exceptions.replace(Array(e))
117
117
  end
118
118
 
119
119
  def validation_exceptions=(e=[])
120
- @validation_exceptions.replace([*e])
120
+ @validation_exceptions.replace(Array(e))
121
121
  end
122
122
 
123
123
  def validation_formatter=(f)
@@ -154,7 +154,7 @@ module Sinja
154
154
  @default_roles[:has_one].merge!(other)
155
155
  end
156
156
 
157
- DEFAULT_OPTS.keys.each do |k|
157
+ DEFAULT_OPTS.each_key do |k|
158
158
  define_method(k) { @opts[k] }
159
159
  define_method("#{k}=") { |v| @opts[k] = v }
160
160
  end
@@ -188,9 +188,11 @@ module Sinja
188
188
  end
189
189
 
190
190
  class Roles < Set
191
- def ===(other)
192
- self.intersect?(Set === other ? other : Set[*other])
191
+ def intersect?(other)
192
+ super(other.instance_of?(self.class) ? other : self.class[*other])
193
193
  end
194
+
195
+ alias === intersect?
194
196
  end
195
197
 
196
198
  class RolesConfig
@@ -211,7 +213,7 @@ module Sinja
211
213
  h.each do |action, roles|
212
214
  abort "Unknown or invalid action helper `#{action}' in configuration" \
213
215
  unless @data.key?(action)
214
- @data[action].replace([*roles])
216
+ @data[action].replace(Array(roles))
215
217
  end
216
218
  @data
217
219
  end
@@ -74,10 +74,10 @@ module Sinja
74
74
  attr_reader :tuples
75
75
 
76
76
  def initialize(tuples=[])
77
- @tuples = [*tuples]
77
+ @tuples = Array(tuples)
78
78
 
79
79
  fail 'Tuples not properly formatted' \
80
- unless @tuples.any? && @tuples.all? { |t| Array === t && t.length == 2 }
80
+ unless @tuples.any? && @tuples.all? { |t| t.instance_of?(Array) && t.length.between?(2, 3) }
81
81
 
82
82
  super(HTTP_STATUS)
83
83
  end
@@ -11,7 +11,7 @@ module Sinja
11
11
  VALID_PAGINATION_KEYS = Set.new(%i[self first prev next last]).freeze
12
12
 
13
13
  def dedasherize(s=nil)
14
- s.to_s.underscore.send(Symbol === s ? :to_sym : :itself)
14
+ s.to_s.underscore.send(s.instance_of?(Symbol) ? :to_sym : :itself)
15
15
  end
16
16
 
17
17
  def dedasherize_names(*args)
@@ -22,7 +22,7 @@ module Sinja
22
22
  return enum_for(__callee__, hash) unless block_given?
23
23
 
24
24
  hash.each do |k, v|
25
- yield dedasherize(k), Hash === v ? dedasherize_names(v) : v
25
+ yield dedasherize(k), v.is_a?(Hash) ? dedasherize_names(v) : v
26
26
  end
27
27
  end
28
28
 
@@ -48,12 +48,12 @@ module Sinja
48
48
  options.delete(:exclude) || []
49
49
 
50
50
  if included.empty?
51
- included = Array === default ? default : default.split(',')
51
+ included = default.is_a?(Array) ? default : default.split(',')
52
52
 
53
53
  return included if included.empty?
54
54
  end
55
55
 
56
- excluded = Array === excluded ? excluded : excluded.split(',')
56
+ excluded = excluded.is_a?(Array) ? excluded : excluded.split(',')
57
57
  unless excluded.empty?
58
58
  excluded = Set.new(excluded)
59
59
  included.delete_if do |termstr|
@@ -79,28 +79,29 @@ module Sinja
79
79
  # Move cursor through each term, avoiding the default proc,
80
80
  # halting if no roles found, i.e. client asked to include
81
81
  # something that Sinja doesn't know about
82
- throw :keep?, true \
83
- unless config = settings._sinja.resource_config.fetch(term.pluralize.to_sym, nil)
82
+ throw :keep?, true unless config =
83
+ settings._sinja.resource_config.fetch(term.pluralize.to_sym, nil)
84
84
  end
85
85
 
86
- roles = (
87
- config.dig(:has_many, last_term.pluralize.to_sym, :fetch) ||
88
- config.dig(:has_one, last_term.singularize.to_sym, :pluck)
89
- )[:roles]
86
+ throw :keep?, true unless roles =
87
+ config.dig(:has_many, last_term.pluralize.to_sym, :fetch, :roles) ||
88
+ config.dig(:has_one, last_term.singularize.to_sym, :pluck, :roles)
90
89
 
91
- throw :keep?, roles && (roles.empty? || roles === memoized_role)
90
+ throw :keep?, roles && (roles.empty? || roles.intersect?(memoized_role))
92
91
  end
93
92
  end
94
93
  end
95
94
 
96
95
  def serialize_model(model=nil, options={})
97
96
  options[:is_collection] = false
98
- options[:skip_collection_check] = defined?(::Sequel) && ::Sequel::Model === model
97
+ options[:skip_collection_check] = defined?(::Sequel) && model.is_a?(::Sequel::Model)
99
98
  options[:include] = include_exclude!(options)
100
99
  options[:fields] ||= params[:fields] unless params[:fields].empty?
101
100
  options = settings._sinja.serializer_opts.merge(options)
102
101
 
103
102
  ::JSONAPI::Serializer.serialize(model, options)
103
+ rescue ::JSONAPI::Serializer::InvalidIncludeError=>e
104
+ raise BadRequestError, e
104
105
  end
105
106
 
106
107
  def serialize_model?(model=nil, options={})
@@ -122,7 +123,7 @@ module Sinja
122
123
  if pagination
123
124
  # Whitelist pagination keys and dasherize query parameter names
124
125
  pagination = VALID_PAGINATION_KEYS
125
- .select { |outer_key| pagination.key?(outer_key) }
126
+ .select(&pagination.method(:key?))
126
127
  .map! do |outer_key|
127
128
  [outer_key, pagination[outer_key].map do |inner_key, value|
128
129
  [inner_key.to_s.dasherize.to_sym, value]
@@ -151,11 +152,13 @@ module Sinja
151
152
  end.to_h)
152
153
  end
153
154
 
154
- ::JSONAPI::Serializer.serialize([*models], options)
155
+ ::JSONAPI::Serializer.serialize(Array(models), options)
156
+ rescue ::JSONAPI::Serializer::InvalidIncludeError=>e
157
+ raise BadRequestError, e
155
158
  end
156
159
 
157
160
  def serialize_models?(models=[], options={}, pagination=nil)
158
- if [*models].any?
161
+ if Array(models).any?
159
162
  body serialize_models(models, options, pagination)
160
163
  elsif options.key?(:meta)
161
164
  body serialize_models([], :meta=>options[:meta])
@@ -166,7 +169,7 @@ module Sinja
166
169
 
167
170
  def serialize_linkage(model, rel, options={})
168
171
  options[:is_collection] = false
169
- options[:skip_collection_check] = defined?(::Sequel) && ::Sequel::Model === model
172
+ options[:skip_collection_check] = defined?(::Sequel::Model) && model.is_a?(::Sequel::Model)
170
173
  options[:include] = rel.to_s
171
174
  options = settings._sinja.serializer_opts.merge(options)
172
175
 
@@ -202,17 +205,18 @@ module Sinja
202
205
  e.respond_to?(:title) ? e.title : e.class.name.demodulize.titleize
203
206
  end
204
207
 
205
- def serialize_errors(&block)
208
+ def serialize_errors
206
209
  raise env['sinatra.error'] if env['sinatra.error'] && sideloaded?
207
210
 
211
+ abody = Array(body)
208
212
  error_hashes =
209
- if [*body].any?
210
- if [*body].all? { |error| Hash === error }
213
+ if abody.any?
214
+ if abody.all? { |error| error.is_a?(Hash) }
211
215
  # `halt' with a hash or array of hashes
212
- [*body].flat_map { |error| error_hash(error) }
216
+ abody.flat_map(&method(:error_hash))
213
217
  elsif not_found?
214
218
  # `not_found' or `halt 404'
215
- message = [*body].first.to_s
219
+ message = abody.first.to_s
216
220
  error_hash \
217
221
  :title=>'Not Found Error',
218
222
  :detail=>(message unless message == '<h1>Not Found</h1>')
@@ -220,7 +224,7 @@ module Sinja
220
224
  # `halt'
221
225
  error_hash \
222
226
  :title=>'Unknown Error',
223
- :detail=>[*body].first.to_s
227
+ :detail=>abody.first.to_s
224
228
  end
225
229
  end
226
230
 
@@ -231,12 +235,12 @@ module Sinja
231
235
  error_hashes ||=
232
236
  case e = env['sinatra.error']
233
237
  when UnprocessibleEntityError
234
- e.tuples.flat_map do |attribute, full_message|
238
+ e.tuples.flat_map do |key, full_message, type=:attributes|
235
239
  error_hash \
236
240
  :title=>exception_title(e),
237
241
  :detail=>full_message.to_s,
238
242
  :source=>{
239
- :pointer=>(attribute ? "/data/attributes/#{attribute.to_s.dasherize}" : '/data')
243
+ :pointer=>(key ? "/data/#{type}/#{key.to_s.dasherize}" : '/data')
240
244
  }
241
245
  end
242
246
  when Exception
@@ -248,7 +252,9 @@ module Sinja
248
252
  :title=>'Unknown Error'
249
253
  end
250
254
 
251
- error_hashes.each { |h| instance_exec(h, &block) } if block
255
+ if block = settings._sinja.error_logger
256
+ error_hashes.each { |h| instance_exec(h, &block) }
257
+ end
252
258
 
253
259
  content_type :api_json
254
260
  JSON.send settings._sinja.json_error_generator,
@@ -27,10 +27,10 @@ module Sinja
27
27
 
28
28
  context.define_singleton_method(action) do |**opts, &block|
29
29
  abort "Unexpected option(s) for `#{action}' action helper" \
30
- unless (opts.keys - [*allow_opts]).empty?
30
+ unless (opts.keys - Array(allow_opts)).empty?
31
31
 
32
32
  resource_config[action].each do |k, v|
33
- v.replace([*opts[k]]) if opts.key?(k)
33
+ v.replace(Array(opts[k])) if opts.key?(k)
34
34
  end
35
35
 
36
36
  return unless block ||=
@@ -57,9 +57,9 @@ module Sinja
57
57
  case result = instance_exec(*args, &block)
58
58
  when Array
59
59
  opts = {}
60
- if Hash === result.last
60
+ if result.last.instance_of?(Hash)
61
61
  opts = result.pop
62
- elsif required_arity < 0 && !(Array === result.first)
62
+ elsif required_arity < 0 && !result.first.is_a?(Array)
63
63
  result = [result]
64
64
  end
65
65
 
@@ -97,7 +97,7 @@ module Sinja
97
97
  parent = sideloaded? && env['sinja.passthru'].to_sym
98
98
 
99
99
  roles, sideload_on = config.fetch(action, {}).values_at(:roles, :sideload_on)
100
- roles.nil? || roles.empty? || roles === memoized_role ||
100
+ roles.nil? || roles.empty? || roles.intersect?(memoized_role) ||
101
101
  parent && sideload_on.include?(parent) && super(parent, *args)
102
102
  end
103
103
 
@@ -15,8 +15,8 @@ module Sinja
15
15
 
16
16
  app.get '', :qcaptures=>{ :filter=>:id }, :qparams=>%i[include fields], :actions=>:show do
17
17
  ids = @qcaptures.first # TODO: Get this as a block parameter?
18
- ids = ids.split(',') if String === ids
19
- ids = [*ids].tap(&:uniq!)
18
+ ids = ids.split(',') if ids.instance_of?(String)
19
+ ids = Array(ids).tap(&:uniq!)
20
20
 
21
21
  resources, opts =
22
22
  if respond_to?(:show_many)
@@ -49,7 +49,7 @@ module Sinja
49
49
  serialize_models(collection, opts, pagination)
50
50
  end
51
51
 
52
- app.post '', :qparams=>:include, :actions=>:create do
52
+ app.post '', :qparams=>%i[include fields], :actions=>:create do
53
53
  sanity_check!
54
54
 
55
55
  opts = {}
@@ -92,7 +92,7 @@ module Sinja
92
92
  serialize_model(tmp, opts)
93
93
  end
94
94
 
95
- app.patch '/:id', :qparams=>:include, :actions=>:update do |id|
95
+ app.patch '/:id', :qparams=>%i[include fields], :actions=>:update do |id|
96
96
  sanity_check!(id)
97
97
  tmp, opts = transaction do
98
98
  update(attributes).tap do
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Sinja
3
- VERSION = '1.2.2'
3
+ VERSION = '1.2.3'
4
4
  end
@@ -18,15 +18,16 @@ Gem::Specification.new do |spec|
18
18
  and it configures Sinatra with the proper settings, MIME-types, filters,
19
19
  conditions, and error-handling.
20
20
 
21
- There are many parsing (deserializing) and rendering (serializing) and
22
- so-called "JSON API" libraries available for Ruby, but relatively few that
23
- attempt to correctly implement the entire {json:api} specification,
24
- including routing, request header and query parameter checking, and
25
- relationship side-loading. Sinja lets you focus on the business logic of
26
- your applications without worrying about the specification, and without
27
- pulling in a heavy framework like Rails. It's lightweight and ORM-agnostic!
21
+ There are many parsing (deserializing), rendering (serializing), and other
22
+ "JSON API" libraries available for Ruby, but relatively few that attempt to
23
+ correctly implement the entire {json:api} server specification, including
24
+ routing, request header and query parameter checking, and relationship
25
+ side-loading. Sinja lets you focus on the business logic of your
26
+ applications without worrying about the specification, and without pulling
27
+ in a heavy framework like Rails. It's lightweight, ORM-agnostic, and
28
+ Ember.js-friendly!
28
29
  EOF
29
- spec.homepage = 'https://github.com/mwpastore/sinja'
30
+ spec.homepage = 'http://sinja-rb.org'
30
31
  spec.license = 'MIT'
31
32
 
32
33
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinja
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Pastore
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-18 00:00:00.000000000 Z
11
+ date: 2017-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -216,13 +216,14 @@ description: |
216
216
  and it configures Sinatra with the proper settings, MIME-types, filters,
217
217
  conditions, and error-handling.
218
218
 
219
- There are many parsing (deserializing) and rendering (serializing) and
220
- so-called "JSON API" libraries available for Ruby, but relatively few that
221
- attempt to correctly implement the entire {json:api} specification,
222
- including routing, request header and query parameter checking, and
223
- relationship side-loading. Sinja lets you focus on the business logic of
224
- your applications without worrying about the specification, and without
225
- pulling in a heavy framework like Rails. It's lightweight and ORM-agnostic!
219
+ There are many parsing (deserializing), rendering (serializing), and other
220
+ "JSON API" libraries available for Ruby, but relatively few that attempt to
221
+ correctly implement the entire {json:api} server specification, including
222
+ routing, request header and query parameter checking, and relationship
223
+ side-loading. Sinja lets you focus on the business logic of your
224
+ applications without worrying about the specification, and without pulling
225
+ in a heavy framework like Rails. It's lightweight, ORM-agnostic, and
226
+ Ember.js-friendly!
226
227
  email:
227
228
  - mike@oobak.org
228
229
  executables: []
@@ -264,7 +265,7 @@ files:
264
265
  - lib/sinja/resource_routes.rb
265
266
  - lib/sinja/version.rb
266
267
  - sinja.gemspec
267
- homepage: https://github.com/mwpastore/sinja
268
+ homepage: http://sinja-rb.org
268
269
  licenses:
269
270
  - MIT
270
271
  metadata: {}