shamu 0.0.9 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
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