sinja 1.0.0.pre2 → 1.1.0.pre1
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/README.md +313 -107
- data/demo-app/README.md +58 -0
- data/demo-app/app.rb +8 -4
- data/demo-app/boot.rb +3 -1
- data/demo-app/classes/author.rb +18 -15
- data/demo-app/classes/comment.rb +11 -9
- data/demo-app/classes/post.rb +21 -16
- data/demo-app/classes/tag.rb +12 -8
- data/demo-app/database.rb +4 -6
- data/lib/sinja/config.rb +85 -76
- data/lib/sinja/helpers/relationships.rb +5 -3
- data/lib/sinja/helpers/sequel.rb +43 -0
- data/lib/sinja/helpers/serializers.rb +39 -20
- data/lib/sinja/relationship_routes/has_many.rb +9 -5
- data/lib/sinja/relationship_routes/has_one.rb +4 -4
- data/lib/sinja/resource.rb +19 -21
- data/lib/sinja/resource_routes.rb +33 -20
- data/lib/sinja/version.rb +1 -1
- data/lib/sinja.rb +137 -48
- data/sinja.gemspec +1 -1
- metadata +4 -4
- data/demo-app/test.rb +0 -17
data/demo-app/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
## Demo App
|
2
|
+
|
3
|
+
This is the demo app for Sinja, used as an example of and for testing Sinja. It
|
4
|
+
is a very simplistic blog-like application with database tables, models,
|
5
|
+
serializers, and controllers for authors, posts, comments, and tags. It uses
|
6
|
+
[Sequel ORM](http://sequel.jeremyevans.net) (and the [Sequel
|
7
|
+
helpers](/lib/sinja/helpers/sequel.rb) provided with Sinja) with an in-memory
|
8
|
+
SQLite database, and it works under both MRI/YARV 2.3+ and JRuby 9.1+.
|
9
|
+
|
10
|
+
### Usage
|
11
|
+
|
12
|
+
Assuming you have a working, Bundler-enabled Ruby environment, simply clone
|
13
|
+
this repo, `cd` into the `demo-app` subdirectory, and run the following
|
14
|
+
commands:
|
15
|
+
|
16
|
+
```
|
17
|
+
$ bundle install
|
18
|
+
$ bundle exec ruby app.rb [-p <PORT>]
|
19
|
+
```
|
20
|
+
|
21
|
+
The web server will report the port it's listening on, or you can specify a
|
22
|
+
port with the `-p` option. It will respond to {json:api}-compliant requests
|
23
|
+
(don't forget to set an `Accept` header) to `/authors`, `/posts`, `/comments`,
|
24
|
+
and `/tags`, although not every endpoint is implemented. Log in by setting the
|
25
|
+
`X-Email` header on the request to the email address of a registered user; the
|
26
|
+
default administrator email address is all@yourbase.com.
|
27
|
+
|
28
|
+
**This is clearly extremely insecure and should not be used as-is in production.
|
29
|
+
Caveat emptor.**
|
30
|
+
|
31
|
+
You can point it at a different database by setting `DATABASE_URL` in the
|
32
|
+
environment before executing `app.rb`. See the relevant [Sequel
|
33
|
+
documentation](http://sequel.jeremyevans.net/rdoc/files/doc/opening_databases_rdoc.html)
|
34
|
+
for more information. It (rather naïvely) migrates the database at
|
35
|
+
startup.
|
36
|
+
|
37
|
+
### Productionalizing
|
38
|
+
|
39
|
+
You can certainly use this as a starting point for a production application,
|
40
|
+
but you will at least want to:
|
41
|
+
|
42
|
+
- [ ] Use a persistent database
|
43
|
+
- [ ] Separate the class files (e.g. `author.rb`, `post.rb`) into separate
|
44
|
+
files for the migrations, models, serializers, and Sinja controllers
|
45
|
+
- [ ] Create a Gemfile using the dependencies in the top-level
|
46
|
+
[gemspec](/sinja.gemspec) as a starting point
|
47
|
+
- [ ] Add authentication middleware and rewrite the `role` helper to enable
|
48
|
+
the authorization scheme. You can use the existing roles as defined or
|
49
|
+
rename them (e.g. use `:admin` instead of `:superuser`)
|
50
|
+
- [ ] Use a real application server such as [Puma](http://puma.io) or
|
51
|
+
[Passenger](https://www.phusionpassenger.com) instead of Ruby's
|
52
|
+
stdlib (WEBrick)
|
53
|
+
- [ ] Configure Sequel's connection pool (i.e. `:max_connections`) to match the
|
54
|
+
application server's thread pool (if any)
|
55
|
+
- [ ] Add caching directives (i.e. `cache_control`, `expires`, `last_modified`,
|
56
|
+
and `etag`) as appropriate
|
57
|
+
|
58
|
+
And probably a whole lot more!
|
data/demo-app/app.rb
CHANGED
@@ -9,6 +9,10 @@ require_relative 'classes/tag'
|
|
9
9
|
|
10
10
|
require 'sinja/helpers/sequel'
|
11
11
|
|
12
|
+
configure :development do
|
13
|
+
set :server_settings, AccessLog: [] # avoid WEBrick double-logging issue
|
14
|
+
end
|
15
|
+
|
12
16
|
configure_jsonapi do |c|
|
13
17
|
Sinja::Helpers::Sequel.config(c)
|
14
18
|
end
|
@@ -19,16 +23,16 @@ helpers Sinja::Helpers::Sequel do
|
|
19
23
|
Author.first_by_email(env['HTTP_X_EMAIL']) if env.key?('HTTP_X_EMAIL')
|
20
24
|
end
|
21
25
|
|
26
|
+
def database
|
27
|
+
DB
|
28
|
+
end
|
29
|
+
|
22
30
|
def role
|
23
31
|
[].tap do |a|
|
24
32
|
a << :logged_in if current_user
|
25
33
|
a << :superuser if current_user&.admin?
|
26
34
|
end
|
27
35
|
end
|
28
|
-
|
29
|
-
def database
|
30
|
-
DB
|
31
|
-
end
|
32
36
|
end
|
33
37
|
|
34
38
|
resource :authors, AuthorController
|
data/demo-app/boot.rb
CHANGED
data/demo-app/classes/author.rb
CHANGED
@@ -4,10 +4,10 @@ require_relative '../database'
|
|
4
4
|
|
5
5
|
DB.create_table?(:authors) do
|
6
6
|
primary_key :id
|
7
|
-
String :email, :
|
7
|
+
String :email, null: false, unique: true
|
8
8
|
String :real_name
|
9
9
|
String :display_name
|
10
|
-
TrueClass :admin, :
|
10
|
+
TrueClass :admin, default: false
|
11
11
|
DateTime :created_at
|
12
12
|
DateTime :updated_at
|
13
13
|
end
|
@@ -17,13 +17,14 @@ class Author < Sequel::Model
|
|
17
17
|
plugin :boolean_readers
|
18
18
|
|
19
19
|
finder def self.by_email(arg)
|
20
|
-
where(:
|
20
|
+
where(email: arg)
|
21
21
|
end
|
22
22
|
|
23
23
|
one_to_many :comments
|
24
24
|
one_to_many :posts
|
25
25
|
end
|
26
26
|
|
27
|
+
# We have to create an admin user here, otherwise we have no way to create one.
|
27
28
|
Author.create(email: 'all@yourbase.com', admin: true)
|
28
29
|
|
29
30
|
class AuthorSerializer < BaseSerializer
|
@@ -40,14 +41,12 @@ AuthorController = proc do
|
|
40
41
|
end
|
41
42
|
|
42
43
|
def role
|
43
|
-
|
44
|
-
|
45
|
-
else
|
46
|
-
super
|
44
|
+
[*super].tap do |a|
|
45
|
+
a << :myself if resource == current_user
|
47
46
|
end
|
48
47
|
end
|
49
48
|
|
50
|
-
def
|
49
|
+
def settable_fields
|
51
50
|
%i[email real_name display_name].tap do |a|
|
52
51
|
a << :admin if role?(:superuser)
|
53
52
|
end
|
@@ -56,33 +55,37 @@ AuthorController = proc do
|
|
56
55
|
|
57
56
|
show
|
58
57
|
|
58
|
+
show_many do |ids|
|
59
|
+
Author.where(id: ids.map!(&:to_i)).all
|
60
|
+
end
|
61
|
+
|
59
62
|
index do
|
60
|
-
Author.
|
63
|
+
Author.dataset
|
61
64
|
end
|
62
65
|
|
63
66
|
create do |attr|
|
64
67
|
author = Author.new
|
65
|
-
author.set_fields(attr,
|
68
|
+
author.set_fields(attr, settable_fields)
|
66
69
|
next_pk author.save(validate: false)
|
67
70
|
end
|
68
71
|
|
69
|
-
update(roles: %i[
|
70
|
-
resource.update_fields(attr,
|
72
|
+
update(roles: %i[myself superuser]) do |attr|
|
73
|
+
resource.update_fields(attr, settable_fields, validate: false, missing: :skip)
|
71
74
|
end
|
72
75
|
|
73
|
-
destroy(roles: %i[
|
76
|
+
destroy(roles: %i[myself superuser]) do
|
74
77
|
resource.destroy
|
75
78
|
end
|
76
79
|
|
77
80
|
has_many :comments do
|
78
81
|
fetch(roles: :logged_in) do
|
79
|
-
resource.
|
82
|
+
resource.comments_dataset
|
80
83
|
end
|
81
84
|
end
|
82
85
|
|
83
86
|
has_many :posts do
|
84
87
|
fetch do
|
85
|
-
resource.
|
88
|
+
resource.posts_dataset
|
86
89
|
end
|
87
90
|
end
|
88
91
|
end
|
data/demo-app/classes/comment.rb
CHANGED
@@ -4,9 +4,9 @@ require_relative '../database'
|
|
4
4
|
|
5
5
|
DB.create_table?(:comments) do
|
6
6
|
primary_key :id
|
7
|
-
foreign_key :author_id, :authors, :
|
8
|
-
foreign_key :post_slug, :posts, :on_delete
|
9
|
-
String :body, :
|
7
|
+
foreign_key :author_id, :authors, on_delete: :cascade
|
8
|
+
foreign_key :post_slug, :posts, type: String, on_delete: :cascade, on_update: :cascade
|
9
|
+
String :body, text: true, null: false
|
10
10
|
DateTime :created_at
|
11
11
|
DateTime :updated_at
|
12
12
|
end
|
@@ -37,12 +37,14 @@ CommentController = proc do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def role
|
40
|
-
|
41
|
-
|
42
|
-
else
|
43
|
-
super
|
40
|
+
[*super].tap do |a|
|
41
|
+
a << :owner if resource&.author == current_user
|
44
42
|
end
|
45
43
|
end
|
44
|
+
|
45
|
+
def settable_fields
|
46
|
+
%i[body]
|
47
|
+
end
|
46
48
|
end
|
47
49
|
|
48
50
|
show do |id|
|
@@ -51,13 +53,13 @@ CommentController = proc do
|
|
51
53
|
|
52
54
|
create(roles: :logged_in) do |attr|
|
53
55
|
comment = Comment.new
|
54
|
-
comment.set_fields(attr,
|
56
|
+
comment.set_fields(attr, settable_fields)
|
55
57
|
comment.save(validate: false)
|
56
58
|
next_pk comment
|
57
59
|
end
|
58
60
|
|
59
61
|
update(roles: %i[owner superuser]) do |attr|
|
60
|
-
resource.update_fields(attr,
|
62
|
+
resource.update_fields(attr, settable_fields, validate: false, missing: :skip)
|
61
63
|
end
|
62
64
|
|
63
65
|
destroy(roles: %i[owner superuser]) do
|
data/demo-app/classes/post.rb
CHANGED
@@ -3,10 +3,10 @@ require_relative '../base'
|
|
3
3
|
require_relative '../database'
|
4
4
|
|
5
5
|
DB.create_table?(:posts) do
|
6
|
-
String :slug, :
|
7
|
-
foreign_key :author_id, :authors, :
|
8
|
-
String :title, :
|
9
|
-
String :body, :
|
6
|
+
String :slug, primary_key: true
|
7
|
+
foreign_key :author_id, :authors, on_delete: :cascade
|
8
|
+
String :title, null: false
|
9
|
+
String :body, text: true, null: false
|
10
10
|
DateTime :created_at
|
11
11
|
DateTime :updated_at
|
12
12
|
end
|
@@ -14,11 +14,11 @@ end
|
|
14
14
|
class Post < Sequel::Model
|
15
15
|
plugin :timestamps
|
16
16
|
|
17
|
-
unrestrict_primary_key
|
17
|
+
unrestrict_primary_key # allow client-generated slugs
|
18
18
|
|
19
19
|
many_to_one :author
|
20
20
|
one_to_many :comments
|
21
|
-
many_to_many :tags, :
|
21
|
+
many_to_many :tags, left_key: :post_slug
|
22
22
|
|
23
23
|
def validate
|
24
24
|
super
|
@@ -45,33 +45,38 @@ PostController = proc do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def role
|
48
|
-
|
49
|
-
|
50
|
-
else
|
51
|
-
super
|
48
|
+
[*super].tap do |a|
|
49
|
+
a << :owner if resource&.author == current_user
|
52
50
|
end
|
53
51
|
end
|
52
|
+
|
53
|
+
def settable_fields
|
54
|
+
%i[title body]
|
55
|
+
end
|
54
56
|
end
|
55
57
|
|
56
58
|
show do |slug|
|
57
59
|
next find(slug), include: %w[author comments tags]
|
58
60
|
end
|
59
61
|
|
62
|
+
show_many do |slugs|
|
63
|
+
next Post.where(slug: slugs.map!(&:to_s)).all, include: %i[author tags]
|
64
|
+
end
|
65
|
+
|
60
66
|
index do
|
61
|
-
|
62
|
-
Post.all
|
67
|
+
Post.dataset
|
63
68
|
end
|
64
69
|
|
65
70
|
create(roles: :logged_in) do |attr, slug|
|
66
71
|
post = Post.new
|
67
|
-
post.set_fields(attr,
|
72
|
+
post.set_fields(attr, settable_fields)
|
68
73
|
post.slug = slug.to_s # set primary key
|
69
74
|
post.save(validate: false)
|
70
75
|
next_pk post
|
71
76
|
end
|
72
77
|
|
73
78
|
update(roles: %i[owner superuser]) do |attr|
|
74
|
-
resource.update_fields(attr,
|
79
|
+
resource.update_fields(attr, settable_fields, validate: false, missing: :skip)
|
75
80
|
end
|
76
81
|
|
77
82
|
destroy(roles: %i[owner superuser]) do
|
@@ -94,13 +99,13 @@ PostController = proc do
|
|
94
99
|
|
95
100
|
has_many :comments do
|
96
101
|
fetch do
|
97
|
-
next resource.
|
102
|
+
next resource.comments_dataset, include: 'author'
|
98
103
|
end
|
99
104
|
end
|
100
105
|
|
101
106
|
has_many :tags do
|
102
107
|
fetch do
|
103
|
-
resource.
|
108
|
+
resource.tags_dataset
|
104
109
|
end
|
105
110
|
|
106
111
|
merge(roles: %i[owner superuser], sideload_on: %i[create update]) do |rios|
|
data/demo-app/classes/tag.rb
CHANGED
@@ -6,18 +6,18 @@ require_relative 'post' # make sure we create the posts table before the join ta
|
|
6
6
|
|
7
7
|
DB.create_table?(:tags) do
|
8
8
|
primary_key :id
|
9
|
-
String :name, :
|
9
|
+
String :name, null: false, unique: true
|
10
10
|
end
|
11
11
|
|
12
12
|
DB.create_table?(:posts_tags) do
|
13
|
-
foreign_key :post_slug, :posts, :null
|
14
|
-
foreign_key :tag_id, :tags, :
|
13
|
+
foreign_key :post_slug, :posts, type: String, null: false, on_delete: :cascade, on_update: :cascade
|
14
|
+
foreign_key :tag_id, :tags, null: false, on_delete: :cascade
|
15
15
|
primary_key [:post_slug, :tag_id]
|
16
16
|
index [:tag_id, :post_slug]
|
17
17
|
end
|
18
18
|
|
19
19
|
class Tag < Sequel::Model
|
20
|
-
many_to_many :posts
|
20
|
+
many_to_many :posts, right_key: :post_slug
|
21
21
|
end
|
22
22
|
|
23
23
|
class TagSerializer < BaseSerializer
|
@@ -31,17 +31,21 @@ TagController = proc do
|
|
31
31
|
def find(id)
|
32
32
|
Tag[id.to_i]
|
33
33
|
end
|
34
|
+
|
35
|
+
def settable_fields
|
36
|
+
%i[name]
|
37
|
+
end
|
34
38
|
end
|
35
39
|
|
36
40
|
show
|
37
41
|
|
38
|
-
index do
|
39
|
-
Tag.
|
42
|
+
index(sort_by: :name, filter_by: :name) do
|
43
|
+
Tag.dataset
|
40
44
|
end
|
41
45
|
|
42
46
|
create(roles: :logged_in) do |attr|
|
43
47
|
tag = Tag.new
|
44
|
-
tag.set_fields(attr,
|
48
|
+
tag.set_fields(attr, settable_fields)
|
45
49
|
tag.save(validate: false)
|
46
50
|
next_pk tag
|
47
51
|
end
|
@@ -52,7 +56,7 @@ TagController = proc do
|
|
52
56
|
|
53
57
|
has_many :posts do
|
54
58
|
fetch do
|
55
|
-
resource.
|
59
|
+
resource.posts_dataset
|
56
60
|
end
|
57
61
|
|
58
62
|
merge(roles: :logged_in) do |rios|
|
data/demo-app/database.rb
CHANGED
@@ -2,11 +2,9 @@
|
|
2
2
|
require 'logger'
|
3
3
|
require_relative 'boot'
|
4
4
|
|
5
|
-
DB =
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
Sequel.sqlite
|
10
|
-
end
|
5
|
+
DB = Sequel.connect ENV.fetch 'DATABASE_URL',
|
6
|
+
defined?(JRUBY_VERSION) ? 'jdbc:sqlite::memory:' : 'sqlite:/'
|
7
|
+
|
8
|
+
DB.extension :pagination
|
11
9
|
|
12
10
|
DB.loggers << Logger.new($stderr) if Sinatra::Base.development?
|
data/lib/sinja/config.rb
CHANGED
@@ -15,7 +15,21 @@ module Sinja
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def deep_freeze(c)
|
18
|
-
c.
|
18
|
+
if c.respond_to?(:default_proc)
|
19
|
+
c.default_proc = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
if c.respond_to?(:values)
|
23
|
+
c.values.each do |i|
|
24
|
+
if Hash === i
|
25
|
+
deep_freeze(i)
|
26
|
+
else
|
27
|
+
i.freeze
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
c.freeze
|
19
33
|
end
|
20
34
|
end
|
21
35
|
|
@@ -33,41 +47,54 @@ module Sinja
|
|
33
47
|
}.freeze
|
34
48
|
|
35
49
|
attr_reader \
|
50
|
+
:query_params,
|
36
51
|
:error_logger,
|
37
|
-
:
|
38
|
-
:default_has_many_roles,
|
39
|
-
:default_has_one_roles,
|
40
|
-
:resource_roles,
|
41
|
-
:resource_sideload,
|
52
|
+
:resource_config,
|
42
53
|
:conflict_exceptions,
|
43
54
|
:not_found_exceptions,
|
44
55
|
:validation_exceptions,
|
45
56
|
:validation_formatter,
|
57
|
+
:page_using,
|
46
58
|
:serializer_opts
|
47
59
|
|
48
60
|
def initialize
|
49
|
-
@
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
61
|
+
@query_params = {
|
62
|
+
:fields=>{}, # passthru to JAS
|
63
|
+
:include=>[], # passthru to JAS
|
64
|
+
:filter=>{},
|
65
|
+
:page=>{},
|
66
|
+
:sort=>[],
|
67
|
+
:capture=>nil
|
68
|
+
}
|
69
|
+
|
70
|
+
@error_logger = ->(h) { logger.error('sinja') { h } }
|
71
|
+
|
72
|
+
@default_roles = {
|
73
|
+
:resource=>RolesConfig.new(%i[show show_many index create update destroy]),
|
74
|
+
:has_many=>RolesConfig.new(%i[fetch merge subtract clear]),
|
75
|
+
:has_one=>RolesConfig.new(%i[pluck graft prune])
|
76
|
+
}
|
77
|
+
|
78
|
+
action_proc = proc { |type, hash, action| hash[action] = {
|
79
|
+
:roles=>@default_roles[type][action].dup,
|
80
|
+
:sideload_on=>Set.new,
|
81
|
+
:filter_by=>Set.new,
|
82
|
+
:sort_by=>Set.new
|
83
|
+
}}.curry
|
84
|
+
|
85
|
+
@resource_config = Hash.new { |h, k| h[k] = {
|
86
|
+
:resource=>Hash.new(&action_proc[:resource]),
|
87
|
+
:has_many=>Hash.new { |rh, rk| rh[rk] = Hash.new(&action_proc[:has_many]) },
|
88
|
+
:has_one=>Hash.new { |rh, rk| rh[rk] = Hash.new(&action_proc[:has_one]) }
|
59
89
|
}}
|
60
90
|
|
61
|
-
@resource_sideload = Hash.new do |h, k|
|
62
|
-
h[k] = SideloadConfig.new(Resource::SIDELOAD_ACTIONS)
|
63
|
-
end
|
64
|
-
|
65
91
|
@conflict_exceptions = Set.new
|
66
92
|
@not_found_exceptions = Set.new
|
67
93
|
@validation_exceptions = Set.new
|
68
94
|
@validation_formatter = ->{ Array.new }
|
69
95
|
|
70
96
|
@opts = deep_copy(DEFAULT_OPTS)
|
97
|
+
@page_using = Hash.new
|
71
98
|
@serializer_opts = deep_copy(DEFAULT_SERIALIZER_OPTS)
|
72
99
|
end
|
73
100
|
|
@@ -82,15 +109,15 @@ module Sinja
|
|
82
109
|
end
|
83
110
|
|
84
111
|
def conflict_exceptions=(e=[])
|
85
|
-
@conflict_exceptions.replace(
|
112
|
+
@conflict_exceptions.replace([*e])
|
86
113
|
end
|
87
114
|
|
88
115
|
def not_found_exceptions=(e=[])
|
89
|
-
@not_found_exceptions.replace(
|
116
|
+
@not_found_exceptions.replace([*e])
|
90
117
|
end
|
91
118
|
|
92
119
|
def validation_exceptions=(e=[])
|
93
|
-
@validation_exceptions.replace(
|
120
|
+
@validation_exceptions.replace([*e])
|
94
121
|
end
|
95
122
|
|
96
123
|
def validation_formatter=(f)
|
@@ -103,12 +130,28 @@ module Sinja
|
|
103
130
|
@validation_formatter = f
|
104
131
|
end
|
105
132
|
|
106
|
-
|
107
|
-
|
108
|
-
|
133
|
+
def default_roles
|
134
|
+
@default_roles[:resource]
|
135
|
+
end
|
109
136
|
|
110
|
-
def
|
111
|
-
@
|
137
|
+
def default_roles=(other={})
|
138
|
+
@default_roles[:resource].merge!(other)
|
139
|
+
end
|
140
|
+
|
141
|
+
def default_has_many_roles
|
142
|
+
@default_roles[:has_many]
|
143
|
+
end
|
144
|
+
|
145
|
+
def default_has_many_roles=(other={})
|
146
|
+
@default_roles[:has_many].merge!(other)
|
147
|
+
end
|
148
|
+
|
149
|
+
def default_has_one_roles
|
150
|
+
@default_roles[:has_one]
|
151
|
+
end
|
152
|
+
|
153
|
+
def default_has_one_roles=(other={})
|
154
|
+
@default_roles[:has_one].merge!(other)
|
112
155
|
end
|
113
156
|
|
114
157
|
DEFAULT_OPTS.keys.each do |k|
|
@@ -116,34 +159,29 @@ module Sinja
|
|
116
159
|
define_method("#{k}=") { |v| @opts[k] = v }
|
117
160
|
end
|
118
161
|
|
162
|
+
def page_using=(p={})
|
163
|
+
@page_using.replace(p)
|
164
|
+
end
|
165
|
+
|
166
|
+
def serializer_opts=(h={})
|
167
|
+
@serializer_opts.replace(deep_copy(DEFAULT_SERIALIZER_OPTS).merge!(h))
|
168
|
+
end
|
169
|
+
|
119
170
|
def freeze
|
171
|
+
@query_params.freeze
|
120
172
|
@error_logger.freeze
|
121
173
|
|
122
|
-
@default_roles
|
123
|
-
@
|
124
|
-
@default_has_one_roles.freeze
|
125
|
-
|
126
|
-
@resource_roles.default_proc = nil
|
127
|
-
@resource_roles.values.each do |h|
|
128
|
-
h[:resource].freeze
|
129
|
-
h[:has_many].default_proc = nil
|
130
|
-
deep_freeze(h[:has_many])
|
131
|
-
h[:has_one].default_proc = nil
|
132
|
-
deep_freeze(h[:has_one])
|
133
|
-
end
|
134
|
-
deep_freeze(@resource_roles)
|
135
|
-
|
136
|
-
@resource_sideload.default_proc = nil
|
137
|
-
deep_freeze(@resource_sideload)
|
174
|
+
deep_freeze(@default_roles)
|
175
|
+
deep_freeze(@resource_config)
|
138
176
|
|
139
177
|
@conflict_exceptions.freeze
|
140
178
|
@not_found_exceptions.freeze
|
141
179
|
@validation_exceptions.freeze
|
142
180
|
@validation_formatter.freeze
|
143
181
|
|
144
|
-
deep_freeze(@serializer_opts)
|
145
|
-
|
146
182
|
@opts.freeze
|
183
|
+
@page_using.freeze
|
184
|
+
deep_freeze(@serializer_opts)
|
147
185
|
|
148
186
|
super
|
149
187
|
end
|
@@ -173,7 +211,7 @@ module Sinja
|
|
173
211
|
h.each do |action, roles|
|
174
212
|
abort "Unknown or invalid action helper `#{action}' in configuration" \
|
175
213
|
unless @data.key?(action)
|
176
|
-
@data[action].replace(
|
214
|
+
@data[action].replace([*roles])
|
177
215
|
end
|
178
216
|
@data
|
179
217
|
end
|
@@ -188,33 +226,4 @@ module Sinja
|
|
188
226
|
super
|
189
227
|
end
|
190
228
|
end
|
191
|
-
|
192
|
-
class SideloadConfig
|
193
|
-
include ConfigUtils
|
194
|
-
extend Forwardable
|
195
|
-
|
196
|
-
def initialize(actions=[])
|
197
|
-
@data = actions.map { |child| [child, Set.new] }.to_h
|
198
|
-
end
|
199
|
-
|
200
|
-
def_delegators :@data, :[], :dig
|
201
|
-
|
202
|
-
def ==(other)
|
203
|
-
@data == other.instance_variable_get(:@data)
|
204
|
-
end
|
205
|
-
|
206
|
-
def merge!(h={})
|
207
|
-
h.each do |child, parents|
|
208
|
-
abort "Unknown or invalid action helper `#{child}' in configuration" \
|
209
|
-
unless @data.key?(child)
|
210
|
-
@data[child].replace(Set[*parents])
|
211
|
-
end
|
212
|
-
@data
|
213
|
-
end
|
214
|
-
|
215
|
-
def freeze
|
216
|
-
deep_freeze(@data)
|
217
|
-
super
|
218
|
-
end
|
219
|
-
end
|
220
229
|
end
|
@@ -19,9 +19,11 @@ module Sinja
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def dispatch_relationship_requests!(id, methods: {}, **opts)
|
22
|
-
data.fetch(:relationships, {}).each do |
|
23
|
-
|
24
|
-
code, _, *json = dispatch_relationship_request
|
22
|
+
data.fetch(:relationships, {}).each do |rel, body|
|
23
|
+
rel_type = settings._resource_config[:has_one].key?(rel) ? :has_one : :has_many
|
24
|
+
code, _, *json = dispatch_relationship_request id, rel,
|
25
|
+
opts.merge(:body=>body, :method=>methods.fetch(rel_type, :patch))
|
26
|
+
|
25
27
|
# TODO: Gather responses and report all errors instead of only first?
|
26
28
|
# `halt' was called (instead of raise); rethrow it as best as possible
|
27
29
|
raise SideloadError.new(code, json) unless (200...300).cover?(code)
|