shamu 0.0.9 → 0.0.11

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +5 -3
  4. data/bin/rake +17 -0
  5. data/bin/rspec +17 -0
  6. data/lib/shamu/attributes.rb +3 -1
  7. data/lib/shamu/events/active_record/migration.rb +6 -6
  8. data/lib/shamu/json_api/builder_methods/identifier.rb +18 -4
  9. data/lib/shamu/json_api/context.rb +3 -1
  10. data/lib/shamu/json_api/error.rb +7 -1
  11. data/lib/shamu/json_api/presenter.rb +23 -1
  12. data/lib/shamu/json_api/rails/controller.rb +195 -62
  13. data/lib/shamu/locale/en.yml +3 -1
  14. data/lib/shamu/rails/controller.rb +5 -2
  15. data/lib/shamu/rails/entity.rb +29 -15
  16. data/lib/shamu/rails/railtie.rb +12 -7
  17. data/lib/shamu/services/active_record.rb +2 -2
  18. data/lib/shamu/services/active_record_crud.rb +36 -38
  19. data/lib/shamu/services/error.rb +11 -1
  20. data/lib/shamu/services/lazy_transform.rb +9 -4
  21. data/lib/shamu/services/request_support.rb +5 -2
  22. data/lib/shamu/services/result.rb +40 -7
  23. data/lib/shamu/services/service.rb +17 -8
  24. data/lib/shamu/services/service_call_failed_error.rb +4 -0
  25. data/lib/shamu/version.rb +2 -2
  26. data/shamu.gemspec +4 -4
  27. data/spec/lib/shamu/json_api/builder_methods/identifier_spec.rb +45 -0
  28. data/spec/lib/shamu/json_api/rails/controller_spec.rb +141 -7
  29. data/spec/lib/shamu/json_api/rails/responder_spec.rb +9 -9
  30. data/spec/lib/shamu/rails/controller_spec.rb +4 -4
  31. data/spec/lib/shamu/rails/entity_spec.rb +34 -16
  32. data/spec/lib/shamu/rails/features_spec.rb +6 -6
  33. data/spec/lib/shamu/services/active_record_crud_spec.rb +12 -7
  34. data/spec/lib/shamu/services/lazy_transform_spec.rb +23 -14
  35. data/spec/lib/shamu/services/request_support_spec.rb +15 -1
  36. data/spec/lib/shamu/services/result_spec.rb +37 -1
  37. data/spec/lib/shamu/services/service_spec.rb +25 -14
  38. data/spec/spec_helper.rb +1 -1
  39. metadata +23 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e6619b53b0ccf99f395e9ef5687a3eb9cd2fd661
4
- data.tar.gz: 425daaaa1d330ffeb4ec885a59e9b0c63e5e1548
3
+ metadata.gz: 202b665e0d910a1c418ca7a5ea4bb35d441c16c7
4
+ data.tar.gz: 8089bef31c11ba75389f8c6b417b2b8f1c34e9dd
5
5
  SHA512:
6
- metadata.gz: 6f1cad8cd5b39cbfe4b186d4dfbb5b4c4f1c5e549dabb829cc801d708516fb15be7e16b8da1f9e1829203ae1d4b695ac6acb8f943767910f716591deb93fc659
7
- data.tar.gz: 9cccb625e79e236cc7829cf86db880f0a0ac814847b305137c7e33d222e63ca507c0e373c243c9472d40159e503f65fb5169fb4c1d9e5bd684a6def1aac54662
6
+ metadata.gz: ab5be942fca9ff72725316a71a84761a28a954c3cb4445073eb94126556569ada8747795c5788811349740e828d83dfabf516fd1b9851c0ac296b993e95c5075
7
+ data.tar.gz: cce3f9ff5242932c85e3dfc92977ff0a4469439a865213aeb264ee67cde98f3731aeea157651dbbc60fd18c12acff8d938e1eed42b9b2e35750cb00ba7e00496
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.3.1
1
+ 2.4.0
data/Gemfile CHANGED
@@ -3,9 +3,11 @@ source "https://rubygems.org"
3
3
  # Specify your gem"s dependencies in shamu.gemspec
4
4
  gemspec
5
5
 
6
+ gem 'rake'
7
+
6
8
  group :test do
7
- gem "activerecord", "~> 4.2.5"
8
- gem "actionpack", "~> 4.2.5"
9
+ gem "activerecord", "~> 5.0"
10
+ gem "actionpack", "~> 5.0"
9
11
  gem "responders", "~> 2.1.2"
10
12
  gem "kaminari", "~> 0.16.3", require: false
11
13
 
@@ -30,4 +32,4 @@ group :test do
30
32
 
31
33
  gem "codeclimate-test-reporter", group: :test, require: nil
32
34
  gem "rspec_junit_formatter", "~> 0.2.2", platforms: :mri
33
- end
35
+ end
data/bin/rake ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'rake' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("rake", "rake")
data/bin/rspec ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'rspec' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] = File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("rspec-core", "rspec")
@@ -121,6 +121,8 @@ module Shamu
121
121
  # Allow protected attributes to be used without explicitly being set.
122
122
  # All 'Attributes' classes are them selves the explicit set of permitted
123
123
  # attributes.
124
+ elsif attributes.respond_to?( :to_unsafe_h )
125
+ attributes.to_unsafe_h
124
126
  elsif attributes.respond_to?( :to_hash )
125
127
  attributes.to_hash.symbolize_keys
126
128
  elsif attributes.respond_to?( :to_h )
@@ -270,4 +272,4 @@ module Shamu
270
272
  end
271
273
 
272
274
  end
273
- end
275
+ end
@@ -3,14 +3,14 @@ module Shamu
3
3
  module ActiveRecord
4
4
 
5
5
  # Prepare the database for storing event messages.
6
- class Migration < ::ActiveRecord::Migration
6
+ class Migration < ::ActiveRecord::Migration[5.0]
7
7
 
8
8
  self.verbose = false
9
9
 
10
10
  # rubocop:disable Metrics/MethodLength
11
11
 
12
12
  def up
13
- return if table_exists? Message.table_name
13
+ return if data_source_exists? Message.table_name
14
14
 
15
15
  # TODO: Need to provide a means for using 64-bit primary keys in
16
16
  # databases that support it. Otherwise limited to 4B events.
@@ -38,12 +38,12 @@ module Shamu
38
38
  end
39
39
 
40
40
  def down
41
- drop_table Message.table_name if table_exists? Message.table_name
42
- drop_table Channel.table_name if table_exists? Channel.table_name
43
- drop_table Runner.table_name if table_exists? Runner.table_name
41
+ drop_table Message.table_name if data_source_exists? Message.table_name
42
+ drop_table Channel.table_name if data_source_exists? Channel.table_name
43
+ drop_table Runner.table_name if data_source_exists? Runner.table_name
44
44
  end
45
45
 
46
46
  end
47
47
  end
48
48
  end
49
- end
49
+ end
@@ -7,9 +7,15 @@ module Shamu
7
7
  # @param [String] type of the resource.
8
8
  # @param [Object] id of the resource.
9
9
  # @return [self]
10
- def identifier( type, id = nil )
11
- output[:type] = @type = type.to_s
12
- output[:id] = id.to_s
10
+ def identifier( type, id = :not_set )
11
+ output[:type] = @type = json_type( type )
12
+
13
+ output[:id] =
14
+ if id == :not_set
15
+ type.id if type.respond_to?( :id )
16
+ else
17
+ id.to_s
18
+ end
13
19
 
14
20
  self
15
21
  end
@@ -24,6 +30,14 @@ module Shamu
24
30
 
25
31
  attr_reader :type
26
32
 
33
+ def json_type( type )
34
+ type = type.json_type if type.respond_to?( :json_type )
35
+ type = type.model_name.element if type.respond_to?( :model_name )
36
+ type = type.name.demodulize.underscore if type.is_a?( Module )
37
+
38
+ type
39
+ end
40
+
27
41
  def require_identifier!
28
42
  fail IncompleteResourceError unless type
29
43
  end
@@ -31,4 +45,4 @@ module Shamu
31
45
  end
32
46
  end
33
47
  end
34
- end
48
+ end
@@ -114,6 +114,8 @@ module Shamu
114
114
  def parse_fields( raw )
115
115
  return {} unless raw
116
116
 
117
+ raw = raw.to_unsafe_hash if raw.respond_to?( :to_unsafe_hash )
118
+
117
119
  raw.each_with_object( {} ) do |(type, fields), parsed|
118
120
  fields = fields.split( "," ) if fields.is_a?( String )
119
121
 
@@ -146,4 +148,4 @@ module Shamu
146
148
 
147
149
  end
148
150
  end
149
- end
151
+ end
@@ -24,5 +24,11 @@ module Shamu
24
24
  super translate( :no_presenter, class: resource.class, namespaces: namespaces )
25
25
  end
26
26
  end
27
+
28
+ class NoJsonBodyError < Error
29
+ def initialize( message = :no_json_body )
30
+ super
31
+ end
32
+ end
27
33
  end
28
- end
34
+ end
@@ -45,6 +45,28 @@ module Shamu
45
45
  attr_reader :resource
46
46
  attr_reader :builder
47
47
 
48
+
49
+ # Present all the named attributes of the {#resource}.
50
+ # @param [Array<Symbol>] names of the resource to present.
51
+ def resource_attributes( *names )
52
+ names.map do |name|
53
+ attribute name, attribute_value( resource.send( name ) )
54
+ end
55
+ end
56
+
57
+ # Get a JSON API safe version of the value.
58
+ # @param [Object] value the value to be coerced.
59
+ # @return [Object]
60
+ def attribute_value( value )
61
+ case value
62
+ when Date, DateTime then
63
+ value.to_date.to_time.iso8601
64
+ when Time, ActiveSupport::TimeWithZone then
65
+ value.iso8601
66
+ else value
67
+ end
68
+ end
69
+
48
70
  end
49
71
  end
50
- end
72
+ end
@@ -8,68 +8,122 @@ module Shamu
8
8
  module Controller
9
9
  extend ActiveSupport::Concern
10
10
 
11
+ # Pattern to identify request params that hold 'ids'
12
+ ID_PATTERN = /\A(id|.+_id)\z/
13
+
11
14
  included do
12
15
  before_action do
13
16
  render json: json_error( "The 'include' parameter is not supported" ), status: :bad_request if params[:include] # rubocop:disable Metrics/LineLength
14
17
  request.formats = [ :json_api, :json ]
15
18
  end
16
- end
17
19
 
18
- # def process_action( * )
19
- # # If no format has been specfied, default to json_api
20
- # request.parameters[:format] ||= "json_api"
21
- # super
22
- # end
23
-
24
- # Builds a well-formed JSON API response for a single resource.
25
- #
26
- # @param [Object] resource to present as JSON.
27
- # @param [Class] presenter {Presenter} class to use when building the
28
- # response for the given resource. If not given, attempts to find a
29
- # presenter by calling {Context#find_presenter}.
30
- # @param (see #json_context)
31
- # @yield (response) write additional top-level links and meta
32
- # information.
33
- # @yieldparam [JsonApi::Response] response
34
- # @return [JsonApi::Response] the presented JSON response.
35
- def json_resource( resource, presenter = nil, **context, &block )
36
- response = build_json_response( context )
37
- response.resource resource, presenter
38
- yield response if block_given?
39
- response.to_json
20
+ rescue_from Exception, with: :render_unhandled_exception unless ::Rails.env.test?
40
21
  end
41
22
 
42
- # Builds a well-formed JSON API response for a collection of resources.
43
- #
44
- # @param [Enumerable<Object>] resources to present as a JSON array.
45
- # @param [Class] presenter {Presenter} class to use when building the
46
- # response for each of the resources. If not given, attempts to find
47
- # a presenter by calling {Context#find_presenter}
48
- # @param (see #json_context)
49
- # @yield (response) write additional top-level links and meta
50
- # information.
51
- # @yieldparam [JsonApi::Response] response
52
- # @return [JsonApi::Response] the presented JSON response.
53
- def json_collection( resources, presenter = nil, pagination: :auto, **context, &block )
54
- response = build_json_response( context )
55
- response.collection resources, presenter
56
- json_paginate_resources response, resources, pagination
57
- yield response if block_given?
58
- response.to_json
59
- end
23
+ private
60
24
 
61
- # Write all the validation errors from a record to the response.
62
- #
63
- # @param (see Shamu::JsonApi::Response#validation_errors)
64
- # @yield (builder, attr, message)
65
- # @yieldparam (see Shamu::JsonApi::Response#validation_errors)
66
- # @return [JsonApi::Response] the presented JSON response.
67
- def json_validation_errors( errors, **context, &block )
68
- response = build_json_response( context )
69
- response.validation_errors errors, &block
70
-
71
- response.to_json
72
- end
25
+ # @!visibility public
26
+ #
27
+ # Builds a well-formed JSON API response for a single resource.
28
+ #
29
+ # @param [Object] resource to present as JSON.
30
+ # @param [Class] presenter {Presenter} class to use when building the
31
+ # response for the given resource. If not given, attempts to find a
32
+ # presenter by calling {Context#find_presenter}.
33
+ # @param (see #json_context)
34
+ # @yield (response) write additional top-level links and meta
35
+ # information.
36
+ # @yieldparam [JsonApi::Response] response
37
+ # @return [JsonApi::Response] the presented JSON response.
38
+ def json_resource( resource, presenter = nil, **context, &block )
39
+ response = build_json_response( context )
40
+ response.resource resource, presenter
41
+ yield response if block_given?
42
+ response.as_json
43
+ end
44
+
45
+ # @!visibility public
46
+ #
47
+ # Present the `resource` as json and render it adding appropriate
48
+ # HTTP response codes and headers for standard JSON API actions.
49
+ #
50
+ # @param [Symbol,Number] status the HTTP status code.
51
+ # @param (see #json_resource)
52
+ def render_resource( resource, presenter: nil, status: nil, location: nil, **context, &block )
53
+ json = json_resource( resource, presenter, **context, &block )
54
+
55
+ # Include canonical url to resource if present
56
+ if data = json[ "data" ]
57
+ if links = data[ "links" ]
58
+ location ||= links[ "self" ] if links[ "self" ]
59
+ end
60
+ end
61
+
62
+ render json: json, status: status, location: location
63
+ end
64
+
65
+ # @!visibility public
66
+ #
67
+ # Renders a {Shamu::Services::Result} presenting either the
68
+ # validation errors or the entity.
69
+ #
70
+ # @param [Shamu::Services::Result] result of a service call
71
+ # @param (see #json_resource)
72
+ def render_result( result, presenter: nil, status: nil, **context, &block )
73
+ if result.valid?
74
+ if result.entity
75
+ status ||= case request.method
76
+ when 'POST' then :created
77
+ when 'DELETE' then :no_content
78
+ else :ok
79
+ end
80
+
81
+ render_resource result.entity, presenter: presenter, status: status, **context, &block
82
+ else
83
+ head status || :no_content
84
+ end
85
+ else
86
+ render json: json_validation_errors( result.errors, **context ), status: :unprocessable_entity
87
+ end
88
+ end
89
+
90
+ # Builds a well-formed JSON API response for a collection of resources.
91
+ #
92
+ # @param [Enumerable<Object>] resources to present as a JSON array.
93
+ # @param [Class] presenter {Presenter} class to use when building the
94
+ # response for each of the resources. If not given, attempts to find
95
+ # a presenter by calling {Context#find_presenter}
96
+ # @param (see #json_context)
97
+ # @yield (response) write additional top-level links and meta
98
+ # information.
99
+ # @yieldparam [JsonApi::Response] response
100
+ # @return [JsonApi::Response] the presented JSON response.
101
+ def json_collection( resources, presenter = nil, pagination: :auto, **context, &block )
102
+ response = build_json_response( context )
103
+ response.collection resources, presenter
104
+ json_paginate_resources response, resources, pagination
105
+ yield response if block_given?
106
+ response.as_json
107
+ end
108
+
109
+ # Present the resources as json and render it adding appropriate HTTP
110
+ # response codes and headers.
111
+ def render_collection( resources, presenter: nil, pagination: :auto, **context, &block )
112
+ render json: json_collection( resources, presenter, pagination: pagination, **context, &block )
113
+ end
114
+
115
+ # Write all the validation errors from a record to the response.
116
+ #
117
+ # @param (see Shamu::JsonApi::Response#validation_errors)
118
+ # @yield (builder, attr, message)
119
+ # @yieldparam (see Shamu::JsonApi::Response#validation_errors)
120
+ # @return [JsonApi::Response] the presented JSON response.
121
+ def json_validation_errors( errors, **context, &block )
122
+ response = build_json_response( context )
123
+ response.validation_errors errors, &block
124
+
125
+ response.as_json
126
+ end
73
127
 
74
128
  private
75
129
 
@@ -93,12 +147,15 @@ module Shamu
93
147
  end
94
148
  end
95
149
 
96
- def json_page_parameter( page_param_name, param, value )
97
- page_params = params.reverse_merge page_param_name => {}
98
- page_params[page_param_name][param] = value
150
+ def json_page_parameter( page_param_name, param, value )
151
+ params = self.params
152
+ params = params.to_unsafe_hash if params.respond_to?( :to_unsafe_hash )
99
153
 
100
- page_params
101
- end
154
+ page_params = params.reverse_merge page_param_name => {}
155
+ page_params[page_param_name][param] = value
156
+
157
+ page_params
158
+ end
102
159
 
103
160
  # @!visibility public
104
161
  #
@@ -127,12 +184,29 @@ module Shamu
127
184
 
128
185
  response.error error do |builder|
129
186
  builder.http_status json_http_status_code_from_error( error )
187
+ annotate_json_error( error, builder )
130
188
  yield builder if block_given?
131
189
  end
132
190
 
133
191
  response.to_json
134
192
  end
135
193
 
194
+ def render_unhandled_exception( exception )
195
+ render json: json_error( exception ), status: :internal_server_error
196
+ end
197
+
198
+ # @!visibility public
199
+ #
200
+ # Annotate an exception that is being rendered to the browser - for
201
+ # example to add current user or security information if available.
202
+ def annotate_json_error( error, builder )
203
+ if ::Rails.env.development?
204
+ builder.meta :type, error.class.to_s
205
+ builder.meta :backtrace, error.backtrace
206
+ end
207
+ end
208
+
209
+
136
210
  JSON_CONTEXT_KEYWORDS = [ :fields, :namespaces, :presenters ].freeze
137
211
 
138
212
  # @!visibility public
@@ -157,8 +231,67 @@ module Shamu
157
231
  # parameters sent by the client.
158
232
  def json_context( fields: :not_set, namespaces: :not_set, presenters: :not_set )
159
233
  Shamu::JsonApi::Context.new fields: fields == :not_set ? json_context_fields : fields,
160
- namespaces: namespaces == :not_set ? json_context_namespaces : namespaces,
161
- presenters: presenters == :not_set ? json_context_presenters : presenters
234
+ namespaces: namespaces == :not_set ? json_context_namespaces : namespaces,
235
+ presenters: presenters == :not_set ? json_context_presenters : presenters
236
+ end
237
+
238
+
239
+ # See (Shamu::Rails::Entity#request_params)
240
+ def request_params( param_key )
241
+ if relationships = json_request_payload[ :relationships ]
242
+ return map_json_resource_payload( relationships[ param_key ][ :data ] ) if relationships.key?( param_key )
243
+ end
244
+
245
+ payload = map_json_resource_payload( json_request_payload )
246
+
247
+ request.params.each do |key, value|
248
+ if ID_PATTERN =~ key
249
+ payload[ key.to_sym ] ||= value
250
+ end
251
+ end
252
+
253
+ payload
254
+ end
255
+
256
+ def map_json_resource_payload( resource )
257
+ payload = resource[ :attributes ] ? resource[ :attributes ].dup : {}
258
+ payload[ :id ] = resource[ :id ] if resource.key?( :id )
259
+
260
+ if relationships = resource[ :relationships ]
261
+ relationships.each do |key, value|
262
+ attr_key = "#{ key.to_s.singularize }_id"
263
+
264
+ if value[ :data ].is_a?( Array )
265
+ attr_key += 's' if value[ :data ].is_a?( Array )
266
+
267
+ payload[ attr_key.to_sym ] = value[ :data ].map { |d| d[ :id ] }
268
+ payload[ key ] = value[ :data ].map { |d| map_json_resource_payload( d ) }
269
+ else
270
+ payload[ attr_key.to_sym ] = value[ :data ][ :id ]
271
+ payload[ key ] = map_json_resource_payload( value[ :data ] )
272
+ end
273
+ end
274
+ end
275
+
276
+ payload
277
+ end
278
+
279
+ # @!visibility public
280
+ #
281
+ # Map a JSON body to a hash.
282
+ # @return [Hash] the parsed JSON payload.
283
+ def json_request_payload
284
+ @json_request_payload ||=
285
+ begin
286
+ body = request.body.read || "{}"
287
+ json = JSON.parse( body, symbolize_names: true )
288
+
289
+ unless json.blank?
290
+ fail NoJsonBodyError unless json[ :data ]
291
+ end
292
+
293
+ json ? json[ :data ] : {}
294
+ end
162
295
  end
163
296
 
164
297
 
@@ -167,7 +300,7 @@ module Shamu
167
300
  end
168
301
 
169
302
  def json_context_namespaces
170
- name = self.class.name.sub /Controller$/, ""
303
+ name = self.class.name.sub( /Controller$/, "" )
171
304
  namespaces = [ name.pluralize ]
172
305
  loop do
173
306
  name = name.deconstantize
@@ -217,4 +350,4 @@ module Shamu
217
350
  end
218
351
  end
219
352
  end
220
- end
353
+ end
@@ -8,8 +8,9 @@ en:
8
8
 
9
9
  services:
10
10
  errors:
11
- active_record_crud_missing_resource: The resource has not been defined. Add `resource entity_class, model_class` to {%service}.
11
+ active_record_crud_missing_resource: "The resource has not been defined. Add `resource entity_class, model_class` to %{service}."
12
12
  incomplete_setup: The service has not been setup. See included modules documentation for details.
13
+ service_request_failed: 'The service call failed with: %{errors}.'
13
14
 
14
15
 
15
16
  security:
@@ -30,3 +31,4 @@ en:
30
31
  errors:
31
32
  incomplete_resource: "`identifier` was not called to define the type and id of the resource."
32
33
  no_presenter: No presenter available for %{class} objects. Looked in %{namespaces}.
34
+ no_json_body: "Missing `data` node for JSON API body. Override `json_request_payload` if no body is expected."
@@ -16,8 +16,11 @@ module Shamu
16
16
  included do
17
17
  include Scorpion::Rails::Controller
18
18
 
19
- helper_method :permit?
20
- helper_method :current_user
19
+ # ActionController::API does not have #helper_method
20
+ if respond_to?( :helper_method )
21
+ helper_method :permit?
22
+ helper_method :current_user
23
+ end
21
24
  end
22
25
 
23
26
  private
@@ -9,7 +9,7 @@ module Shamu
9
9
  private
10
10
 
11
11
  def fetch_entity( service, param )
12
- service.find( params[ param ] )
12
+ service.find( params[ param ] ) if params.key?( param )
13
13
  end
14
14
 
15
15
  def fetch_entities( service, param )
@@ -22,16 +22,24 @@ module Shamu
22
22
  return unless request = service.request_for( action, entity )
23
23
 
24
24
  param_key ||= entity.model_name.param_key
25
+ request.assign_attributes( request_params( param_key ) )
25
26
 
27
+ service.authorize!( action, entity, request ) if service.respond_to?( :authorize! )
28
+ request
29
+ end
30
+
31
+ # @!visibility public
32
+ #
33
+ # Get the raw request hash params for the given parameter key.
34
+ # @param [Symbol] param_key key of the entity params to fetch.
35
+ # @return [Hash] the params
36
+ def request_params( param_key )
26
37
  strong_param = :"#{ param_key }_params"
27
38
  if respond_to?( strong_param, true )
28
- request.assign_attributes( send( strong_param ) )
39
+ send( strong_param )
29
40
  else
30
- request.assign_attributes( params[ param_key ] )
41
+ params[ param_key ]
31
42
  end
32
-
33
- service.authorize!( action, entity, request ) if service.respond_to?( :authorize! )
34
- request
35
43
  end
36
44
 
37
45
  def load_entity( method:, list_method:, action: nil, only: nil, except: nil )
@@ -44,11 +52,16 @@ module Shamu
44
52
  def matching_entity_action?( action, only:, except: )
45
53
  return if only.present? && !only.include?( action )
46
54
  return if except.present? && except.include?( action )
47
- true
55
+
56
+ !create_action?( action )
57
+ end
58
+
59
+ def list_action?( action = params[ :action ] )
60
+ action.to_sym == :index
48
61
  end
49
62
 
50
- def list_action?( action )
51
- action == :index
63
+ def create_action?( action = params[ :action ] )
64
+ [ :new, :create ].include?( action.to_sym )
52
65
  end
53
66
 
54
67
  class_methods do
@@ -101,7 +114,7 @@ module Shamu
101
114
  # not being modified in an :update request.
102
115
  def entity( entity_class, through: nil, as: nil, list: nil, only: nil, except: nil, param: :id, list_param: nil, action: nil, param_key: nil ) # rubocop:disable Metrics/LineLength
103
116
  as ||= entity_as_name( entity_class )
104
- through ||= :"#{ as }_service"
117
+ through ||= :"#{ as.to_s.pluralize }_service"
105
118
  list ||= as.to_s.pluralize.to_sym
106
119
 
107
120
  define_entity_method( as, through, param )
@@ -132,7 +145,7 @@ module Shamu
132
145
  @#{ as } = fetch_entity( #{ through }, :#{ param } ) # @entity = fetch_entity( entity_service, :id )
133
146
  end # end
134
147
 
135
- helper_method :#{ as }
148
+ helper_method :#{ as } if respond_to?( :helper_method )
136
149
  RUBY
137
150
  end
138
151
 
@@ -145,7 +158,7 @@ module Shamu
145
158
  @#{ as } = fetch_entities( #{ through }, #{ param ? ":#{ param }" : 'nil' } ) # @entities = fetch_entities( entity_service, nil )
146
159
  end # end
147
160
 
148
- helper_method :#{ as }
161
+ helper_method :#{ as } if respond_to?( :helper_method )
149
162
  RUBY
150
163
  end
151
164
 
@@ -155,14 +168,15 @@ module Shamu
155
168
 
156
169
  def #{ as }_request # def entity_request
157
170
  return @#{ as }_request if defined? @#{ as }_request # return @entity_request if defined? @entity_request
158
- @#{ as }_request = fetch_entity_request( #{ through }, #{ as }, :#{ as } ) # @entity_request = fetch_entity_request( entity_service, entity, :entity )
171
+ _entity = #{ as } unless create_action? # _entity = entity unless create_action?
172
+ @#{ as }_request = fetch_entity_request( #{ through }, _entity, :#{ as } ) # @entity_request = fetch_entity_request( entity_service, _entity, :entity )
159
173
  end # end
160
174
 
161
- helper_method :#{ as }_request
175
+ helper_method :#{ as }_request if respond_to?( :helper_method )
162
176
  RUBY
163
177
  end
164
178
 
165
179
  end
166
180
  end
167
181
  end
168
- end
182
+ end