sinja 1.1.0.pre1 → 1.1.0.pre2

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: 4491b24eba74ba902735fc26ff679b074bc5a76c
4
- data.tar.gz: 54f55fd1b953b7dd31b5bb911386bb6f1bf83419
3
+ metadata.gz: d785d25fe6c9bd3e61893ca5a1ec23b237d9e3cb
4
+ data.tar.gz: 0eb4c07d1691587fb117ebbfc6d2fb63a911ae81
5
5
  SHA512:
6
- metadata.gz: 362031551256e0e28912b13447825269388a54ff698624027ae44e557fc37489686c11427fe43c84b5983d23f8317772c58f5d0185c8373df1e4f43698abf90a
7
- data.tar.gz: f282eef01caa1c271f7555f1121f3d2d7fba5e65962a20c10e8e688e380ec67930be8b95002eaad734de1ed9b573421cadaa95b77af6bcea9ef64dd3975e8bf7
6
+ metadata.gz: 0ee40f0a379063b13846967163f181c50ec7c86303155864ecf234369e41faf1d84006c3ac4d07aeff6df13cfcd7ae1010b8055e59b9d0e06d98e666700159aa
7
+ data.tar.gz: 6c82ef3fc4fba1670526d5f2aab3fa09ea76921cfe2c8de997f8b13fd52b110fcd45ea34c03bbfaeddc7c43dd76ded5936f71bd5acc1e12f73c47058331e280c
data/README.md CHANGED
@@ -35,6 +35,7 @@ the {json:api} specification is).
35
35
  - [`resource`](#resource)
36
36
  - [`index {..}` => Array](#index---array)
37
37
  - [`show {|id| ..}` => Object](#show-id---object)
38
+ - [`show_many {|ids| ..}` => Array](#show_many-ids---array)
38
39
  - [`create {|attr, id| ..}` => id, Object?](#create-attr-id---id-object)
39
40
  - [`create {|attr| ..}` => id, Object](#create-attr---id-object)
40
41
  - [`update {|attr| ..}` => Object?](#update-attr---object)
@@ -114,7 +115,7 @@ all other {json:api} endpoints returning 404 or 405):
114
115
  * `POST /posts`
115
116
 
116
117
  The resource locator and other action helpers, documented below, enable other
117
- endpoints. Please see the [demo-app](/demo-app) for more complete examples.
118
+ endpoints.
118
119
 
119
120
  Of course, "modular"-style Sinatra aplications require you to register the
120
121
  extension:
@@ -134,6 +135,8 @@ class App < Sinatra::Base
134
135
  end
135
136
  ```
136
137
 
138
+ Please see the [demo-app](/demo-app) for a more complete example.
139
+
137
140
  ## Installation
138
141
 
139
142
  Add this line to your application's Gemfile:
@@ -172,11 +175,11 @@ with the JsonApi adapter, [JSONAPI::Resources][8] (JR), and
172
175
  [jsonapi-utils][26], all of which are designed to work with [Rails][16] and
173
176
  [ActiveRecord][17]/[ActiveModel][18] (although they may work with [Sequel][13]
174
177
  via [sequel-rails][14] and Sequel's [`:active_model` plugin][15]). Otherwise,
175
- you might use something like Sinatra, [Roda][20], or [Grape][19] with
176
- JSONAPI::Serializers (or another (de)serialization library), your own routes,
177
- and a ton of boilerplate. The goal of this extension is to provide most or all
178
- of the boilerplate for a Sintara application and automate the drawing of routes
179
- based on the resource definitions.
178
+ you might use something like Sinatra, [Roda][20], or [Grape][19] with a
179
+ (de)serialization library, your own routes, and a ton of boilerplate. The goal
180
+ of this extension is to provide most or all of the boilerplate for a Sintara
181
+ application and automate the drawing of routes based on the resource
182
+ definitions.
180
183
 
181
184
  ### Ol' Blue Eyes is Back
182
185
 
@@ -836,7 +839,7 @@ resource :foos do
836
839
  end
837
840
  ```
838
841
 
839
- Please see the [demo-app](/demo-app) for more complete examples.
842
+ Please see the [demo-app](/demo-app) for a more complete example.
840
843
 
841
844
  ### Query Parameters
842
845
 
@@ -847,8 +850,8 @@ a per-route whitelist, and interrogates your application to see which features
847
850
  it supports; for example, a route may allow a `filter` query parameter, but you
848
851
  may not have defined a `filter` helper.
849
852
 
850
- To allow a custom query parameter through, add it to the `query_params`
851
- configurable with a `nil` value:
853
+ To let a custom query parameter through to the standard action helpers, add it
854
+ to the `query_params` configurable with a `nil` value:
852
855
 
853
856
  ```ruby
854
857
  configure_jsonapi do |c|
@@ -898,6 +901,23 @@ resource :posts do
898
901
  end
899
902
  ```
900
903
 
904
+ The easiest way to set a default filter is to tweak the post-processed query
905
+ parameter(s) in a `before_<action>` hook:
906
+
907
+ ```ruby
908
+ resource :posts do
909
+ helpers do
910
+ def before_index
911
+ params['filter']['type'] ||= 'article'
912
+ end
913
+ end
914
+
915
+ index do
916
+ # ..
917
+ end
918
+ end
919
+ ```
920
+
901
921
  #### Sorting
902
922
 
903
923
  Allow clients to sort the collections returned by the `index` and `fetch`
@@ -924,6 +944,23 @@ resource :posts do
924
944
  end
925
945
  ```
926
946
 
947
+ The easiest way to set a default sort order is to tweak the post-processed
948
+ query parameter(s) in a `before_<action>` hook:
949
+
950
+ ```ruby
951
+ resource :posts do
952
+ helpers do
953
+ def before_index
954
+ params['sort'] << [:title, :asc] if params['sort'].empty?
955
+ end
956
+ end
957
+
958
+ index do
959
+ # ..
960
+ end
961
+ end
962
+ ```
963
+
927
964
  #### Paging
928
965
 
929
966
  Allow clients to page the collections returned by the `index` and `fetch`
@@ -965,6 +1002,23 @@ and `:size` for the above example) along with their default values (or `nil`).
965
1002
  Please see the [Sequel helpers](/lib/sinja/helpers/sequel.rb) in this
966
1003
  repository for a detailed, working example.
967
1004
 
1005
+ The easiest way to page a collection by default is to tweak the post-processed
1006
+ query parameter(s) in a `before_<action>` hook:
1007
+
1008
+ ```ruby
1009
+ resource :posts do
1010
+ helpers do
1011
+ def before_index
1012
+ params['page']['number'] ||= 1
1013
+ end
1014
+ end
1015
+
1016
+ index do
1017
+ # ..
1018
+ end
1019
+ end
1020
+ ```
1021
+
968
1022
  #### Finalizing
969
1023
 
970
1024
  If you need to perform any additional actions on a collection after it is
@@ -1123,7 +1177,8 @@ either `graft` or `create`.
1123
1177
  `graft`, `merge`, and `clear` are the only action helpers invoked by
1124
1178
  sideloading. You must indicate which combinations are valid using the
1125
1179
  `:sideload_on` action helper option. (Note that if you want to sideload `merge`
1126
- on `update`, you must define a `clear` action helper as well.) For example:
1180
+ on `update`, you must define a `clear` action helper and allow it to sideload
1181
+ on `update` as well.) For example:
1127
1182
 
1128
1183
  ```ruby
1129
1184
  resource :photos do
@@ -1259,7 +1314,26 @@ status 404.
1259
1314
  Optionally, to reduce round trips to the database, you may define a "special"
1260
1315
  `show_many` action helper that takes an array of IDs to show. It does not take
1261
1316
  `:roles` or any other options and will only be invoked if the current user has
1262
- access to `show`. This feature is still experimental.
1317
+ access to `show`. This feature is experimental.
1318
+
1319
+ Collections assembled during coalesced find requests will not be filtered,
1320
+ sorted, or paged. The easiest way to limit the number of records that can be
1321
+ queried is to define a `show_many` action helper and validate the length of the
1322
+ passed array in the `before_show_many` hook:
1323
+
1324
+ ```ruby
1325
+ resource :foos do
1326
+ helpers do
1327
+ def before_show_many(ids)
1328
+ halt 413, 'You want the impossible.' if ids.length > 50
1329
+ end
1330
+ end
1331
+
1332
+ show_many do |ids|
1333
+ # ..
1334
+ end
1335
+ end
1336
+ ```
1263
1337
 
1264
1338
  ### Patchless Clients
1265
1339
 
data/lib/sinja.rb CHANGED
@@ -42,10 +42,8 @@ module Sinja
42
42
  define_singleton_method(:resource_config) { config[:resource] }
43
43
 
44
44
  helpers do
45
- %i[can? sanity_check! sideload?].each do |meth|
46
- define_method(meth) do |*args|
47
- super(resource_name, *args)
48
- end
45
+ define_method(:sanity_check!) do |*args|
46
+ super(resource_name, *args)
49
47
  end
50
48
  end
51
49
 
@@ -93,7 +91,7 @@ module Sinja
93
91
  condition do
94
92
  actions.each do |action|
95
93
  raise ForbiddenError, 'You are not authorized to perform this action' \
96
- unless can?(action) || sideload?(action)
94
+ unless can?(action)
97
95
  raise MethodNotAllowedError, 'Action or method not implemented or supported' \
98
96
  unless respond_to?(action)
99
97
  end
@@ -178,14 +176,9 @@ module Sinja
178
176
  end
179
177
  end
180
178
 
181
- def can?(resource_name, action, rel_type=nil, rel=nil)
182
- config = settings._sinja.resource_config[resource_name]
183
- config = rel_type && rel ? config[rel_type][rel] : config[:resource]
184
- # JRuby issues with nil default_proc (fixed in 9.1.7.0?)
185
- # https://github.com/jruby/jruby/issues/4302
186
- #roles = config&.dig(action, :roles)
187
- roles = config&.key?(action) && config[action][:roles]
188
- !roles || roles.empty? || roles === memoized_role
179
+ def can?(action)
180
+ roles = settings._resource_config[:resource].fetch(action, {})[:roles]
181
+ roles.nil? || roles.empty? || roles === memoized_role
189
182
  end
190
183
 
191
184
  def content?
@@ -204,7 +197,7 @@ module Sinja
204
197
  def normalize_filter_params
205
198
  return {} unless params[:filter]&.any?
206
199
 
207
- halt 400, "Unsupported `filter' query parameter(s)" \
200
+ raise BadRequestError, "Unsupported `filter' query parameter(s)" \
208
201
  unless respond_to?(:filter)
209
202
 
210
203
  params[:filter].map do |k, v|
@@ -212,10 +205,17 @@ module Sinja
212
205
  end.to_h
213
206
  end
214
207
 
208
+ def filter_by?(action)
209
+ return true if settings.resource_config[action][:filter_by].empty? ||
210
+ params[:filter].keys.to_set.subset?(settings.resource_config[action][:filter_by])
211
+
212
+ raise BadRequestError, "Invalid `filter' query parameter(s)"
213
+ end
214
+
215
215
  def normalize_sort_params
216
216
  return [] unless params[:sort]&.any?
217
217
 
218
- halt 400, "Unsupported `sort' query parameter(s)" \
218
+ raise BadRequestError, "Unsupported `sort' query parameter(s)" \
219
219
  unless respond_to?(:sort)
220
220
 
221
221
  params[:sort].map do |k|
@@ -224,41 +224,40 @@ module Sinja
224
224
  end.to_h
225
225
  end
226
226
 
227
+ def sort_by?(action)
228
+ return true if settings.resource_config[action][:sort_by].empty? ||
229
+ params[:sort].keys.to_set.subset?(settings.resource_config[action][:sort_by])
230
+
231
+ raise BadRequestError, "Invalid `sort' query parameter(s)"
232
+ end
233
+
227
234
  def normalize_page_params
228
235
  return {} unless params[:page]&.any?
229
236
 
230
- halt 400, "Unsupported `page' query parameter(s)" \
237
+ raise BadRequestError, "Unsupported `page' query parameter(s)" \
231
238
  unless respond_to?(:page)
232
239
 
233
- h = params[:page].map do |k, v|
240
+ params[:page].map do |k, v|
234
241
  [dedasherize(k).to_sym, v]
235
242
  end.to_h
236
-
237
- return h if h.keys.to_set.subset?(settings._sinja.page_using.keys.to_set)
238
-
239
- halt 400, "Invalid `page' query parameter(s)"
240
243
  end
241
244
 
242
- def filter_sort_page(collection, action)
243
- unless params[:filter].empty?
244
- halt 400, "Invalid `filter' query parameter(s)" \
245
- unless settings.resource_config[action][:filter_by].empty? ||
246
- params[:filter].keys.to_set.subset?(settings.resource_config[action][:filter_by])
245
+ def page_using?
246
+ return true if params[:page].keys.to_set.subset?(settings._sinja.page_using.keys.to_set)
247
247
 
248
- collection = filter(collection, params[:filter])
249
- end
250
-
251
- unless params[:sort].empty?
252
- halt 400, "Invalid `sort' query parameter(s)" \
253
- unless settings.resource_config[action][:sort_by].empty? ||
254
- params[:sort].keys.to_set.subset?(settings.resource_config[action][:sort_by])
248
+ raise BadRequestError, "Invalid `page' query parameter(s)"
249
+ end
255
250
 
256
- collection = sort(collection, params[:sort])
257
- end
251
+ def filter_sort_page?(action)
252
+ filter_by?(action) unless params[:filter].empty?
253
+ sort_by?(action) unless params[:sort].empty?
254
+ page_using? unless params[:page].empty?
255
+ end
258
256
 
259
- unless params[:page].empty?
260
- collection, pagination = page(collection, params[:page])
261
- end
257
+ def filter_sort_page(collection)
258
+ collection = filter(collection, params[:filter]) unless params[:filter].empty?
259
+ collection = sort(collection, params[:sort]) unless params[:sort].empty?
260
+ collection, pagination = page(collection, params[:page]) unless params[:page].empty?
262
261
 
263
262
  collection = finalize(collection) if respond_to?(:finalize)
264
263
 
@@ -279,12 +278,6 @@ module Sinja
279
278
  @role ||= role
280
279
  end
281
280
 
282
- def sideload?(resource_name, child)
283
- return unless sideloaded?
284
- parent = env['sinja.passthru'].to_sym
285
- settings.resource_config[child][:sideload_on]&.include?(parent) && can?(parent)
286
- end
287
-
288
281
  def sideloaded?
289
282
  env.key?('sinja.passthru')
290
283
  end
@@ -27,15 +27,15 @@ module Sinja
27
27
  ::Sequel::DATABASES.first
28
28
  end
29
29
 
30
- def filter(collection, **fields)
30
+ def filter(collection, fields)
31
31
  collection.where(fields)
32
32
  end
33
33
 
34
- def sort(collection, **fields)
34
+ def sort(collection, fields)
35
35
  collection.order(*fields.map { |k, v| ::Sequel.send(v, k) })
36
36
  end
37
37
 
38
- def page(collection, **opts)
38
+ def page(collection, opts)
39
39
  opts = settings._sinja.page_using.merge(opts)
40
40
  collection = collection.dataset \
41
41
  unless collection.respond_to?(:paginate)
@@ -23,8 +23,9 @@ module Sinja
23
23
  end
24
24
 
25
25
  app.get '', :qparams=>%i[include fields filter sort page], :actions=>:fetch do
26
+ filter_sort_page?(:fetch)
26
27
  collection, opts = fetch
27
- collection, links = filter_sort_page(collection, :fetch)
28
+ collection, links = filter_sort_page(collection)
28
29
  (opts[:links] ||= {}).merge!(links)
29
30
  serialize_models(collection, opts)
30
31
  end
@@ -92,8 +92,12 @@ module Sinja
92
92
  define_singleton_method(:resource_config) { config }
93
93
 
94
94
  helpers Helpers::Nested do
95
- define_method(:can?) do |*args|
96
- super(*args, rel_type, rel)
95
+ define_method(:can?) do |action, *args|
96
+ parent = sideloaded? && env['sinja.passthru'].to_sym
97
+
98
+ roles, sideload_on = config.fetch(action, {}).values_at(:roles, :sideload_on)
99
+ roles.nil? || roles.empty? || roles === memoized_role ||
100
+ parent && sideload_on.include?(parent) && super(parent, *args)
97
101
  end
98
102
 
99
103
  define_method(:serialize_linkage) do |*args|
@@ -18,21 +18,20 @@ module Sinja
18
18
  ids = ids.split(',') if String === ids
19
19
  ids = [*ids].tap(&:uniq!)
20
20
 
21
+ resources, opts = [], {}
21
22
  if respond_to?(:show_many)
22
23
  resources, opts = show_many(ids)
23
24
  raise NotFoundError, "Resource(s) not found" \
24
25
  unless ids.length == resources.length
25
- serialize_models(resources, opts)
26
26
  else
27
- opts = {}
28
- resources = ids.map! do |id|
27
+ ids.each do |id|
29
28
  tmp, opts = show(id)
30
29
  raise NotFoundError, "Resource '#{id}' not found" unless tmp
31
- tmp
30
+ resources << tmp
32
31
  end
33
-
34
- serialize_models(resources, opts)
35
32
  end
33
+
34
+ serialize_models(resources, opts)
36
35
  end
37
36
 
38
37
  app.head '' do
@@ -40,8 +39,9 @@ module Sinja
40
39
  end
41
40
 
42
41
  app.get '', :qparams=>%i[include fields filter sort page], :actions=>:index do
42
+ filter_sort_page?(:index)
43
43
  collection, opts = index
44
- collection, links = filter_sort_page(collection, :index)
44
+ collection, links = filter_sort_page(collection)
45
45
  (opts[:links] ||= {}).merge!(links)
46
46
  serialize_models(collection, opts)
47
47
  end
data/lib/sinja/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Sinja
3
- VERSION = '1.1.0.pre1'
3
+ VERSION = '1.1.0.pre2'
4
4
  end
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.1.0.pre1
4
+ version: 1.1.0.pre2
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-06 00:00:00.000000000 Z
11
+ date: 2016-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport