interactor_support 1.0.5 → 1.0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 315dd265352ed997c5b69934609478e6e5f8b61bbe74bba065acf495f7dac41c
4
- data.tar.gz: 6a942d0ccb3f3323d156d85f302987b4af7809cdcce852dca56fb9b1ae8419bc
3
+ metadata.gz: bf8cda13999c9971a1499398e23135ef6e870af7581ffe7e9f173b9d7c4231ea
4
+ data.tar.gz: 48c401b5b42e80ccea3db170ecb1e8476861522804b800ad28651eebfa674222
5
5
  SHA512:
6
- metadata.gz: 2fb25732876e4702b12da283cf4d205504c597ecb904cc9406af3a9825814e8ba518e84bcc615c1c8aadc31ed81cfb9008e2addf759273c2688e029d5b07628e
7
- data.tar.gz: 5615bf986b8ab272cff775eebbd874c336be93f4e17333d37fd6e87a04b9d74d110b2fcab2a48fb1e0ff86956785b4a7825d03f4201701700566825c2335c633
6
+ metadata.gz: 2adf5f317e6432571a24358e9e1ba453397fcfd5f6b83758930fd45bef652dfb01ead87aa68fcfec385151bd60646127a087968e332baa0193e5bb9f7ac14ff6
7
+ data.tar.gz: a6a670cacb4abd50812a82232dcbeb1e582eea72d3526b2659f4dccd827226c6e07c6b12f6c39b282eb276db482d02911cd4d0e125094f8c1d114fba5459c899
data/CHANGELOG.md CHANGED
@@ -29,3 +29,9 @@
29
29
  - Introduce `InteractorSupport.configuration.logger` and `log_level` for customizable logging
30
30
  - Override `assign_attributes` to integrate attribute ignoring and error-raising behavior
31
31
  - Improve test coverage for unknown attribute handling and logging
32
+
33
+ ## [1.0.6] - 2025-09-24
34
+
35
+ - Wrap request object validation failures from `Organizable#organize` in `InteractorSupport::Errors::InvalidRequestObject` for consistent controller handling
36
+ - Honor `configuration.log_unknown_request_object_attributes` when logging ignored request object keys
37
+ - Improve request object error messaging for failed casts and unknown attributes
data/README.md CHANGED
@@ -559,7 +559,7 @@ The Organizable concern provides utility methods to simplify working with intera
559
559
 
560
560
  Features
561
561
 
562
- - organize: Call interactors with request objects, optionally namespaced under a context_key.
562
+ - organize: Call interactors with request objects, optionally namespaced under a context_key, and wrap validation failures in a consistent error.
563
563
  - request_params: Extract, shape, filter, rename, flatten, and merge incoming params in a clear and declarative way.
564
564
  - Built for controllers or service entry points.
565
565
  - Rails-native feel — works seamlessly with strong params.
