sinja 0.1.0.beta1 → 0.1.0.beta2

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
  SHA1:
3
- metadata.gz: b8ac4687f2a8eea409209c9da5ac107ccdb2b886
4
- data.tar.gz: c5e3e902e1e54a0e93a0888411d09e8046e80a20
3
+ metadata.gz: 56256224f31086937e46be0e727db8ec8ae05cc7
4
+ data.tar.gz: a9216fc46b80a2d4aede971848d749859f7358f1
5
5
  SHA512:
6
- metadata.gz: 370e3e8f1f13a4ee3f216a1d3bf92c16b0fbf343e317d7774f45b6287479be69d59055ae690266ac8f570d514e60ae7c5051fa534af8f6ecb2fd13eae13105ec
7
- data.tar.gz: 823218b82a74b85566d10a3df1d1ac7ef92ffb3b110c19f739dd5eec39c53d289df4a23e101ba29ff3f3cbc59fff702c64f0db00de5b57d72a183907756085c6
6
+ metadata.gz: d6045cbbca2b859e33bc66730139b1e8fc6fe3fa5a95004c138c4d1f739a876148ea0eff2da525ab9a064fccee2e8d5aa0be5452d084f99b9fb2a9b5cb233ea6
7
+ data.tar.gz: e2bc9f2014b3ebc582705f636943f543a6e5ce096ca3916d631a77f8b7c1fe3582499deebc096504872ad9732f996027f954fd676e11e565049c2cab11a998af
data/README.md CHANGED
@@ -81,6 +81,9 @@ resource :posts do
81
81
  create do |attr|
82
82
  Post.create(attr)
83
83
  end
84
+
85
+ has_one :author
86
+ has_many :comments
84
87
  end
85
88
 
86
89
  freeze_jsonapi
@@ -92,6 +95,10 @@ all other JSON:API endpoints returning 404 or 405):
92
95
 
93
96
  * `GET /posts`
94
97
  * `GET /posts/<id>`
98
+ * `GET /posts/<id>/author`
99
+ * `GET /posts/<id>/comments`
100
+ * `GET /posts/<id>/relationships/author`
101
+ * `GET /posts/<id>/relationships/comments`
95
102
  * `POST /posts`
96
103
 
97
104
  Of course, "modular"-style Sinatra aplications require you to register the
@@ -267,6 +274,15 @@ end
267
274
  and returns a serialized collection if non-empty, or the root metadata if
268
275
  present, or a HTTP status 204.
269
276
 
277
+ **dedasherize**
278
+ : Takes a string or symbol and returns the string or symbol with any and all
279
+ dashes transliterated to underscores.
280
+
281
+ **dedasherize_names**
282
+ : Takes a hash and returns the hash with its keys dedasherized (deeply).
283
+ Useful for fixing up the hashes of attributes passed to the `create` and
284
+ `update` action helpers before they are passed on to ORM methods.
285
+
270
286
  ### Performance
271
287
 
272
288
  Although there is some heavy metaprogramming happening at boot time, the end
@@ -384,7 +400,8 @@ below. Implicitly return the expected values as described below (as an array if
384
400
  necessary) or use the `next` keyword (instead of `return` or `break`) to exit
385
401
  the action helper. Return values marked with a question mark below may be
386
402
  omitted entirely. Any helper may additionally return an options hash to pass
387
- along to JSONAPI::Serializers.
403
+ along to JSONAPI::Serializer.serialize (will be merged into the global
404
+ `serializer_opts` described above).
388
405
 
389
406
  The `:include` and `:fields` query parameters are automatically passed through
390
407
  to JSONAPI::Serializers. You may also use the special `:exclude` option to
@@ -26,36 +26,38 @@ module Sinatra::JSONAPI
26
26
  [resource.pk, resource, opts]
27
27
  end
28
28
 
29
- def add_missing(association, *args)
30
- meth = "add_#{singularize(association)}".to_sym
31
- transaction do
32
- resource.lock!
33
- venn(:-, association, *args) do |subresource|
34
- resource.send(meth, subresource)
35
- end
36
- resource.reload
37
- end
29
+ # <= association, rios, extra_keys
30
+ def add_missing(*args)
31
+ add_remove(:add, :-, *args)
32
+ end
33
+
34
+ # <= association, rios, extra_keys
35
+ def remove_present(*args)
36
+ add_remove(:remove, :&, *args)
38
37
  end
39
38
 
40
- def remove_present(association, *args)
41
- meth = "remove_#{singularize(association)}".to_sym
39
+ private
40
+
41
+ def add_remove(meth_prefix, operator, association, rios)
42
+ meth = "#{meth_prefix}_#{singularize(association)}".to_sym
42
43
  transaction do
43
44
  resource.lock!
44
- venn(:&, association, *args) do |subresource|
45
- resource.send(meth, subresource)
45
+ venn(operator, association, rios) do |subresource, rio|
46
+ args = [subresource]
47
+ args.push(yield(rio)) if block_given?
48
+ resource.send(meth, *args)
46
49
  end
47
50
  resource.reload
48
51
  end
49
52
  end
50
53
 
51
- private
52
-
53
54
  def venn(operator, association, rios)
54
- klass = resource.class.association_reflection(association) # get e.g. ProductType for :types
55
55
  dataset = resource.send("#{association}_dataset")
56
- rios.map { |rio| rio[:id] }.tap(&:uniq!) # unique PKs in request payload
57
- .send(operator, dataset.select_map(klass.primary_key)) # set operation with existing PKs in dataset
58
- .each { |id| yield klass.with_pk!(id) } # TODO: return 404 if not found?
56
+ klass = resource.class.association_reflection(association)
57
+ rios = rios.map { |rio| [rio[:id], rio] }.to_h
58
+ rios.keys.send(operator, dataset.select_map(klass.primary_key)).each do |id|
59
+ yield klass.with_pk!(id), rios[id]
60
+ end
59
61
  end
60
62
  end
61
63
  end
@@ -6,8 +6,24 @@ require 'set'
6
6
  module Sinatra::JSONAPI
7
7
  module Helpers
8
8
  module Serializers
9
+ def dedasherize(s=nil)
10
+ s.to_s.tr('-', '_').send(Symbol === s ? :to_sym : :itself)
11
+ end
12
+
13
+ def dedasherize_names(*args)
14
+ _dedasherize_names(*args).to_h
15
+ end
16
+
17
+ private def _dedasherize_names(hash={})
18
+ return enum_for(__callee__) unless block_given?
19
+
20
+ hash.each do |k, v|
21
+ yield dasherize(k), Hash === v ? dedasherize_names(v) : v
22
+ end
23
+ end
24
+
9
25
  def deserialized_request_body
10
- return {} unless request.body.respond_to?(:size) && request.body.size > 0
26
+ return {} unless content?
11
27
 
12
28
  request.body.rewind
13
29
  JSON.parse(request.body.read, :symbolize_names=>true)
@@ -40,6 +56,8 @@ module Sinatra::JSONAPI
40
56
  def serialize_model(model=nil, options={})
41
57
  options[:is_collection] = false
42
58
  options[:skip_collection_check] = defined?(::Sequel) && model.is_a?(::Sequel::Model)
59
+ # TODO: This should allow a default include, take the param value if
60
+ # present, and support disabling passthru.
43
61
  options[:include] ||= params[:include] unless params[:include].empty?
44
62
  options[:fields] ||= params[:fields] unless params[:fields].empty?
45
63
 
@@ -61,6 +79,8 @@ module Sinatra::JSONAPI
61
79
 
62
80
  def serialize_models(models=[], options={})
63
81
  options[:is_collection] = true
82
+ # TODO: This should allow a default include, take the param value if
83
+ # present, and support disabling passthru.
64
84
  options[:include] ||= params[:include] unless params[:include].empty?
65
85
  options[:fields] ||= params[:fields] unless params[:fields].empty?
66
86
 
@@ -108,7 +128,7 @@ module Sinatra::JSONAPI
108
128
  elsif detail = [*body].first
109
129
  end
110
130
 
111
- { title: title, detail: detail }
131
+ { :title=>title, :detail=>detail }
112
132
  end
113
133
 
114
134
  def error_hash(title: nil, detail: nil, source: nil)
@@ -111,7 +111,7 @@ module Sinatra::JSONAPI
111
111
  end
112
112
 
113
113
  before do
114
- not_found unless resource
114
+ not_found 'Parent resource not found' unless resource
115
115
  end
116
116
 
117
117
  get '' do
@@ -13,7 +13,7 @@ module Sinatra::JSONAPI
13
13
 
14
14
  app.get '/:id', :actions=>:show do |id|
15
15
  self.resource, opts = show(id)
16
- not_found unless resource
16
+ not_found "Resource '#{id}' not found" unless resource
17
17
  serialize_model(resource, opts)
18
18
  end
19
19
 
@@ -44,7 +44,7 @@ module Sinatra::JSONAPI
44
44
  app.patch '/:id', :actions=>%i[show update] do |id|
45
45
  sanity_check!(id)
46
46
  self.resource, = show(id)
47
- not_found unless resource
47
+ not_found "Resource '#{id}' not found" unless resource
48
48
  serialize_model?(transaction do
49
49
  update(data.fetch(attributes, {})).tap do
50
50
  dispatch_relationship_requests!(id, :method=>:patch)
@@ -54,7 +54,7 @@ module Sinatra::JSONAPI
54
54
 
55
55
  app.delete '/:id', :actions=>%i[show destroy] do |id|
56
56
  self.resource, = show(id)
57
- not_found unless resource
57
+ not_found "Resource '#{id}' not found" unless resource
58
58
  _, opts = destroy
59
59
  serialize_model?(nil, opts)
60
60
  end
@@ -78,6 +78,10 @@ module Sinatra::JSONAPI
78
78
  roles.nil? || roles.empty? || Set[*role].intersect?(roles)
79
79
  end
80
80
 
81
+ def content?
82
+ request.body.respond_to?(:size) && request.body.size > 0
83
+ end
84
+
81
85
  def data
82
86
  @data ||= deserialized_request_body[:data]
83
87
  end
@@ -104,8 +108,11 @@ module Sinatra::JSONAPI
104
108
 
105
109
  app.before do
106
110
  halt 406 unless request.preferred_type.entry == MIME_TYPE
107
- halt 415 unless request.media_type == MIME_TYPE
108
- halt 415 if request.media_type_params.keys.any? { |k| k != 'charset' }
111
+
112
+ if content?
113
+ halt 415 unless request.media_type == MIME_TYPE
114
+ halt 415 if request.media_type_params.keys.any? { |k| k != 'charset' }
115
+ end
109
116
 
110
117
  content_type :api_json
111
118
 
data/lib/sinja/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: false
2
2
  module Sinatra
3
3
  module JSONAPI
4
- VERSION = '0.1.0.beta1'.freeze
4
+ VERSION = '0.1.0.beta2'.freeze
5
5
  end
6
6
  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: 0.1.0.beta1
4
+ version: 0.1.0.beta2
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-11-07 00:00:00.000000000 Z
11
+ date: 2016-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json