sinja 1.2.3 → 1.2.4
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 +4 -4
- data/.travis.yml +8 -4
- data/README.md +42 -0
- data/demo-app/Dockerfile +1 -1
- data/demo-app/Gemfile +3 -1
- data/demo-app/app.rb +3 -0
- data/demo-app/database.rb +2 -1
- data/lib/jsonapi/ember_serializer.rb +39 -0
- data/lib/sinja.rb +8 -8
- data/lib/sinja/helpers/nested.rb +4 -0
- data/lib/sinja/helpers/relationships.rb +9 -2
- data/lib/sinja/helpers/serializers.rb +6 -4
- data/lib/sinja/relationship_routes/has_many.rb +1 -1
- data/lib/sinja/relationship_routes/has_one.rb +1 -1
- data/lib/sinja/resource.rb +2 -2
- data/lib/sinja/resource_routes.rb +3 -3
- data/lib/sinja/version.rb +1 -1
- data/sinja.gemspec +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 851e8f43eabf59529257a1ef2fcc7a6df84f8ea5
|
4
|
+
data.tar.gz: b75968556128599080ba71d790d8b1671cbc89e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ddd2782e3e63efab5bfee81ace29615708e08cc9a137ba28451724543f98ee3f2f6d59eda0444a0859ebd9f4b8ccdc26ed661ebde0cbdf9b902e97e18f8d23f
|
7
|
+
data.tar.gz: 1fea65b16d22c9bbb22dc0af0ed4744308eaf5dfe217da0c054da4c57b40ae5d00a36617b06d114ae2a50b324a72ac45a4c17917bd61debed1ed812d35c5e0ef
|
data/.travis.yml
CHANGED
@@ -2,14 +2,18 @@ sudo: false
|
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
4
|
- 2.3.3
|
5
|
+
- 2.4.0
|
5
6
|
- ruby-head
|
6
|
-
- jruby-9.1.
|
7
|
+
- jruby-9.1.7.0
|
7
8
|
- jruby-head
|
8
9
|
env:
|
9
10
|
- sinatra=1.4.7 rails=4.2.7.1
|
10
|
-
- sinatra=2.0.0.beta2 rails=5.0.
|
11
|
+
- sinatra=2.0.0.beta2 rails=5.0.1
|
11
12
|
jdk:
|
12
13
|
- oraclejdk8
|
13
14
|
before_install:
|
14
|
-
- gem
|
15
|
-
|
15
|
+
- gem install bundler
|
16
|
+
matrix:
|
17
|
+
allow_failures:
|
18
|
+
- rvm: ruby-head
|
19
|
+
- rvm: jruby-head
|
data/README.md
CHANGED
@@ -778,6 +778,24 @@ end
|
|
778
778
|
|
779
779
|
Please see the [demo-app](/demo-app) for a more complete example.
|
780
780
|
|
781
|
+
Finally, because the `role` helper is invoked several times and may return
|
782
|
+
different results throughout the request lifecycle, Sinja does not memoize
|
783
|
+
(cache the return value keyed by function signature) it. If you have an
|
784
|
+
expensive component of your role helper that is not context-dependent, it may
|
785
|
+
be worth memoizing yourself:
|
786
|
+
|
787
|
+
```ruby
|
788
|
+
helpers do
|
789
|
+
def role
|
790
|
+
@roles ||= expensive_role_lookup.freeze
|
791
|
+
|
792
|
+
@roles.dup.tap do |a|
|
793
|
+
a << :foo if bar
|
794
|
+
end
|
795
|
+
end
|
796
|
+
end
|
797
|
+
```
|
798
|
+
|
781
799
|
### Query Parameters
|
782
800
|
|
783
801
|
The {json:api} specification states that any unhandled query parameters should
|
@@ -1167,6 +1185,30 @@ The following matrix outlines which combinations of action helpers and
|
|
1167
1185
|
</tbody>
|
1168
1186
|
</table>
|
1169
1187
|
|
1188
|
+
#### Deferring Relationships
|
1189
|
+
|
1190
|
+
If you're side-loading multiple relationships, you may need one applied before
|
1191
|
+
another (e.g. set the author of a post before setting its tags). You can use
|
1192
|
+
the built-in `defer` helper to affect the order of operations:
|
1193
|
+
|
1194
|
+
```ruby
|
1195
|
+
has_one :author do
|
1196
|
+
graft do |rio|
|
1197
|
+
resource.author = Author.with_pk!(rio[:id].to_i)
|
1198
|
+
resource.save_changes
|
1199
|
+
end
|
1200
|
+
end
|
1201
|
+
|
1202
|
+
has_many :tags do
|
1203
|
+
replace do |rios|
|
1204
|
+
defer unless resource.author # come back to this if the author isn't set yet
|
1205
|
+
|
1206
|
+
tags = resource.author.preferred_tags
|
1207
|
+
# ..
|
1208
|
+
end
|
1209
|
+
end
|
1210
|
+
```
|
1211
|
+
|
1170
1212
|
#### Avoiding Null Foreign Keys
|
1171
1213
|
|
1172
1214
|
Now, let's say our DBA is forward-thinking and wants to make the foreign key
|
data/demo-app/Dockerfile
CHANGED
data/demo-app/Gemfile
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
+
gem 'jdbc-sqlite3', '~> 3.8', :platform=>:jruby
|
3
4
|
gem 'json', '~> 2.0'
|
5
|
+
gem 'sequel', '~> 4.43'
|
4
6
|
gem 'sinatra', '>= 2.0.0.beta2', '< 3'
|
5
7
|
gem 'sinatra-contrib', '>= 2.0.0.beta2', '< 3'
|
6
8
|
gem 'sinja',
|
7
9
|
git: 'https://github.com/mwpastore/sinja.git', branch: 'master'
|
8
10
|
gem 'sinja-sequel',
|
9
11
|
git: 'https://github.com/mwpastore/sinja-sequel.git', branch: 'master'
|
10
|
-
gem 'sqlite3', '~> 1.3'
|
12
|
+
gem 'sqlite3', '~> 1.3', :platform=>[:ruby, :mswin]
|
data/demo-app/app.rb
CHANGED
@@ -10,6 +10,9 @@ require_relative 'classes/comment'
|
|
10
10
|
require_relative 'classes/post'
|
11
11
|
require_relative 'classes/tag'
|
12
12
|
|
13
|
+
# Freeze database after creating tables and loading models
|
14
|
+
DB.freeze
|
15
|
+
|
13
16
|
configure :development do
|
14
17
|
set :server_settings, AccessLog: [] # avoid WEBrick double-logging issue
|
15
18
|
end
|
data/demo-app/database.rb
CHANGED
@@ -5,6 +5,7 @@ require_relative 'boot'
|
|
5
5
|
DB = Sequel.connect ENV.fetch 'DATABASE_URL',
|
6
6
|
defined?(JRUBY_VERSION) ? 'jdbc:sqlite::memory:' : 'sqlite:/'
|
7
7
|
|
8
|
-
DB.extension
|
8
|
+
DB.extension(:freeze_datasets)
|
9
|
+
DB.extension(:pagination)
|
9
10
|
|
10
11
|
DB.loggers << Logger.new($stderr) if Sinatra::Base.development?
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'jsonapi-serializers'
|
3
|
+
|
4
|
+
module JSONAPI
|
5
|
+
module EmberSerializer
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
include Serializer
|
9
|
+
|
10
|
+
alias type_for_link type
|
11
|
+
alias format_name_for_link format_name
|
12
|
+
|
13
|
+
include InstanceMethods
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceMethods
|
18
|
+
def type
|
19
|
+
object.class.name.demodulize.underscore.dasherize
|
20
|
+
end
|
21
|
+
|
22
|
+
def format_name(attribute_name)
|
23
|
+
attribute_name.to_s.underscore.camelize(:lower)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self_link
|
27
|
+
"#{base_url}/#{type_for_link}/#{id}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def relationship_self_link(attribute_name)
|
31
|
+
"#{self_link}/relationships/#{format_name_for_link(attribute_name)}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def relationship_related_link(attribute_name)
|
35
|
+
"#{self_link}/#{format_name_for_link(attribute_name)}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/sinja.rb
CHANGED
@@ -13,6 +13,7 @@ require 'sinja/resource'
|
|
13
13
|
require 'sinja/version'
|
14
14
|
|
15
15
|
module Sinja
|
16
|
+
DEFER_CODE = 357
|
16
17
|
MIME_TYPE = 'application/vnd.api+json'
|
17
18
|
ERROR_CODES = ObjectSpace.each_object(Class).to_a
|
18
19
|
.keep_if { |klass| klass < HttpError }
|
@@ -138,11 +139,14 @@ module Sinja
|
|
138
139
|
|
139
140
|
def can?(action)
|
140
141
|
roles = settings._resource_config[:resource].fetch(action, {})[:roles]
|
141
|
-
roles.nil? || roles.empty? || roles.intersect?(
|
142
|
+
roles.nil? || roles.empty? || roles.intersect?(role)
|
142
143
|
end
|
143
144
|
|
144
145
|
def content?
|
145
|
-
request.body.respond_to?(:size) && request.body.size > 0
|
146
|
+
request.body.respond_to?(:size) && request.body.size > 0 || begin
|
147
|
+
request.body.rewind
|
148
|
+
request.body.read(1)
|
149
|
+
end
|
146
150
|
end
|
147
151
|
|
148
152
|
def data
|
@@ -243,10 +247,6 @@ module Sinja
|
|
243
247
|
end
|
244
248
|
end
|
245
249
|
|
246
|
-
def memoized_role
|
247
|
-
@role ||= Roles[*role]
|
248
|
-
end
|
249
|
-
|
250
250
|
def sideloaded?
|
251
251
|
env.key?('sinja.passthru')
|
252
252
|
end
|
@@ -256,7 +256,7 @@ module Sinja
|
|
256
256
|
end
|
257
257
|
|
258
258
|
def role?(*roles)
|
259
|
-
Roles[*roles].intersect?(
|
259
|
+
Roles[*roles].intersect?(role)
|
260
260
|
end
|
261
261
|
|
262
262
|
def sanity_check!(resource_name, id=nil)
|
@@ -328,7 +328,7 @@ module Sinja
|
|
328
328
|
# trigger default procs
|
329
329
|
config = _sinja.resource_config[resource_name]
|
330
330
|
|
331
|
-
namespace
|
331
|
+
namespace %r{/#{resource_name}(?![^/])} do
|
332
332
|
define_singleton_method(:_resource_config) { config }
|
333
333
|
define_singleton_method(:resource_config) { config[:resource] }
|
334
334
|
|
data/lib/sinja/helpers/nested.rb
CHANGED
@@ -19,11 +19,18 @@ module Sinja
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def dispatch_relationship_requests!(id, methods: {}, **opts)
|
22
|
-
data.fetch(:relationships, {}).
|
23
|
-
|
22
|
+
rels = data.fetch(:relationships, {}).to_a
|
23
|
+
rels.each do |rel, body, rel_type=nil, count=0|
|
24
|
+
rel_type ||= settings._resource_config[:has_one].key?(rel) ? :has_one : :has_many
|
24
25
|
code, _, *json = dispatch_relationship_request id, rel,
|
25
26
|
opts.merge(:body=>body, :method=>methods.fetch(rel_type, :patch))
|
26
27
|
|
28
|
+
if code == DEFER_CODE && count == 0
|
29
|
+
rels << [rel, body, rel_type, count + 1]
|
30
|
+
|
31
|
+
next
|
32
|
+
end
|
33
|
+
|
27
34
|
# TODO: Gather responses and report all errors instead of only first?
|
28
35
|
# `halt' was called (instead of raise); rethrow it as best as possible
|
29
36
|
raise SideloadError.new(code, json) unless (200...300).cover?(code)
|
@@ -87,7 +87,7 @@ module Sinja
|
|
87
87
|
config.dig(:has_many, last_term.pluralize.to_sym, :fetch, :roles) ||
|
88
88
|
config.dig(:has_one, last_term.singularize.to_sym, :pluck, :roles)
|
89
89
|
|
90
|
-
throw :keep?, roles && (roles.empty? || roles.intersect?(
|
90
|
+
throw :keep?, roles && (roles.empty? || roles.intersect?(role))
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
@@ -170,12 +170,14 @@ module Sinja
|
|
170
170
|
def serialize_linkage(model, rel, options={})
|
171
171
|
options[:is_collection] = false
|
172
172
|
options[:skip_collection_check] = defined?(::Sequel::Model) && model.is_a?(::Sequel::Model)
|
173
|
-
options[:include] = rel.to_s
|
174
173
|
options = settings._sinja.serializer_opts.merge(options)
|
175
174
|
|
175
|
+
options[:serializer] ||= ::JSONAPI::Serializer.find_serializer_class(model, options)
|
176
|
+
options[:include] = options[:serializer].new(model, options).format_name(rel)
|
177
|
+
|
176
178
|
# TODO: This is extremely wasteful. Refactor JAS to expose the linkage serializer?
|
177
179
|
content = ::JSONAPI::Serializer.serialize(model, options)
|
178
|
-
content['data']['relationships'][
|
180
|
+
content['data']['relationships'].fetch(options[:include]).tap do |linkage|
|
179
181
|
%w[meta jsonapi].each do |key|
|
180
182
|
linkage[key] = content[key] if content.key?(key)
|
181
183
|
end
|
@@ -192,7 +194,7 @@ module Sinja
|
|
192
194
|
|
193
195
|
def error_hash(title: nil, detail: nil, source: nil)
|
194
196
|
[
|
195
|
-
{ id
|
197
|
+
{ :id=>SecureRandom.uuid }.tap do |hash|
|
196
198
|
hash[:title] = title if title
|
197
199
|
hash[:detail] = detail if detail
|
198
200
|
hash[:status] = status.to_s if status
|
data/lib/sinja/resource.rb
CHANGED
@@ -89,7 +89,7 @@ module Sinja
|
|
89
89
|
|
90
90
|
config = _resource_config[rel_type][rel] # trigger default proc
|
91
91
|
|
92
|
-
namespace %r{/[^/]+(?<r>/relationships)?/#{rel}} do
|
92
|
+
namespace %r{/[^/]+(?<r>/relationships)?/#{rel}(?![^/])} do
|
93
93
|
define_singleton_method(:resource_config) { config }
|
94
94
|
|
95
95
|
helpers Helpers::Nested do
|
@@ -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.intersect?(
|
100
|
+
roles.nil? || roles.empty? || roles.intersect?(role) ||
|
101
101
|
parent && sideload_on.include?(parent) && super(parent, *args)
|
102
102
|
end
|
103
103
|
|
@@ -9,7 +9,7 @@ module Sinja
|
|
9
9
|
app.def_action_helper(app, :update, :roles)
|
10
10
|
app.def_action_helper(app, :destroy, :roles)
|
11
11
|
|
12
|
-
app.
|
12
|
+
app.options '', :qcaptures=>{ :filter=>:id } do
|
13
13
|
allow :get=>:show
|
14
14
|
end
|
15
15
|
|
@@ -38,7 +38,7 @@ module Sinja
|
|
38
38
|
serialize_models(resources, opts)
|
39
39
|
end
|
40
40
|
|
41
|
-
app.
|
41
|
+
app.options '' do
|
42
42
|
allow :get=>:index, :post=>:create
|
43
43
|
end
|
44
44
|
|
@@ -82,7 +82,7 @@ module Sinja
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
|
-
app.
|
85
|
+
app.options '/:id' do
|
86
86
|
allow :get=>:show, :patch=>:update, :delete=>:destroy
|
87
87
|
end
|
88
88
|
|
data/lib/sinja/version.rb
CHANGED
data/sinja.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ['mike@oobak.org']
|
11
11
|
|
12
12
|
spec.summary = 'RESTful, {json:api}-compliant web services in Sinatra'
|
13
|
-
spec.description = <<~EOF
|
13
|
+
spec.description = <<~'EOF'
|
14
14
|
Sinja is a Sinatra extension for quickly building RESTful,
|
15
15
|
{json:api}-compliant web services, leveraging the excellent
|
16
16
|
JSONAPI::Serializers gem for payload serialization. It enhances Sinatra's
|
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.
|
4
|
+
version: 1.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Pastore
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -251,6 +251,7 @@ files:
|
|
251
251
|
- demo-app/classes/post.rb
|
252
252
|
- demo-app/classes/tag.rb
|
253
253
|
- demo-app/database.rb
|
254
|
+
- lib/jsonapi/ember_serializer.rb
|
254
255
|
- lib/sinatra/jsonapi.rb
|
255
256
|
- lib/sinja.rb
|
256
257
|
- lib/sinja/config.rb
|
@@ -285,7 +286,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
285
286
|
version: '0'
|
286
287
|
requirements: []
|
287
288
|
rubyforge_project:
|
288
|
-
rubygems_version: 2.6.
|
289
|
+
rubygems_version: 2.6.10
|
289
290
|
signing_key:
|
290
291
|
specification_version: 4
|
291
292
|
summary: RESTful, {json:api}-compliant web services in Sinatra
|