activeinteractor 1.0.2 → 1.0.3

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: f83ff894cc3ce150f3a97ab2b006035acf9f27c181040f02c04e0058b3d3dd49
4
- data.tar.gz: fd8d75ec724d1b35eec4b7cd2017c74a9fb61c54209f58830a7ffd5b780b5ba8
3
+ metadata.gz: bb760f4036450f632f5734d8575389462d80019152811ea20479754b04754aa2
4
+ data.tar.gz: ebc3326b09daec0e2b654a29014794aedf9e3d508d869d3b4aa1e180bf9b2fc8
5
5
  SHA512:
6
- metadata.gz: '085c0039e71216c6c3dc734a4aad4bb30fc0bf5fa5babe8a9b72e2b961c5066732c5bdfbc5eb1566ababc42cbcf38e1b5e3d87e61a94251e08eec2c13a99c524'
7
- data.tar.gz: fa572ba8068e8856bfbc405500cffd4b398f47c67d7b665a02ead9a5d701cdb62983f17e6eb6df7c13f7b29b981c58054a7613b6eb4970a488c45d15acc7bc40
6
+ metadata.gz: 1358b3923eb46838102b5e5e6c84021465cd3ade38954e9a36432019491e598f1d4b6aa2a1467816d81753a59d47d5205a10f5fa1fad2212110d926266ef20ae
7
+ data.tar.gz: 6318ca357ceba0628cbc13dec29bfe7971b8c5341a31f11c1b930ba060bc311b79813548fe73b99a4a689bb10019f9b02ea4e2c62d8753e260a47521a57c63f1
data/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning].
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [v1.0.3] - 2020-02-10
11
+
12
+ ### Added
13
+
14
+ - `ActiveInteractor::Context::Errors`
15
+ - `ActiveInteractor::Context::Status#resolve`
16
+
17
+ ### Fixed
18
+
19
+ - [\#168](https://github.com/aaronmallen/activeinteractor/issues/168) `#classify` is called on const arguments
20
+ - [\#169](https://github.com/aaronmallen/activeinteractor/issues/169) If some of the interactors of the organizer fail
21
+ error message is not persisted.
22
+
10
23
  ## [v1.0.2] - 2020-02-04
11
24
 
12
25
  ### Added
@@ -175,7 +188,8 @@ and this project adheres to [Semantic Versioning].
175
188
 
176
189
  <!-- versions -->
177
190
 
178
- [Unreleased]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.2...HEAD
191
+ [Unreleased]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.3...HEAD
192
+ [v1.0.3]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.2...v1.0.3
179
193
  [v1.0.2]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.1...v1.0.2
180
194
  [v1.0.1]: https://github.com/aaronmallen/activeinteractor/compare/v1.0.0...v1.0.1
181
195
  [v1.0.0]: https://github.com/aaronmallen/activeinteractor/compare/v0.1.7...v1.0.0
@@ -64,6 +64,7 @@ module ActiveInteractor
64
64
 
65
65
  autoload :Attributes
66
66
  autoload :Base
67
+ autoload :Errors
67
68
  autoload :Loader
68
69
  autoload :Status
69
70
  end
@@ -50,7 +50,7 @@ module ActiveInteractor
50
50
  # @param context [Hash, Base, Class] attributes to assign to the {Base context}
51
51
  # @return [Base] a new instance of {Base}
52
52
  def initialize(context = {})
53
- merge_errors!(context.errors) if context.respond_to?(:errors)
53
+ merge_errors!(context) if context.respond_to?(:errors)
54
54
  copy_flags!(context)
55
55
  copy_called!(context)
56
56
  context = context_attributes_as_hash(context) || {}
@@ -105,7 +105,7 @@ module ActiveInteractor
105
105
  # @param context [Class] a {Base context} instance to be merged
106
106
  # @return [self] the {Base context} instance
107
107
  def merge!(context)
108
- merge_errors!(context.errors) if context.respond_to?(:errors)
108
+ merge_errors!(context) if context.respond_to?(:errors)
109
109
  copy_flags!(context)
110
110
  context.each_pair do |key, value|
111
111
  public_send("#{key}=", value) unless value.nil?
@@ -143,14 +143,6 @@ module ActiveInteractor
143
143
  public_send("#{key}=", value)
144
144
  end
145
145
  end
146
-
147
- def merge_errors!(errors)
148
- if errors.is_a? String
149
- self.errors.add(:context, errors)
150
- else
151
- self.errors.merge!(errors)
152
- end
153
- end
154
146
  end
155
147
  end
156
148
  end
@@ -326,6 +326,7 @@ module ActiveInteractor
326
326
  include ActiveModel::Attributes
327
327
  include ActiveModel::Validations
328
328
  include ActiveInteractor::Context::Attributes
329
+ include ActiveInteractor::Context::Errors
329
330
  include ActiveInteractor::Context::Status
330
331
  end
331
332
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveInteractor
4
+ module Context
5
+ # Context error methods. Because {Errors} is a module classes should include {Errors} rather than
6
+ # inherit from it.
7
+ #
8
+ # @api private
9
+ # @author Aaron Allen <hello@aaronmallen.me>
10
+ # @since 1.0.3
11
+ module Errors
12
+ # Generic errors generated outside of validation.
13
+ #
14
+ # @return [ActiveModel::Errors] an instance of `ActiveModel::Errors`
15
+ def failure_errors
16
+ @failure_errors ||= ActiveModel::Errors.new(self)
17
+ end
18
+
19
+ private
20
+
21
+ def handle_errors(errors)
22
+ if errors.is_a?(String)
23
+ failure_errors.add(:context, errors)
24
+ else
25
+ failure_errors.merge!(errors)
26
+ end
27
+ end
28
+
29
+ def merge_errors!(other)
30
+ errors.merge!(other.errors)
31
+ failure_errors.merge!(other.errors)
32
+ failure_errors.merge!(other.failure_errors)
33
+ end
34
+
35
+ def resolve_errors
36
+ errors.merge!(failure_errors)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -22,7 +22,7 @@ module ActiveInteractor
22
22
  # @param interactor_class [Const] an {ActiveInteractor::Base interactor} class
23
23
  # @return [Const] a class that inherits from {Base}
24
24
  def self.create(context_class_name, interactor_class)
25
- interactor_class.const_set(context_class_name.to_s.classify, Class.new(BASE_CONTEXT))
25
+ interactor_class.const_set(context_class_name.to_s.camelize, Class.new(BASE_CONTEXT))
26
26
  end
27
27
 
28
28
  # Find or create a {Base context} class for a given {ActiveInteractor::Base interactor}. If a class exists
@@ -37,8 +37,9 @@ module ActiveInteractor
37
37
  # @see https://api.rubyonrails.org/classes/ActiveModel/Errors.html ActiveModel::Errors
38
38
  # @raise [Error::ContextFailure]
39
39
  def fail!(errors = nil)
40
- merge_errors!(errors) if errors
40
+ handle_errors(errors) if errors
41
41
  @_failed = true
42
+ resolve
42
43
  raise ActiveInteractor::Error::ContextFailure, self
43
44
  end
44
45
 
@@ -59,6 +60,16 @@ module ActiveInteractor
59
60
  end
60
61
  alias fail? failure?
61
62
 
63
+ # Resolve an instance of {Base context}. Called when an interactor
64
+ # is finished with it's context.
65
+ #
66
+ # @since 1.0.3
67
+ # @return [self] the instance of {Base context}
68
+ def resolve
69
+ resolve_errors
70
+ self
71
+ end
72
+
62
73
  # {#rollback! Rollback} an instance of {Base context}. Any {ActiveInteractor::Base interactors} the instance has
63
74
  # been passed via the {#called!} method are asked to roll themselves back by invoking their
64
75
  # {Interactor::Perform#rollback #rollback} methods. The instance is also flagged as rolled back.
@@ -211,7 +211,7 @@ module ActiveInteractor
211
211
  # @return [Const] the {Base interactor} class' {ActiveInteractor::Context::Base context} class
212
212
  def contextualize_with(klass)
213
213
  @context_class = begin
214
- context_class = klass.to_s.classify.safe_constantize
214
+ context_class = klass.to_s.camelize.safe_constantize
215
215
  raise(ActiveInteractor::Error::InvalidContextClass, klass) unless context_class
216
216
 
217
217
  context_class
@@ -366,6 +366,7 @@ module ActiveInteractor
366
366
  # @return [Class] the {ActiveInteractor::Context::Base context} instance
367
367
  def finalize_context!
368
368
  context.called!(self)
369
+ context.resolve
369
370
  context
370
371
  end
371
372
 
@@ -40,6 +40,7 @@ module ActiveInteractor
40
40
  extend ActiveInteractor::Context::Attributes::ClassMethods
41
41
 
42
42
  include ActiveInteractor::Context::Attributes
43
+ include ActiveInteractor::Context::Errors
43
44
  include ActiveInteractor::Context::Status
44
45
  include ActiveInteractor::Models::InstanceMethods
45
46
  delegate :each_pair, to: :attributes
@@ -40,7 +40,7 @@ module ActiveInteractor
40
40
  # {Interactor::Perform::ClassMethods#perform .perform}. See {Interactor::Perform::Options}.
41
41
  # @return [InteractorInterface] a new instance of {InteractorInterface}
42
42
  def initialize(interactor_class, options = {})
43
- @interactor_class = interactor_class.to_s.classify.safe_constantize
43
+ @interactor_class = interactor_class.to_s.camelize.safe_constantize
44
44
  @filters = options.select { |key, _value| CONDITIONAL_FILTERS.include?(key) }
45
45
  @perform_options = options.reject { |key, _value| CONDITIONAL_FILTERS.include?(key) }
46
46
  end
@@ -3,5 +3,5 @@
3
3
  module ActiveInteractor
4
4
  # The ActiveInteractor version
5
5
  # @return [String] the ActiveInteractor version
6
- VERSION = '1.0.2'
6
+ VERSION = '1.0.3'
7
7
  end
@@ -6,8 +6,8 @@ class <%= class_name %> < ApplicationOrganizer
6
6
 
7
7
  <%- end -%>
8
8
  <%- if interactors.any? -%>
9
- organize <%= interactors.map(&:classify).join(", ") %>
9
+ organize <%= interactors.join(", ") %>
10
10
  <%- else -%>
11
- # organize Interactor1, Interactor2
11
+ # organize :interactor_1, :interactor_2
12
12
  <%- end -%>
13
13
  end
@@ -27,6 +27,17 @@ RSpec.describe ActiveInteractor::Base do
27
27
  subject
28
28
  expect(described_class.context_class).to eq TestContext
29
29
  end
30
+
31
+ # https://github.com/aaronmallen/activeinteractor/issues/168
32
+ context 'when singularized' do
33
+ let!(:singularized_class) { build_context('PlaceData') }
34
+ let(:klass) { 'PlaceData' }
35
+
36
+ it 'is expected to assign the appropriate context class' do
37
+ subject
38
+ expect(described_class.context_class).to eq PlaceData
39
+ end
40
+ end
30
41
  end
31
42
 
32
43
  context 'when passed as a symbol' do
@@ -36,6 +47,17 @@ RSpec.describe ActiveInteractor::Base do
36
47
  subject
37
48
  expect(described_class.context_class).to eq TestContext
38
49
  end
50
+
51
+ # https://github.com/aaronmallen/activeinteractor/issues/168
52
+ context 'when singularized' do
53
+ let!(:singularized_class) { build_context('PlaceData') }
54
+ let(:klass) { :place_data }
55
+
56
+ it 'is expected to assign the appropriate context class' do
57
+ subject
58
+ expect(described_class.context_class).to eq PlaceData
59
+ end
60
+ end
39
61
  end
40
62
 
41
63
  context 'when passed as a constant' do
@@ -45,6 +67,17 @@ RSpec.describe ActiveInteractor::Base do
45
67
  subject
46
68
  expect(described_class.context_class).to eq TestContext
47
69
  end
70
+
71
+ # https://github.com/aaronmallen/activeinteractor/issues/168
72
+ context 'when singularized' do
73
+ let!(:singularized_class) { build_context('PlaceData') }
74
+ let(:klass) { PlaceData }
75
+
76
+ it 'is expected to assign the appropriate context class' do
77
+ subject
78
+ expect(described_class.context_class).to eq PlaceData
79
+ end
80
+ end
48
81
  end
49
82
  end
50
83
  end
@@ -127,7 +127,7 @@ RSpec.describe ActiveInteractor::Context::Base do
127
127
  it { expect { subject }.to raise_error(ActiveInteractor::Error::ContextFailure) }
128
128
 
129
129
  it 'is expected not to merge errors' do
130
- expect(instance.errors).not_to receive(:merge!)
130
+ expect(instance.errors).not_to receive(:merge!).with(nil)
131
131
  subject
132
132
  rescue ActiveInteractor::Error::ContextFailure # rubocop:disable Lint/SuppressedException
133
133
  end
@@ -69,6 +69,25 @@ RSpec.describe 'A basic organizer', type: :integration do
69
69
  expect_any_instance_of(test_interactor_2).not_to receive(:perform!)
70
70
  subject
71
71
  end
72
+
73
+ # https://github.com/aaronmallen/activeinteractor/issues/169
74
+ context 'with error message "something went wrong"' do
75
+ let!(:test_interactor_1) do
76
+ build_interactor('TestInteractor1') do
77
+ def perform
78
+ context.fail!('something went wrong')
79
+ end
80
+ end
81
+ end
82
+
83
+ it { expect { subject }.not_to raise_error }
84
+ it { is_expected.to be_a interactor_class.context_class }
85
+ it { is_expected.to be_failure }
86
+ it 'is expected to have errors "something went wrong" on :context' do
87
+ expect(subject.errors[:context]).not_to be_empty
88
+ expect(subject.errors[:context]).to include 'something went wrong'
89
+ end
90
+ end
72
91
  end
73
92
  end
74
93
 
@@ -92,6 +111,25 @@ RSpec.describe 'A basic organizer', type: :integration do
92
111
  expect_any_instance_of(test_interactor_1).to receive(:rollback)
93
112
  subject
94
113
  end
114
+
115
+ # https://github.com/aaronmallen/activeinteractor/issues/169
116
+ context 'with error message "something went wrong"' do
117
+ let!(:test_interactor_2) do
118
+ build_interactor('TestInteractor2') do
119
+ def perform
120
+ context.fail!('something went wrong')
121
+ end
122
+ end
123
+ end
124
+
125
+ it { expect { subject }.not_to raise_error }
126
+ it { is_expected.to be_a interactor_class.context_class }
127
+ it { is_expected.to be_failure }
128
+ it 'is expected to have errors "something went wrong" on :context' do
129
+ expect(subject.errors[:context]).not_to be_empty
130
+ expect(subject.errors[:context]).to include 'something went wrong'
131
+ end
132
+ end
95
133
  end
96
134
  end
97
135
 
data/spec/spec_helper.rb CHANGED
@@ -1,21 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  begin
4
- require 'codacy-coverage'
5
- require 'simplecov'
6
-
7
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
8
- [
9
- SimpleCov::Formatter::HTMLFormatter,
10
- Codacy::Formatter
11
- ]
12
- )
13
-
14
- SimpleCov.start do
15
- enable_coverage :branch
16
- add_filter '/spec/'
17
- add_filter '/lib/rails/**/*.rb'
18
- track_files '/lib/**/*.rb'
4
+ if ENV['CODACY_PROJECT_TOKEN']
5
+ require 'codacy-coverage'
6
+
7
+ Codacy::Reporter.start
8
+ else
9
+ require 'simplecov'
10
+
11
+ SimpleCov.start do
12
+ enable_coverage :branch
13
+ add_filter '/spec/'
14
+ add_filter '/lib/rails/**/*.rb'
15
+ track_files '/lib/**/*.rb'
16
+ end
19
17
  end
20
18
  rescue LoadError
21
19
  puts 'Skipping coverage...'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeinteractor
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Allen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-04 00:00:00.000000000 Z
11
+ date: 2020-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -111,6 +111,7 @@ files:
111
111
  - lib/active_interactor/configurable.rb
112
112
  - lib/active_interactor/context/attributes.rb
113
113
  - lib/active_interactor/context/base.rb
114
+ - lib/active_interactor/context/errors.rb
114
115
  - lib/active_interactor/context/loader.rb
115
116
  - lib/active_interactor/context/status.rb
116
117
  - lib/active_interactor/error.rb
@@ -200,10 +201,10 @@ licenses:
200
201
  - MIT
201
202
  metadata:
202
203
  bug_tracker_uri: https://github.com/aaronmallen/activeinteractor/issues
203
- changelog_uri: https://github.com/aaronmallen/activeinteractor/blob/v1.0.2/CHANGELOG.md
204
- documentation_uri: https://www.rubydoc.info/gems/activeinteractor/1.0.2
204
+ changelog_uri: https://github.com/aaronmallen/activeinteractor/blob/v1.0.3/CHANGELOG.md
205
+ documentation_uri: https://www.rubydoc.info/gems/activeinteractor/1.0.3
205
206
  hompage_uri: https://github.com/aaronmallen/activeinteractor
206
- source_code_uri: https://github.com/aaronmallen/activeinteractor/tree/v1.0.2
207
+ source_code_uri: https://github.com/aaronmallen/activeinteractor/tree/v1.0.3
207
208
  wiki_uri: https://github.com/aaronmallen/activeinteractor/wiki
208
209
  post_install_message:
209
210
  rdoc_options: []