actioninteractor 0.1.0.1 → 0.2.4

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: 2204c4f56ead70dd52e32bf5a656a2421c5b85e7de1ceec6d43caa7446ae9866
4
- data.tar.gz: a915d6983916e0a41b12fc65806fae06e0e76c6a4dbce3a1e907de519ecc7b7f
3
+ metadata.gz: 6482218b9fe304483abc4ae16d38e51c8de0cd6b9fe9c1155caaca1bff6a660c
4
+ data.tar.gz: 98db252d344409893580173b7f982419db14986a598e7b772eba8bca22ce5ed6
5
5
  SHA512:
6
- metadata.gz: 6d41987911103f961d7286fd4b57cf55ba646e4021bf98361dd9962691ff9cecd780896f65cf3a02318a3bb5d01915037d6ef524093251fcf193d6a5df5b53ae
7
- data.tar.gz: 44eae8fdacf7ff12e16e93178acec405db9178f1f8394ee2f6a140d152a433a97e5ef7c0ac8e20155cddc1de7ae4e5521876faf12230f2257a260746e1982b6a
6
+ metadata.gz: bb962e73b235f0ebe37c906d00904ee71e1efce001eeef1d3e756317c9994cc2473443629b538451c25024aeb6b6fe949b0542f0a5c324aeb92fa4fe10e9377b
7
+ data.tar.gz: 6fa15f43b82685f294d5e63a5a01fdde4775cae10405c99023c48aa698ce79da9dc7ba246a337221869037e17bd9e7e8031a07ef83c2e74eb4ab1ef6d66a027d
@@ -14,24 +14,25 @@ module ActionInteractor
14
14
  #
15
15
  # class RegistrationInteractor < ActionInteractor::Base
16
16
  # def execute
17
- # return fail! unless payload[:name]
17
+ # return failure! unless payload[:name]
18
18
  # user = User.create!(name: payload[:name])
19
19
  # notiticaion = user.notifications.create!(name: 'Welcome')
20
20
  # RegistrationNotificationJob.perform_later!
21
21
  # results.add(:user, user)
22
- # success!
22
+ # successful!
23
23
  # end
24
24
  # end
25
25
  class Base
26
- attr_reader :payload, :errors, :results
26
+ attr_reader :payload, :errors, :results, :state, :interactor_name
27
27
 
28
28
  # Initialize with payload
29
29
  # Errors and Results data and initial state will be set.
30
30
  def initialize(payload)
31
31
  @payload = payload
32
- @errors = Errors.new
33
- @results = Results.new
34
- reset!
32
+ @errors = payload[:errors] || Errors.new
33
+ @results = payload[:results] || Results.new
34
+ @state = payload[:state] || ExecutionState.new
35
+ @interactor_name = payload[:interactor_name] || underscore(self.class.name)
35
36
  end
36
37
 
37
38
  # Execute the operation with given payload.
@@ -39,24 +40,24 @@ module ActionInteractor
39
40
  def execute
40
41
  # if the interactor already finished execution, do nothing.
41
42
  return if finished?
42
- # if contract is not satisfied= (ex: payload is empty), mark as failed.
43
- return fail! if payload.nil?
43
+ # if contract is not satisfied= (ex: payload is empty), mark as failure.
44
+ return failure! if payload.nil?
44
45
  # (Implement some codes for the operation.)
45
46
 
46
- # if finished execution, mark as success.
47
- success!
47
+ # if finished execution, mark as successful.
48
+ successful!
48
49
  end
49
50
 
50
51
  # Execute the operation with given payload.
51
52
  # If there are some errors, ActionInteractor::ExeuctionError will be raised.
52
53
  def execute!
53
54
  execute
54
- success? || raise(ExecutionError.new("Failed to execute the interactor"))
55
+ successful? || raise(ExecutionError.new("Failed to execute the interactor"))
55
56
  end
56
57
 
57
58
  # Returns `true` if marked as finished otherwise `false`.
58
59
  def finished?
59
- @_finished
60
+ state.successful? || state.failure?
60
61
  end
61
62
 
62
63
  # Returns `true` if not marked as finished otherwise `false`.
@@ -65,44 +66,46 @@ module ActionInteractor
65
66
  end
66
67
 
67
68
  # Returns `true` if marked as success and there are no errors otherwise `false`.
68
- def success?
69
- @_success && @errors.empty?
69
+ def successful?
70
+ state.successful? && @errors.empty?
70
71
  end
71
72
 
73
+ alias_method :success?, :successful?
74
+
72
75
  # Returns `true` if not marked as success or there are some errors otherwise `false`.
73
76
  def failure?
74
- !success?
77
+ !successful?
75
78
  end
76
79
 
77
80
  # Returns `true` if the operation was not successful and not finished otherwise `false`.
78
81
  def aborted?
79
- failure? && unfinished?
82
+ state.aborted?
80
83
  end
81
84
 
82
85
  # Reset statuses.
83
86
  def reset!
84
- @_success = false
85
- @_finished = false
87
+ @state = State.new
86
88
  end
87
89
 
88
- # Mark the operation as failed and unfinished.
90
+ # Mark the operation as aborted.
89
91
  def abort!
90
- @_success = false
91
- @_finished = false
92
+ state.aborted!
92
93
  end
93
94
 
94
- # Mark the operation as success and finished.
95
- def success!
96
- @_success = true
97
- @_finished = true
95
+ # Mark the operation as successful.
96
+ def successful!
97
+ state.successful!
98
98
  end
99
99
 
100
- # Mask the operation as failed and finished.
101
- def fail!
102
- @_success = false
103
- @_finished = true
100
+ alias_method :success!, :successful!
101
+
102
+ # Mask the operation as failure.
103
+ def failure!
104
+ state.failure!
104
105
  end
105
106
 
107
+ alias_method :fail!, :failure!
108
+
106
109
  class << self
107
110
  # Execute the operation with given payload.
108
111
  def execute(payload)
@@ -115,6 +118,16 @@ module ActionInteractor
115
118
  new(payload).tap(&:execute!)
116
119
  end
117
120
  end
121
+
122
+ private
123
+
124
+ def underscore(name)
125
+ name.gsub(/::/, "__")
126
+ .gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
127
+ .gsub(/([a-z\d])([A-Z])/,'\1_\2')
128
+ .tr("-", "_")
129
+ .downcase
130
+ end
118
131
  end
119
132
 
120
133
  class ExecutionError < StandardError; end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionInteractor
4
+ class Composite < Base
5
+ # == Action \Interactor \Composite
6
+ #
7
+ # An interactor class which containing multiple interactors
8
+ # using the composite pattern.
9
+ #
10
+ # It can be used for execute multiple operations and it will be
11
+ # marked as successful if all operations executed successful.
12
+ # (otherwise it will be marked as failure.)
13
+ attr_reader :interactors
14
+
15
+ # Initialize with payload and an array for containing interactors.
16
+ def initialize(payload)
17
+ super
18
+ @interactors = []
19
+ end
20
+
21
+ # Execute containing interactors' `execute` method with given payload.
22
+ def execute
23
+ return if finished?
24
+ return failure! if payload.nil?
25
+
26
+ interactors.each_with_index do |interactor, index|
27
+ execute_sub_interactor(interactor, index)
28
+ return failure! if interactor.failure?
29
+ end
30
+
31
+ successful!
32
+ end
33
+
34
+ # Add an interactor to the interactors array.
35
+ def add(interactor)
36
+ interactors << interactor
37
+ end
38
+
39
+ # Delete the interactor from the interactors array.
40
+ def delete(interactor)
41
+ interactors.delete(interactor)
42
+ end
43
+
44
+ private
45
+
46
+ def execute_sub_interactor(interactor, index)
47
+ interactor.execute
48
+ if interactor.successful?
49
+ interactor.results.each_pair do |attr_name, value|
50
+ results.add(sub_attr_key(interactor, index, attr_name), value)
51
+ end
52
+ else interactor.failure?
53
+ interactor.errors.each_pair do |attr_name, value|
54
+ errors.add(sub_attr_key(interactor, index, attr_name), value)
55
+ end
56
+ end
57
+ end
58
+
59
+ def sub_attr_key(interactor, index, attr_name)
60
+ "#{interactor.interactor_name}_#{index}__#{attr_name}".to_sym
61
+ end
62
+ end
63
+ end
@@ -11,7 +11,7 @@ module ActionInteractor
11
11
 
12
12
  attr_reader :errors
13
13
 
14
- def_delegators :@errors, :clear, :keys, :values, :[], :empty?, :any?
14
+ def_delegators :@errors, :clear, :keys, :values, :[], :empty?, :any?, :each_pair
15
15
 
16
16
  def initialize(*)
17
17
  @errors = {}
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionInteractor
4
+ # == Action \Interactor \Execution \State
5
+ #
6
+ # State machine used in `ActionInteractor::Base`
7
+ class ExecutionState < State
8
+ # Define default states
9
+ STATES = [
10
+ :initial, # Initial state
11
+ :processing, # The operation is processing
12
+ :successful, # The operation is finished successfully
13
+ :failure, # The operation is failed
14
+ :aborted, # The operation is aborted
15
+ ]
16
+
17
+ # Define default transitions
18
+ # key: target state, value: original states
19
+ TRANSITIONS = {
20
+ initial: [:processing],
21
+ processing: [:initial],
22
+ successful: [:initial, :processing],
23
+ failure: [:initial, :processing],
24
+ aborted: [:initial, :processing],
25
+ }
26
+ end
27
+ end
@@ -11,7 +11,7 @@ module ActionInteractor
11
11
 
12
12
  attr_reader :results
13
13
 
14
- def_delegators :@results, :clear, :keys, :values, :[]
14
+ def_delegators :@results, :clear, :keys, :values, :[], :empty?, :any?, :each, :each_pair
15
15
 
16
16
  def initialize(*)
17
17
  @results = {}
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionInteractor
4
+ # == Action \Interactor \State
5
+ #
6
+ # Simple state machine for general purpose.
7
+ # You can declare STATES / TRANSITIONS in subclasses for
8
+ # customizing the behavior.
9
+ # See also `ActionInteractor::ExecutionState`
10
+ class State
11
+ # Define default states
12
+ STATES = [:initial, :finished]
13
+
14
+ # Define default transitions
15
+ # key: target state, value: original states
16
+ TRANSITIONS = {
17
+ finished: [:initial]
18
+ }
19
+
20
+ attr_reader :state
21
+
22
+ def initialize(initial_state=nil)
23
+ @state = initial_state || default_state
24
+ end
25
+
26
+ # Default initial state
27
+ # (You can override in subclasses.)
28
+ def default_state
29
+ :initial
30
+ end
31
+
32
+ # Returns true if transition to target_state from current state
33
+ def valid_transition?(target_state)
34
+ transitions[target_state].include?(state)
35
+ end
36
+
37
+ # Available states for the instance
38
+ def states
39
+ self.class.states
40
+ end
41
+
42
+ # Avaiolable transitions for the instance's states
43
+ def transitions
44
+ self.class.transitions
45
+ end
46
+
47
+ # Available states for the class
48
+ def self.states
49
+ self::STATES
50
+ end
51
+
52
+ # Available transitions for the class
53
+ def self.transitions
54
+ self::TRANSITIONS
55
+ end
56
+
57
+ def method_missing(method_name, *args)
58
+ name = method_name.to_s
59
+ # Returns true if state_name is the same as current state
60
+ if status_method_with_suffix?(name, "?")
61
+ return state == name.chop.to_sym
62
+ end
63
+
64
+ # Set current state to the state_name, otherwise raises error
65
+ if status_method_with_suffix?(name, "!")
66
+ state_name = name.chop.to_sym
67
+ unless valid_transition?(state_name)
68
+ raise TransitionError.new("Could not change state :#{state_name} from :#{state}")
69
+ end
70
+ @state = state_name
71
+ return
72
+ end
73
+
74
+ super
75
+ end
76
+
77
+ private
78
+
79
+ def status_method_with_suffix?(method_name, suffix)
80
+ return false unless method_name.end_with?(suffix)
81
+ states.include?(method_name.chop.to_sym)
82
+ end
83
+
84
+ # Error for invalid transitions
85
+ class TransitionError < StandardError; end
86
+ end
87
+ end
@@ -6,4 +6,7 @@ module ActionInteractor
6
6
  autoload :Base, "action_interactor/base"
