sinja 0.2.0.beta2 → 1.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,11 +3,24 @@ module Sinja
3
3
  module RelationshipRoutes
4
4
  module HasOne
5
5
  ACTIONS = %i[pluck prune graft].freeze
6
- CONFLICT_ACTIONS = %i[graft].freeze
7
6
 
8
7
  def self.registered(app)
9
8
  app.def_action_helpers(ACTIONS, app)
10
9
 
10
+ app.head '' do
11
+ unless relationship_link?
12
+ allow :get=>:pluck
13
+ else
14
+ allow :get=>:show, :patch=>[:prune, :graft]
15
+ end
16
+ end
17
+
18
+ app.get '', :actions=>:show do
19
+ pass unless relationship_link?
20
+
21
+ serialize_linkage
22
+ end
23
+
11
24
  app.get '', :actions=>:pluck do
12
25
  serialize_model(*pluck)
13
26
  end
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
+ require 'active_support/inflector'
3
+ require 'set'
2
4
  require 'sinatra/base'
3
5
  require 'sinatra/namespace'
4
6
 
7
+ require 'sinja/helpers/nested'
5
8
  require 'sinja/helpers/relationships'
6
9
  require 'sinja/relationship_routes/has_many'
7
10
  require 'sinja/relationship_routes/has_one'
@@ -9,36 +12,36 @@ require 'sinja/resource_routes'
9
12
 
10
13
  module Sinja
11
14
  module Resource
12
- def def_action_helper(action, context=nil)
13
- abort "JSONAPI resource actions can't be HTTP verbs!" \
15
+ SIDELOAD_ACTIONS = Set.new(%i[graft merge clear]).freeze
16
+
17
+ def def_action_helper(action, context)
18
+ abort "JSONAPI action helpers can't be HTTP verbs!" \
14
19
  if Sinatra::Base.respond_to?(action)
15
20
 
16
21
  context.define_singleton_method(action) do |**opts, &block|
17
- can(action, opts[:roles]) if opts.key?(:roles)
22
+ resource_roles.merge!(action=>opts[:roles]) if opts.key?(:roles)
23
+ resource_sideload.merge!(action=>opts[:sideload_on]) if opts.key?(:sideload_on)
24
+
25
+ return unless block ||=
26
+ case !method_defined?(action) && action
27
+ when :show
28
+ proc { |id| find(id) } if method_defined?(:find)
29
+ end
18
30
 
19
- return if block.nil?
31
+ # TODO: Move this to a constant or configurable?
32
+ required_arity = {
33
+ :create=>2,
34
+ :index=>-1,
35
+ :fetch=>-1
36
+ }.freeze[action] || 1
20
37
 
21
38
  define_method(action) do |*args|
22
- send("before_#{action}") if respond_to?("before_#{action}")
23
-
24
- result =
25
- begin
26
- instance_exec(*args.take(block.arity.abs), &block)
27
- rescue Exception=>e
28
- halt 409, e.message if settings._sinja.conflict?(action, e.class)
29
- #halt 422, resource.errors if settings._sinja.invalid?(action, e.class) # TODO
30
- #not_found if settings._sinja.not_found?(action, e.class) # TODO
31
- raise
32
- end
39
+ raise ArgumentError, "Unexpected block signature for `#{action}' action helper" \
40
+ unless args.length == block.arity
33
41
 
34
- # TODO: Move this to a constant or configurable?
35
- required_arity = {
36
- :create=>2,
37
- :index=>-1,
38
- :fetch=>-1
39
- }.freeze[action] || 1
42
+ public_send("before_#{action}", *args) if respond_to?("before_#{action}")
40
43
 
41
- case result
44
+ case result = instance_exec(*args, &block)
42
45
  when Array
43
46
  opts = {}
44
47
  if Hash === result.last
@@ -59,7 +62,7 @@ module Sinja
59
62
  end
60
63
 
61
64
  define_singleton_method("remove_#{action}") do
62
- remove_method(action)
65
+ remove_method(action) if respond_to?(action)
63
66
  end
64
67
  end
65
68
  end
@@ -71,18 +74,6 @@ module Sinja
71
74
  def self.registered(app)
72
75
  app.helpers Helpers::Relationships do
73
76
  attr_accessor :resource
74
-
75
- def attributes
76
- dedasherize_names(data.fetch(:attributes, {}))
77
- end
78
-
79
- def sanity_check!(id=nil)
80
- halt 409, 'Resource type in payload does not match endpoint' \
81
- if data[:type] != request.path.split('/').last # TODO
82
-
83
- halt 409, 'Resource ID in payload does not match endpoint' \
84
- if id && data[:id].to_s != id.to_s
85
- end
86
77
  end
87
78
 
88
79
  app.register ResourceRoutes
@@ -90,38 +81,28 @@ module Sinja
90
81
 
91
82
  %i[has_one has_many].each do |rel_type|
92
83
  define_method(rel_type) do |rel, &block|
93
- rel_path = rel.to_s.tr('_', '-')
84
+ rel_path = rel.to_s
85
+ .send(rel_type == :has_one ? :singularize : :pluralize)
86
+ .dasherize
87
+ .to_sym
94
88
 
95
- namespace %r{/(?<resource_id>[^/]+)(?<r>/relationships)?/#{rel_path}}, :actions=>:show do
96
- helpers do
97
- def relationship_link?
98
- !params[:r].nil?
99
- end
89
+ _resource_roles[rel_type][rel.to_sym] # trigger default proc
100
90
 
101
- def resource
102
- super || self.resource = show(params[:resource_id]).first
103
- end
91
+ namespace %r{/[^/]+(?<r>/relationships)?/#{rel_path}} do
92
+ define_singleton_method(:resource_roles) do
93
+ _resource_roles[rel_type][rel.to_sym]
94
+ end
104
95
 
105
- def sanity_check!
106
- super(params[:resource_id])
96
+ helpers Helpers::Nested do
97
+ define_method(:can?) do |*args|
98
+ super(*args, rel_type, rel.to_sym)
107
99
  end
108
100
 
109
- define_method(:linkage) do
110
- # TODO: This is extremely wasteful. Refactor JAS to expose the linkage serializer?
111
- serialize_model(resource, :include=>rel_path)['data']['relationships'][rel_path]
101
+ define_method(:serialize_linkage) do |*args|
102
+ super(resource, rel_path, *args)
112
103
  end
113
104
  end
114
105
 
115
- before do
116
- not_found 'Parent resource not found' unless resource
117
- end
118
-
119
- get '' do
120
- pass unless relationship_link?
121
-
122
- serialize_linkage
123
- end
124
-
125
106
  register RelationshipRoutes.const_get \
126
107
  rel_type.to_s.split('_').map(&:capitalize).join.to_sym
127
108
 
@@ -2,45 +2,57 @@
2
2
  module Sinja
3
3
  module ResourceRoutes
4
4
  ACTIONS = %i[index show create update destroy].freeze
5
- CONFLICT_ACTIONS = %i[create update].freeze
6
5
 
7
6
  def self.registered(app)
8
7
  app.def_action_helpers(ACTIONS, app)
9
8
 
9
+ app.head '', :pfilters=>:id do
10
+ allow :get=>:show
11
+ end
12
+
10
13
  app.get '', :pfilters=>:id, :actions=>:show do
11
14
  ids = params['filter'].delete('id')
12
15
  ids = ids.split(',') if ids.respond_to?(:split)
13
16
 
14
17
  opts = {}
15
18
  resources = [*ids].tap(&:uniq!).map! do |id|
16
- self.resource, opts = show(id)
17
- not_found "Resource '#{id}' not found" unless resource
18
- resource
19
+ tmp, opts = show(id)
20
+ raise NotFoundError, "Resource '#{id}' not found" unless tmp
21
+ tmp
19
22
  end
20
23
 
21
24
  # TODO: Serialize collection with opts from last model found?
22
25
  serialize_models(resources, opts)
23
26
  end
24
27
 
25
- app.get '', :actions=>:index do
26
- serialize_models(*index)
28
+ app.head '' do
29
+ allow :get=>:index, :post=>:create
27
30
  end
28
31
 
29
- app.get '/:id', :actions=>:show do |id|
30
- self.resource, opts = show(id)
31
- not_found "Resource '#{id}' not found" unless resource
32
- serialize_model(resource, opts)
32
+ app.get '', :actions=>:index do
33
+ serialize_models(*index)
33
34
  end
34
35
 
35
36
  app.post '', :actions=>:create do
36
37
  sanity_check!
37
- halt 403, 'Client-generated IDs not supported' \
38
- if data[:id] && method(:create).arity != 2
39
38
 
40
- _, self.resource, opts = transaction do
41
- create(attributes, data[:id]).tap do |id, *|
42
- dispatch_relationship_requests!(id, :method=>:patch)
43
- end
39
+ opts = {}
40
+ transaction do
41
+ id, self.resource, opts =
42
+ begin
43
+ args = [attributes]
44
+ args << data[:id] if data.key?(:id)
45
+ create(*args)
46
+ rescue ArgumentError
47
+ if data.key?(:id)
48
+ raise ForbiddenError, 'Client-generated ID not supported'
49
+ else
50
+ raise ForbiddenError, 'Client-generated ID not provided'
51
+ end
52
+ end
53
+
54
+ dispatch_relationship_requests!(id, :from=>:create, :methods=>{ :has_many=>:post })
55
+ validate! if respond_to?(:validate!)
44
56
  end
45
57
 
46
58
  if resource
@@ -49,27 +61,35 @@ module Sinja
49
61
  headers 'Location'=>self_link
50
62
  end
51
63
  [201, content]
52
- elsif data[:id]
64
+ elsif data.key?(:id)
53
65
  204
54
66
  else
55
67
  raise ActionHelperError, "Unexpected return value(s) from `create' action helper"
56
68
  end
57
69
  end
58
70
 
59
- app.patch '/:id', :actions=>%i[show update] do |id|
71
+ app.head '/:id' do
72
+ allow :get=>:show, :patch=>:update, :delete=>:destroy
73
+ end
74
+
75
+ app.get '/:id', :actions=>:show do |id|
76
+ tmp, opts = show(id)
77
+ raise NotFoundError, "Resource '#{id}' not found" unless tmp
78
+ serialize_model(tmp, opts)
79
+ end
80
+
81
+ app.patch '/:id', :actions=>:update do |id|
60
82
  sanity_check!(id)
61
- self.resource, = show(id)
62
- not_found "Resource '#{id}' not found" unless resource
63
- serialize_model?(transaction do
83
+ tmp, opts = transaction do
64
84
  update(attributes).tap do
65
- dispatch_relationship_requests!(id, :method=>:patch)
85
+ dispatch_relationship_requests!(id, :from=>:update)
86
+ validate! if respond_to?(:validate!)
66
87
  end
67
- end)
88
+ end
89
+ serialize_model?(tmp, opts)
68
90
  end
69
91
 
70
- app.delete '/:id', :actions=>%i[show destroy] do |id|
71
- self.resource, = show(id)
72
- not_found "Resource '#{id}' not found" unless resource
92
+ app.delete '/:id', :actions=>:destroy do |id|
73
93
  _, opts = destroy
74
94
  serialize_model?(nil, opts)
75
95
  end
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
  module Sinja
3
- VERSION = '0.2.0.beta2'.freeze
3
+ VERSION = '1.0.0.pre1'
4
4
  end
@@ -18,13 +18,24 @@ Gem::Specification.new do |spec|
18
18
  end
19
19
  spec.require_paths = %w[lib]
20
20
 
21
- # TODO: relax these dependencies
22
- spec.add_dependency 'json', '~> 2.0.1'
23
- spec.add_dependency 'jsonapi-serializers', '~> 0.16.0'
24
- spec.add_dependency 'sinatra', '~> 2.0.0.beta2'
25
- spec.add_dependency 'sinatra-contrib', '~> 2.0.0.beta2'
21
+ spec.required_ruby_version = '>= 2.3.0'
26
22
 
27
- spec.add_development_dependency 'bundler', '~> 1.13'
23
+ spec.add_dependency 'activesupport', ">= #{ENV.fetch('rails', '4.2.7.1')}", '< 6'
24
+ spec.add_dependency 'json', '>= 1.8.3', '< 3'
25
+ spec.add_dependency 'jsonapi-serializers', '~> 0.16'
26
+ spec.add_dependency 'mustermann', '>= 1.0.0.beta2', '< 2'
27
+ spec.add_dependency 'sinatra', ">= #{ENV.fetch('sinatra', '1.4.7')}", '< 3'
28
+ spec.add_dependency 'sinatra-contrib', ">= #{ENV.fetch('sinatra', '1.4.7')}", '< 3'
29
+
30
+ spec.add_development_dependency 'bundler', '~> 1.11'
31
+ spec.add_development_dependency 'minitest', '~> 5.9'
32
+ spec.add_development_dependency 'minitest-hooks', '~> 1.4'
33
+ #spec.add_development_dependency 'munson', '~> 0.4'
28
34
  spec.add_development_dependency 'rake', '~> 11.3'
29
- spec.add_development_dependency 'rspec', '~> 3.0'
35
+ spec.add_development_dependency 'sequel', '~> 4.38'
36
+ if defined?(JRUBY_VERSION)
37
+ spec.add_development_dependency 'jdbc-sqlite3', '~> 3.8'
38
+ else
39
+ spec.add_development_dependency 'sqlite3', '~> 1.3'
40
+ end
30
41
  end
metadata CHANGED
@@ -1,85 +1,171 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinja
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0.beta2
4
+ version: 1.0.0.pre1
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-20 00:00:00.000000000 Z
11
+ date: 2016-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.7.1
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '6'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 4.2.7.1
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '6'
13
33
  - !ruby/object:Gem::Dependency
14
34
  name: json
15
35
  requirement: !ruby/object:Gem::Requirement
16
36
  requirements:
17
- - - "~>"
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 1.8.3
40
+ - - "<"
18
41
  - !ruby/object:Gem::Version
19
- version: 2.0.1
42
+ version: '3'
20
43
  type: :runtime
21
44
  prerelease: false
22
45
  version_requirements: !ruby/object:Gem::Requirement
23
46
  requirements:
24
- - - "~>"
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 1.8.3
50
+ - - "<"
25
51
  - !ruby/object:Gem::Version
26
- version: 2.0.1
52
+ version: '3'
27
53
  - !ruby/object:Gem::Dependency
28
54
  name: jsonapi-serializers
29
55
  requirement: !ruby/object:Gem::Requirement
30
56
  requirements:
31
57
  - - "~>"
32
58
  - !ruby/object:Gem::Version
33
- version: 0.16.0
59
+ version: '0.16'
34
60
  type: :runtime
35
61
  prerelease: false
36
62
  version_requirements: !ruby/object:Gem::Requirement
37
63
  requirements:
38
64
  - - "~>"
39
65
  - !ruby/object:Gem::Version
40
- version: 0.16.0
66
+ version: '0.16'
67
+ - !ruby/object:Gem::Dependency
68
+ name: mustermann
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 1.0.0.beta2
74
+ - - "<"
75
+ - !ruby/object:Gem::Version
76
+ version: '2'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 1.0.0.beta2
84
+ - - "<"
85
+ - !ruby/object:Gem::Version
86
+ version: '2'
41
87
  - !ruby/object:Gem::Dependency
42
88
  name: sinatra
43
89
  requirement: !ruby/object:Gem::Requirement
44
90
  requirements:
45
- - - "~>"
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: 1.4.7
94
+ - - "<"
46
95
  - !ruby/object:Gem::Version
47
- version: 2.0.0.beta2
96
+ version: '3'
48
97
  type: :runtime
49
98
  prerelease: false
50
99
  version_requirements: !ruby/object:Gem::Requirement
51
100
  requirements:
52
- - - "~>"
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 1.4.7
104
+ - - "<"
53
105
  - !ruby/object:Gem::Version
54
- version: 2.0.0.beta2
106
+ version: '3'
55
107
  - !ruby/object:Gem::Dependency
56
108
  name: sinatra-contrib
57
109
  requirement: !ruby/object:Gem::Requirement
58
110
  requirements:
59
- - - "~>"
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: 1.4.7
114
+ - - "<"
60
115
  - !ruby/object:Gem::Version
61
- version: 2.0.0.beta2
116
+ version: '3'
62
117
  type: :runtime
63
118
  prerelease: false
64
119
  version_requirements: !ruby/object:Gem::Requirement
65
120
  requirements:
66
- - - "~>"
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: 1.4.7
124
+ - - "<"
67
125
  - !ruby/object:Gem::Version
68
- version: 2.0.0.beta2
126
+ version: '3'
69
127
  - !ruby/object:Gem::Dependency
70
128
  name: bundler
71
129
  requirement: !ruby/object:Gem::Requirement
72
130
  requirements:
73
131
  - - "~>"
74
132
  - !ruby/object:Gem::Version
75
- version: '1.13'
133
+ version: '1.11'
76
134
  type: :development
77
135
  prerelease: false
78
136
  version_requirements: !ruby/object:Gem::Requirement
79
137
  requirements:
80
138
  - - "~>"
81
139
  - !ruby/object:Gem::Version
82
- version: '1.13'
140
+ version: '1.11'
141
+ - !ruby/object:Gem::Dependency
142
+ name: minitest
143
+ requirement: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - "~>"
146
+ - !ruby/object:Gem::Version
147
+ version: '5.9'
148
+ type: :development
149
+ prerelease: false
150
+ version_requirements: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - "~>"
153
+ - !ruby/object:Gem::Version
154
+ version: '5.9'
155
+ - !ruby/object:Gem::Dependency
156
+ name: minitest-hooks
157
+ requirement: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - "~>"
160
+ - !ruby/object:Gem::Version
161
+ version: '1.4'
162
+ type: :development
163
+ prerelease: false
164
+ version_requirements: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - "~>"
167
+ - !ruby/object:Gem::Version
168
+ version: '1.4'
83
169
  - !ruby/object:Gem::Dependency
84
170
  name: rake
85
171
  requirement: !ruby/object:Gem::Requirement
@@ -95,19 +181,33 @@ dependencies:
95
181
  - !ruby/object:Gem::Version
96
182
  version: '11.3'
97
183
  - !ruby/object:Gem::Dependency
98
- name: rspec
184
+ name: sequel
185
+ requirement: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - "~>"
188
+ - !ruby/object:Gem::Version
189
+ version: '4.38'
190
+ type: :development
191
+ prerelease: false
192
+ version_requirements: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - "~>"
195
+ - !ruby/object:Gem::Version
196
+ version: '4.38'
197
+ - !ruby/object:Gem::Dependency
198
+ name: sqlite3
99
199
  requirement: !ruby/object:Gem::Requirement
100
200
  requirements:
101
201
  - - "~>"
102
202
  - !ruby/object:Gem::Version
103
- version: '3.0'
203
+ version: '1.3'
104
204
  type: :development
105
205
  prerelease: false
106
206
  version_requirements: !ruby/object:Gem::Requirement
107
207
  requirements:
108
208
  - - "~>"
109
209
  - !ruby/object:Gem::Version
110
- version: '3.0'
210
+ version: '1.3'
111
211
  description:
112
212
  email:
113
213
  - mike@oobak.org
@@ -124,16 +224,28 @@ files:
124
224
  - Rakefile
125
225
  - bin/console
126
226
  - bin/setup
127
- - lib/role_list.rb
227
+ - demo-app/Gemfile
228
+ - demo-app/app.rb
229
+ - demo-app/base.rb
230
+ - demo-app/boot.rb
231
+ - demo-app/classes/author.rb
232
+ - demo-app/classes/comment.rb
233
+ - demo-app/classes/post.rb
234
+ - demo-app/classes/tag.rb
235
+ - demo-app/database.rb
236
+ - demo-app/test.rb
128
237
  - lib/sinatra/jsonapi.rb
129
238
  - lib/sinja.rb
130
239
  - lib/sinja/config.rb
240
+ - lib/sinja/errors.rb
241
+ - lib/sinja/extensions/sequel.rb
242
+ - lib/sinja/helpers/nested.rb
131
243
  - lib/sinja/helpers/relationships.rb
132
244
  - lib/sinja/helpers/sequel.rb
133
245
  - lib/sinja/helpers/serializers.rb
246
+ - lib/sinja/method_override.rb
134
247
  - lib/sinja/relationship_routes/has_many.rb
135
248
  - lib/sinja/relationship_routes/has_one.rb
136
- - lib/sinja/relationship_routes/sequel.rb
137
249
  - lib/sinja/resource.rb
138
250
  - lib/sinja/resource_routes.rb
139
251
  - lib/sinja/version.rb
@@ -150,7 +262,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
150
262
  requirements:
151
263
  - - ">="
152
264
  - !ruby/object:Gem::Version
153
- version: '0'
265
+ version: 2.3.0
154
266
  required_rubygems_version: !ruby/object:Gem::Requirement
155
267
  requirements:
156
268
  - - ">"
@@ -158,7 +270,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
270
  version: 1.3.1
159
271
  requirements: []
160
272
  rubyforge_project:
161
- rubygems_version: 2.5.1
273
+ rubygems_version: 2.5.2
162
274
  signing_key:
163
275
  specification_version: 4
164
276
  summary: RESTful, JSON:API-compliant web services in Sinatra