Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update after_commit behavior #33

Merged
merged 2 commits into from
Feb 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## [0.9.0] - 2019-04-01
### Changed
- Changed behavior for `:after_commit` step wrapper, on `:sequel_models` plugin, to capture current state and reuse it later when executing.

### Fixed
- Allow invoking `call` directly on an operation class even if the `:responder` plugin is not loaded.

## [0.8.0] - 2018-10-01
### Changed
- Added support for `dry-validation` 0.12.x
Expand All @@ -13,7 +20,7 @@
- Allow `authorization` block to take multiple parameters on `simple_auth` plugin.

## [0.6.2] - 2018-05-19
### Fixes
### Fixed
- Allow `:error_message` option for `sequel_models` plugin to propagate down inherited classes

## [0.6.1] - 2018-03-16
Expand Down
16 changes: 10 additions & 6 deletions lib/pathway.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,20 @@ module Plugins
module Base
module ClassMethods
attr_accessor :result_key
alias :result_at :result_key=

def process(&bl)
dsl = self::DSL
define_method(:call) do |input|
dsl.new(self, input).run(&bl).then(&:result)
dsl.new(State.new(self, input: input), self)
.run(&bl)
.then(&:result)
end
end

alias :result_at :result_key=
def call(ctx, *params)
new(ctx).call(*params)
end

def inherited(subclass)
super
Expand Down Expand Up @@ -120,9 +125,8 @@ def self.apply(klass)
end

module DSLMethods
def initialize(operation, input)
@result = wrap(State.new(operation, input: input))
@operation = operation
def initialize(state, operation)
@result, @operation = wrap(state), operation
end

def run(&bl)
Expand Down Expand Up @@ -155,7 +159,7 @@ def map(callable)

def around(wrapper, &steps)
@result.then do |state|
seq = -> { @result = dup.run(&steps) }
seq = -> (dsl = self) { @result = dsl.run(&steps) }
_callable(wrapper).call(seq, state)
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/pathway/plugins/responder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Plugins
module Responder
module ClassMethods
def call(ctx, *params, &bl)
result = new(ctx).call(*params)
result = super(ctx, *params)
block_given? ? Responder.respond(result, &bl) : result
end
end
Expand Down
6 changes: 4 additions & 2 deletions lib/pathway/plugins/sequel_models.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ def transaction(&bl)
end

def after_commit(&bl)
around(-> steps, _ {
around(-> steps, state {
dsl = self.class::DSL.new(State.new(self, state.to_h.dup), self)

db.after_commit do
steps.call
steps.call(dsl)
end
}, &bl)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/pathway/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Pathway
VERSION = '0.8.0'
VERSION = '0.9.0'
end
16 changes: 13 additions & 3 deletions spec/plugins/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
module Pathway
module Plugins
describe Base do

class OperationWithSteps < Operation
context :validator, :back_end, :notifier, :cond
result_at :result_value
Expand Down Expand Up @@ -61,9 +62,8 @@ def notify(state)
let(:notifier) { double }
let(:cond) { double }

subject(:operation) do
OperationWithSteps.new(validator: validator, back_end: back_end, notifier: notifier, cond: cond)
end
let(:ctx) { { validator: validator, back_end: back_end, notifier: notifier, cond: cond } }
subject(:operation) { OperationWithSteps.new(ctx) }

before do
allow(validator).to receive(:call) do |input:, **|
Expand Down Expand Up @@ -99,6 +99,16 @@ def notify(state)
end
end

describe ".call" do
let(:result) { OperationWithSteps.call(ctx, input) }
it "creates a new instance an invokes the 'call' method on it" do
expect(back_end).to receive(:call).and_return(:SOME_RETURN_VALUE)

expect(result).to be_a_success
expect(result.value).to eq(:SOME_RETURN_VALUE)
end
end

describe "#set" do
it "defines an updating step which sets the result key if no key is specified" do
expect(back_end).to receive(:call).and_return(:SOME_VALUE)
Expand Down
47 changes: 43 additions & 4 deletions spec/plugins/sequel_models_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,61 @@ class MyOperation < Operation

model MyModel, search_by: :email

process do
step :fetch_model
end
end

class MailerOperation < MyOperation
process do
transaction do
step :fetch_model
after_commit do
step :send_emails
end
end
step :as_hash
end

def as_hash(state)
state[:my_model] = { model: state[:my_model] }
end

def send_emails(my_model:,**)
@mailer.send_emails(my_model) if @mailer
end
end

class SubOperation < MyOperation; end
class ChainedOperation < MyOperation
result_at :result

let(:mailer) { double.tap { |d| allow(d).to receive(:send_emails) } }
let(:operation) { MyOperation.new(mailer: mailer) }
process do
transaction do
set :chain_operation, to: :result
end
end

def chain_operation(input:,**)
MailerOperation.call(context, input)
end
end

class SubOperation < MyOperation; end

describe 'DSL' do
let(:result) { operation.call(params) }
let(:params) { { email: 'asd@fgh.net' } }
let(:model) { double }

let(:operation) { MailerOperation.new(mailer: mailer) }
let(:mailer) { double.tap { |d| allow(d).to receive(:send_emails) } }

describe '#transaction' do
it 'returns the result state provided by the inner transaction when successful' do
allow(MyModel).to receive(:first).with(params).and_return(model)

expect(result).to be_a_success
expect(result.value).to eq(model)
expect(result.value).to eq(model: model)
end

it "returns the error state provided by the inner transaction when there's a failure" do
Expand All @@ -71,9 +96,23 @@ class SubOperation < MyOperation; end
expect(mailer).to_not receive(:send_emails)
expect(result).to be_a_failure
end

context 'when the state after if changed after the callback is set' do
let(:operation) { ChainedOperation.new(mailer: mailer) }

it 'ignores state changes that took place on the remaining steps' do
allow(MyModel).to receive(:first).with(params).and_return(model)
expect(mailer).to receive(:send_emails).with(model)

expect(result).to be_a_success
expect(result.value).to eq(model: model)
end
end
end
end

let(:operation) { MyOperation.new }

describe '.model' do
it "sets the 'result_key' using the model class name" do
expect(operation.result_key).to eq(:my_model)
Expand Down