7
7
  autoload :Errors, "action_interactor/errors"
8
8
  autoload :Results, "action_interactor/results"
9
+ autoload :State, "action_interactor/state"
10
+ autoload :ExecutionState, "action_interactor/execution_state"
11
+ autoload :Composite, "action_interactor/composite"
9
12
  end
data/test/base_test.rb CHANGED
@@ -33,7 +33,13 @@ class BaseTest < Test::Unit::TestCase
33
33
  assert_instance_of(ActionInteractor::Results, interactor.results)
34
34
  end
35
35
 
36
- test "#success? is true" do
36
+ test "#successful? is true" do
37
+ payload = {}
38
+ interactor = ActionInteractor::Base.execute(payload)
39
+ assert interactor.successful?
40
+ end
41
+
42
+ test "#success? (alias) is true" do
37
43
  payload = {}
38
44
  interactor = ActionInteractor::Base.execute(payload)
39
45
  assert interactor.success?
@@ -47,7 +53,7 @@ class BaseTest < Test::Unit::TestCase
47
53
 
48
54
  test "#aborted? is true after #abort!" do
49
55
  payload = {}
50
- interactor = ActionInteractor::Base.execute(payload)
56
+ interactor = ActionInteractor::Base.new(payload)
51
57
  interactor.abort!
52
58
  assert interactor.aborted?
53
59
  end
@@ -0,0 +1,92 @@
1
+ require "test/unit"
2
+ require_relative "../lib/actioninteractor"
3
+
4
+ class ChiefInteractor < ActionInteractor::Composite
5
+ end
6
+
7
+ class CookInteractor < ActionInteractor::Base
8
+ def execute
9
+ results.add(:steak, "Juicy beaf steak")
10
+ successful!
11
+ end
12
+ end
13
+
14
+ class ArrangeInteractor < ActionInteractor::Base
15
+ attr_reader :broken_dish
16
+
17
+ def initialize(payload)
18
+ super
19
+ @broken_dish = payload[:broken_dish].nil? ? false : payload[:broken_dish]
20
+ end
21
+
22
+ def execute
23
+ if broken_dish
24
+ errors.add(:dish, "Broken dish")
25
+ failure!
26
+ else
27
+ successful!
28
+ end
29
+ end
30
+ end
31
+
32
+
33
+ class CompositeTest < Test::Unit::TestCase
34
+ test "initialized successfully" do
35
+ payload = {}
36
+ interactor = ActionInteractor::Composite.new(payload)
37
+ assert_equal(interactor.success?, false)
38
+ assert_equal(interactor.finished?, false)
39
+ assert_equal(interactor.errors.empty?, true)
40
+ end
41
+
42
+ test "add interactors" do
43
+ payload = {}
44
+ chief_interactor = ChiefInteractor.new(payload)
45
+ cook_interactor = CookInteractor.new(interactor_name: "cook")
46
+ arrange_interactor = ArrangeInteractor.new(interactor_name: "arrange")
47
+ chief_interactor.add(cook_interactor)
48
+ chief_interactor.add(arrange_interactor)
49
+ interactor_names = chief_interactor.interactors.map(&:interactor_name)
50
+ assert_equal(interactor_names, %w[cook arrange])
51
+ end
52
+
53
+ test "remove interactor" do
54
+ payload = {}
55
+ chief_interactor = ChiefInteractor.new(payload)
56
+ cook_interactor = CookInteractor.new(interactor_name: "cook")
57
+ arrange_interactor = ArrangeInteractor.new(interactor_name: "arrange")
58
+ chief_interactor.add(cook_interactor)
59
+ chief_interactor.add(arrange_interactor)
60
+ chief_interactor.delete(cook_interactor)
61
+ interactor_names = chief_interactor.interactors.map(&:interactor_name)
62
+ assert_equal(interactor_names, %w[arrange])
63
+ end
64
+
65
+ test "successful in executing all interactors" do
66
+ payload = {}
67
+ chief_interactor = ChiefInteractor.new(payload)
68
+ cook_interactor = CookInteractor.new(interactor_name: "cook")
69
+ arrange_interactor = ArrangeInteractor.new(interactor_name: "arrange")
70
+ chief_interactor.add(cook_interactor)
71
+ chief_interactor.add(arrange_interactor)
72
+ chief_interactor.execute
73
+ assert_equal(chief_interactor.successful?, true)
74
+ assert_equal(chief_interactor.results.keys, [:cook_0__steak])
75
+ assert_equal(chief_interactor.results[:cook_0__steak], "Juicy beaf steak")
76
+ end
77
+
78
+ test "failure in executing the last interactor" do
79
+ payload = {}
80
+ chief_interactor = ChiefInteractor.new(payload)
81
+ cook_interactor = CookInteractor.new(interactor_name: "cook")
82
+ arrange_interactor = ArrangeInteractor.new(interactor_name: "arrange", broken_dish: true)
83
+ chief_interactor.add(cook_interactor)
84
+ chief_interactor.add(arrange_interactor)
85
+ chief_interactor.execute
86
+ assert_equal(chief_interactor.successful?, false)
87
+ assert_equal(chief_interactor.results.keys, [:cook_0__steak])
88
+ assert_equal(chief_interactor.results[:cook_0__steak], "Juicy beaf steak")
89
+ assert_equal(chief_interactor.errors.keys, [:arrange_1__dish])
90
+ assert_equal(chief_interactor.errors[:arrange_1__dish], "Broken dish")
91
+ end
92
+ end
@@ -0,0 +1,60 @@
1
+ require "test/unit"
2
+ require_relative "../lib/actioninteractor"
3
+
4
+ class ExecutionStateTest < Test::Unit::TestCase
5
+ test "initialized correctly" do
6
+ assert_nothing_raised { ActionInteractor::ExecutionState.new }
7
+ end
8
+
9
+ test "initial state is :initial" do
10
+ state = ActionInteractor::ExecutionState.new
11
+ assert_equal(state.state, :initial)
12
+ end
13
+
14
+ test "default states are :initial, :processing, :successful and :failure" do
15
+ state = ActionInteractor::ExecutionState.new
16
+ assert_equal(state.states, [:initial, :processing, :successful, :failure, :aborted])
17
+ end
18
+
19
+ test "default transitions are defined correctly" do
20
+ state = ActionInteractor::ExecutionState.new
21
+ assert_equal(
22
+ state.transitions,
23
+ {
24
+ initial: [:processing],
25
+ processing: [:initial],
26
+ successful: [:initial, :processing],
27
+ failure: [:initial, :processing],
28
+ aborted: [:initial, :processing],
29
+ }
30
+ )
31
+ end
32
+
33
+ test "able to change state from :initial to :processing" do
34
+ state = ActionInteractor::ExecutionState.new
35
+ assert_equal(state.initial?, true)
36
+ state.processing!
37
+ assert_equal(state.processing?, true)
38
+ end
39
+
40
+ test "able to change state from :processing to :successful" do
41
+ state = ActionInteractor::ExecutionState.new(:processing)
42
+ state.successful!
43
+ assert_equal(state.successful?, true)
44
+ end
45
+
46
+ test "able to change state from :initial to :failure" do
47
+ state = ActionInteractor::ExecutionState.new(:initial)
48
+ state.failure!
49
+ assert_equal(state.failure?, true)
50
+ end
51
+
52
+ test "raise TransitionError when try to change state from :successful to :failure" do
53
+ state = ActionInteractor::ExecutionState.new(:successful)
54
+ assert_equal(state.valid_transition?(:failure), false)
55
+ error = assert_raises ActionInteractor::ExecutionState::TransitionError do
56
+ state.failure!
57
+ end
58
+ assert_equal("Could not change state :failure from :successful", error.message)
59
+ end
60
+ end
@@ -13,10 +13,10 @@ class RegistrationInteractor < ActionInteractor::Base
13
13
  def execute
14
14
  unless payload[:name]
15
15
  errors.add(:name, "can't be blank.")
16
- return fail!
16
+ return failure!
17
17
  end
18
18
  results.add(:user, User.new(name: payload[:name]))
19
- success!
19
+ successful!
20
20
  end
21
21
  end
22
22
 
@@ -24,10 +24,10 @@ class NotificationInteractor < ActionInteractor::Base
24
24
  def execute
25
25
  errors.add(:name, "can't be blank.") unless payload[:name]
26
26
  errors.add(:email, "can't be blank.") unless payload[:email]
27
- return fail! if errors.any?
27
+ return failure! if errors.any?
28
28
  results.add(:name, payload[:name])
29
29
  results.add(:email, payload[:email])
30
- success!
30
+ successful!
31
31
  end
32
32
  end
33
33
 
@@ -38,6 +38,18 @@ class InheritanceTest < Test::Unit::TestCase
38
38
  assert_nothing_raised { RegistrationInteractor.execute(payload) }
39
39
  end
40
40
 
41
+ test "returns 'registration_interactor' as default interactor name" do
42
+ payload = { name: 'John'}
43
+ interactor = RegistrationInteractor.new(payload)
44
+ assert_equal('registration_interactor', interactor.interactor_name)
45
+ end
46
+
47
+ test "returns 'registration' as specified interactor name" do
48
+ payload = { name: 'John', interactor_name: 'registration' }
49
+ interactor = RegistrationInteractor.new(payload)
50
+ assert_equal('registration', interactor.interactor_name)
51
+ end
52
+
41
53
  test ".execute returns an RegistrationInteractor instance" do
42
54
  payload = { name: 'John'}
43
55
  interactor = RegistrationInteractor.execute(payload)
@@ -57,10 +69,10 @@ class InheritanceTest < Test::Unit::TestCase
57
69
  assert_equal(user.name, 'John')
58
70
  end
59
71
 
60
- test "#success? is true" do
72
+ test "#successful? is true" do
61
73
  payload = { name: 'John'}
62
74
  interactor = RegistrationInteractor.execute(payload)
63
- assert interactor.success?
75
+ assert interactor.successful?
64
76
  end
65
77
 
66
78
  test "#finished? is true" do
@@ -122,7 +134,7 @@ class InheritanceTest < Test::Unit::TestCase
122
134
  test "if only the name is given, the registration is successful but the notification is not." do
123
135
  payload = { name: 'Taro' }
124
136
  registration = RegistrationInteractor.execute(payload)
125
- assert registration.success?
137
+ assert registration.successful?
126
138
  assert registration.errors.empty?
127
139
  notification = NotificationInteractor.execute(payload)
128
140
  assert notification.failure?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actioninteractor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.1
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Hashimoto
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-02 00:00:00.000000000 Z
11
+ date: 2021-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -62,11 +62,16 @@ files:
62
62
  - Rakefile
63
63
  - actioninteractor.gemspec
64
64
  - lib/action_interactor/base.rb
65
+ - lib/action_interactor/composite.rb
65
66
  - lib/action_interactor/errors.rb
67
+ - lib/action_interactor/execution_state.rb
66
68
  - lib/action_interactor/results.rb
69
+ - lib/action_interactor/state.rb
67
70
  - lib/actioninteractor.rb
68
71
  - test/base_test.rb
72
+ - test/composite_test.rb
69
73
  - test/errors_test.rb
74
+ - test/execution_state_test.rb
70
75
  - test/inheritance_test.rb
71
76
  - test/results_test.rb
72
77
  homepage: https://github.com/ryohashimoto/lightrails
@@ -88,13 +93,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
93
  - !ruby/object:Gem::Version
89
94
  version: 1.8.11
90
95
  requirements: []
91
- rubygems_version: 3.0.3
96
+ rubygems_version: 3.1.4
92
97
  signing_key:
93
98
  specification_version: 4
94
99
  summary: Action Interactor provides a simple interface for performing operations (part
95
100
  of Lightrails).
96
101
  test_files:
97
102
  - test/base_test.rb
103
+ - test/composite_test.rb
98
104
  - test/errors_test.rb
105
+ - test/execution_state_test.rb
99
106
  - test/inheritance_test.rb
100
107
  - test/results_test.rb