@@ -588,6 +588,18 @@ organize(MyInteractor, params: request_params, request_object: MyRequest, contex
588
588
  # => MyInteractor.call({ request: MyRequest.new(params) })
589
589
  ```
590
590
 
591
+ Validation failures inside the request object raise `InteractorSupport::Errors::InvalidRequestObject`. The exception exposes the request class and its validation messages, making it straightforward to surface errors back to the caller.
592
+
593
+ ```rb
594
+ def create
595
+ organize(CreateUserInteractor, params: request_params(:user), request_object: CreateUserRequest)
596
+ redirect_to dashboard_path
597
+ rescue InteractorSupport::Errors::InvalidRequestObject => e
598
+ flash.now[:alert] = "Unable to continue: #{e.errors.to_sentence}"
599
+ render :new, status: :unprocessable_entity
600
+ end
601
+ ```
602
+
591
603
  #### #request_params(\*top_level_keys, merge: {}, except: [], rewrite: [])
592
604
 
593
605
  Returns a shaped parameter hash derived from params.permit!. You can extract specific top-level keys, rename them, flatten values, apply defaults, and remove unwanted fields.
@@ -38,8 +38,22 @@ module InteractorSupport
38
38
  # # => Calls MyInteractor with an instance of MyRequest initialized with request_params at :context_key.
39
39
  # # # => The context will contain { request: MyRequest.new(request_params) }
40
40
  def organize(interactor, params:, request_object:, context_key: nil)
41
+ request_payload = request_object.new(params)
42
+
41
43
  @context = interactor.call(
42
- context_key ? { context_key => request_object.new(params) } : request_object.new(params),
44
+ context_key ? { context_key => request_payload } : request_payload,
45
+ )
46
+ rescue ActiveModel::ValidationError => e
47
+ errors =
48
+ if e.model&.respond_to?(:errors)
49
+ e.model.errors.full_messages
50
+ else
51
+ []
52
+ end
53
+
54
+ raise InteractorSupport::Errors::InvalidRequestObject.new(
55
+ request_class: request_object,
56
+ errors: errors,
43
57
  )
44
58
  end
45
59
 
@@ -1,8 +1,55 @@
1
1
  module InteractorSupport
2
2
  module Errors
3
3
  class UnknownAttribute < StandardError
4
- def initialize(attribute)
5
- super("Unknown attribute: #{attribute}")
4
+ attr_reader :attribute, :owner
5
+
6
+ def initialize(attribute, owner: nil)
7
+ @attribute = attribute
8
+ @owner = owner
9
+ super(build_message(attribute, owner))
10
+ end
11
+
12
+ private
13
+
14
+ def build_message(attribute, owner)
15
+ name =
16
+ case attribute
17
+ when String then attribute
18
+ when Symbol then attribute.to_s
19
+ else attribute.inspect
20
+ end
21
+
22
+ owner_name =
23
+ if owner.respond_to?(:name) && owner.name
24
+ owner.name
25
+ elsif owner.respond_to?(:to_s)
26
+ owner.to_s
27
+ else
28
+ owner
29
+ end
30
+
31
+ suffix = owner_name ? " for #{owner_name}" : ''
32
+ "Unknown attribute: #{name}#{suffix}"
33
+ end
34
+ end
35
+
36
+ class InvalidRequestObject < StandardError
37
+ attr_reader :request_class, :errors
38
+
39
+ def initialize(request_class:, errors: [])
40
+ @request_class = request_class
41
+ @errors = Array(errors)
42
+
43
+ request_name =
44
+ if request_class.respond_to?(:name) && request_class.name
45
+ request_class.name
46
+ else
47
+ request_class.to_s
48
+ end
49
+
50
+ detail = @errors.any? ? ": #{@errors.join(', ')}" : ''
51
+
52
+ super("Invalid #{request_name}#{detail}")
6
53
  end
7
54
  end
8
55
  end
@@ -105,12 +105,14 @@ module InteractorSupport
105
105
  if respond_to?(setter)
106
106
  send(setter, v)
107
107
  elsif respond_to?(:ignore_unknown_attributes?) && ignore_unknown_attributes?
108
- InteractorSupport.configuration.logger.log(
109
- InteractorSupport.configuration.log_level,
110
- "InteractorSupport::RequestObject ignoring unknown attribute '#{k}' for #{self.class.name}.",
111
- )
108
+ if InteractorSupport.configuration.log_unknown_request_object_attributes
109
+ InteractorSupport.configuration.logger.log(
110
+ InteractorSupport.configuration.log_level,
111
+ "InteractorSupport::RequestObject ignoring unknown attribute '#{k}' for #{self.class.name}.",
112
+ )
113
+ end
112
114
  else
113
- raise Errors::UnknownAttribute, "`#{k}` for #{self.class.name}."
115
+ raise Errors::UnknownAttribute.new(k, owner: self.class)
114
116
  end
115
117
  end
116
118
  end
@@ -232,7 +234,13 @@ module InteractorSupport
232
234
  message = ":#{type} is not a supported type. Supported types are: #{SUPPORTED_TYPES.join(", ")}"
233
235
  raise TypeError, message
234
236
  rescue
235
- raise TypeError, "Cannot cast #{value.inspect} to #{type.name}"
237
+ type_name =
238
+ if type.respond_to?(:name) && type.name
239
+ type.name
240
+ else
241
+ type.to_s
242
+ end
243
+ raise TypeError, "Cannot cast #{value.inspect} to #{type_name}"
236
244
  end
237
245
  end
238
246
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module InteractorSupport
4
- VERSION = '1.0.5'
4
+ VERSION = '1.0.6'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: interactor_support
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charlie Mitchell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-07-01 00:00:00.000000000 Z
11
+ date: 2025-09-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: