flow 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # A callback driven approach to wrap some business logic within a transaction.
3
+ # A callback driven approach to wrap business logic within database transaction.
4
4
  module TransactionWrapper
5
5
  extend ActiveSupport::Concern
6
6
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "custom_matchers/define_argument"
4
+ require_relative "custom_matchers/define_attribute"
4
5
  require_relative "custom_matchers/define_option"
5
6
  require_relative "custom_matchers/use_operations"
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # RSpec matcher for flow state attributes.
4
+ #
5
+ # Usage:
6
+ #
7
+ # RSpec.describe ExampleState, type: :state do
8
+ # subject { described_class.new(**input) }
9
+ #
10
+ # let(:input) { {} }
11
+ #
12
+ # it { is_expected.to define_attribute :foo }
13
+ # end
14
+
15
+ RSpec::Matchers.define :define_attribute do |attribute|
16
+ match { |state| expect(state.class._attributes).to include attribute }
17
+ description { "has attribute #{attribute}" }
18
+ failure_message { |state| "expected #{state.class.name}# to have attribute #{attribute}" }
19
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Operations are an ordered list of the behaviors which should are executed with and possibly change the Flow's state.
3
+ # Operations are an ordered list of the behaviors which are executed with (and possibly change) the Flow's state.
4
4
  module Flow
5
5
  module Operations
6
6
  extend ActiveSupport::Concern
@@ -10,7 +10,7 @@ module Flow
10
10
  end
11
11
 
12
12
  def triggered?
13
- executed_operations.any?
13
+ executed_operations.any? || failed_operation?
14
14
  end
15
15
 
16
16
  def success?
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # It's best practice to have Flows in which nothing should be done unless everything is successful to use a transaction.
3
+ # Flows where no operation should be persisted unless all are successful should use a transaction.
4
4
  module Flow
5
5
  module Transactions
6
6
  extend ActiveSupport::Concern
@@ -12,7 +12,7 @@ require_relative "flow/status"
12
12
  require_relative "flow/transactions"
13
13
  require_relative "flow/trigger"
14
14
 
15
- # A flow is a collection of procedurally executed operations sharing a common state.
15
+ # A **Flow** is a collection of procedurally executed **Operations** sharing a common **State**.
16
16
  class FlowBase
17
17
  include ShortCircuIt
18
18
  include Technologic
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Accepts input representing the state.
3
+ # Operations take a state as input.
4
4
  module Operation
5
5
  module Core
6
6
  extend ActiveSupport::Concern
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # An unhandled error during execution is exceptional, and handlers unable to rescue an error cause a failure instead.
3
+ # When an exception is raised during during execution, but a handler can rescue, it causes a failure instead.
4
4
  module Operation
5
5
  module ErrorHandler
6
6
  extend ActiveSupport::Concern
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Operation
4
+ module Errors
5
+ class AlreadyExecuted < StandardError; end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Operation
4
+ module Errors
5
+ class AlreadyRewound < StandardError; end
6
+ end
7
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Operations *must* define the `#behavior` that occurs when `#execute` is called.
3
+ # Operations define a `#behavior` that occurs when `#execute` is called.
4
4
  module Operation
5
5
  module Execute
6
6
  extend ActiveSupport::Concern
@@ -11,6 +11,7 @@ module Operation
11
11
  attr_reader :operation_failure
12
12
 
13
13
  set_callback :execute, :around, ->(_, block) { surveil(:execute) { block.call } }
14
+ set_callback :execute, :before, -> { raise Operation::Errors::AlreadyExecuted }, if: :executed?
14
15
  end
15
16
 
16
17
  def execute!
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # When `#execute` is unsuccessful, expected problems are *failures* and unexpected problems are *Exceptions*.
3
+ # When `#execute` is unsuccessful, expected problems are **failures** and unexpected problems are **Exceptions**.
4
4
  module Operation
5
5
  module Failures
6
6
  extend ActiveSupport::Concern
@@ -7,6 +7,7 @@ module Operation
7
7
 
8
8
  included do
9
9
  set_callback :rewind, :around, ->(_, block) { surveil(:rewind) { block.call } }
10
+ set_callback :rewind, :before, -> { raise Operation::Errors::AlreadyRewound }, if: :rewound?
10
11
  end
11
12
 
12
13
  def rewind
@@ -7,16 +7,21 @@ module Operation
7
7
 
8
8
  included do
9
9
  set_callback(:execute, :before) { self.was_executed = true }
10
+ set_callback(:rewind, :before) { self.was_rewound = true }
10
11
 
11
12
  private
12
13
 
13
- attr_accessor :was_executed
14
+ attr_accessor :was_executed, :was_rewound
14
15
  end
15
16
 
16
17
  def executed?
17
18
  was_executed.present?
18
19
  end
19
20
 
21
+ def rewound?
22
+ was_rewound.present?
23
+ end
24
+
20
25
  def failed?
21
26
  operation_failure.present?
22
27
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # It's best practice to have Operations which modify several persisted objects to use a transaction.
3
+ # Operations which modify several persisted objects together should use a transaction.
4
4
  module Operation
5
5
  module Transactions
6
6
  extend ActiveSupport::Concern
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "flow/errors/state_invalid"
3
+ require_relative "operation/errors/already_executed"
4
+ require_relative "operation/errors/already_rewound"
4
5
 
5
6
  require_relative "operation/callbacks"
6
7
  require_relative "operation/core"
@@ -11,7 +12,7 @@ require_relative "operation/rewind"
11
12
  require_relative "operation/status"
12
13
  require_relative "operation/transactions"
13
14
 
14
- # Operations are service objects which are executed with a state.
15
+ # An **Operation** is a service object which is executed with a **State**.
15
16
  class OperationBase
16
17
  include ShortCircuIt
17
18
  include Technologic
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Defines the immutable structure by defining attribute accessors for the state data.
3
+ # A state's attributes provide accessors to the input data it was initialized with.
4
4
  module State
5
5
  module Attributes
6
6
  extend ActiveSupport::Concern
@@ -25,6 +25,7 @@ module State
25
25
  attr_accessor attribute
26
26
  define_attribute_methods attribute
27
27
  end
28
+ alias_method :attribute, :define_attribute
28
29
  end
29
30
  end
30
31
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Accepts input representing the arguments and options which define the initial state.
3
+ # A state accepts input represented by arguments and options which initialize it.
4
4
  module State
5
5
  module Core
6
6
  extend ActiveSupport::Concern
@@ -7,7 +7,7 @@ require_relative "state/options"
7
7
  require_relative "state/core"
8
8
  require_relative "state/string"
9
9
 
10
- # A flow state is the immutable structure of relevant data.
10
+ # A **State** is an aggregation of input and derived data.
11
11
  class StateBase
12
12
  include ShortCircuIt
13
13
  include Technologic
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Flow
4
- VERSION = "0.9.2"
4
+ VERSION = "0.9.3"
5
5
  end
@@ -5,4 +5,5 @@ class <%= class_name %>State < ApplicationState
5
5
  # option :optional_input
6
6
  # option :option_with_default, default: :default_static_value
7
7
  # option(:option_with_default_from_block) { required_input.default_dynamic_value }
8
+ # attribute :some_runtime_value
8
9
  end
@@ -11,4 +11,18 @@ RSpec.describe <%= class_name %>Flow, type: :flow do
11
11
 
12
12
  it { is_expected.to inherit_from ApplicationFlow }
13
13
  # it { is_expected.to use_operations ExampleOperation }
14
+
15
+ describe "#trigger" do
16
+ subject(:trigger) { flow.trigger! }
17
+
18
+ pending "describe the effects of a successful `Flow#flux` (or delete) #{__FILE__}"
19
+ end
20
+
21
+ describe "#revert" do
22
+ before { flow.trigger! }
23
+
24
+ subject(:revert) { flow.revert }
25
+
26
+ pending "describe the effects of a successful `Flow#ebb` (or delete) #{__FILE__}"
27
+ end
14
28
  end
@@ -18,7 +18,17 @@ RSpec.describe <%= class_name %>, type: :operation do
18
18
 
19
19
  it { is_expected.to inherit_from ApplicationOperation }
20
20
 
21
- describe "#execute" do
22
- pending "add some examples to (or delete) #{__FILE__}"
21
+ describe "#execute!" do
22
+ subject(:execute!) { operation.execute! }
23
+
24
+ pending "describe `Operation#behavior` (or delete) #{__FILE__}"
25
+ end
26
+
27
+ describe "#rewind" do
28
+ before { operation.execute! }
29
+
30
+ subject(:execute!) { operation.rewind }
31
+
32
+ pending "describe `Operation#undo` (or delete) #{__FILE__}"
23
33
  end
24
34
  end
@@ -15,4 +15,5 @@ RSpec.describe <%= class_name %>State, type: :state do
15
15
  # it { is_expected.to define_option(:foo).with_default_value(:bar) }
16
16
  # it { is_expected.to define_option(:foo).with_default_value_block }
17
17
  # it { is_expected.to validate_presence_of ... }
18
+ # it { is_expected.to define_attribute :foo }
18
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Garside
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-03 00:00:00.000000000 Z
11
+ date: 2019-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -234,6 +234,7 @@ files:
234
234
  - lib/flow/concerns/transaction_wrapper.rb
235
235
  - lib/flow/custom_matchers.rb
236
236
  - lib/flow/custom_matchers/define_argument.rb
237
+ - lib/flow/custom_matchers/define_attribute.rb
237
238
  - lib/flow/custom_matchers/define_option.rb
238
239
  - lib/flow/custom_matchers/use_operations.rb
239
240
  - lib/flow/flow/callbacks.rb
@@ -250,6 +251,8 @@ files:
250
251
  - lib/flow/operation/callbacks.rb
251
252
  - lib/flow/operation/core.rb
252
253
  - lib/flow/operation/error_handler.rb
254
+ - lib/flow/operation/errors/already_executed.rb
255
+ - lib/flow/operation/errors/already_rewound.rb
253
256
  - lib/flow/operation/execute.rb
254
257
  - lib/flow/operation/failures.rb
255
258
  - lib/flow/operation/rewind.rb