sinja 0.1.0.beta1 → 0.1.0.beta2

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: 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