activeinteractor 1.0.2 → 1.0.3

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