Skip to content
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [1.1.0] - 2025-05-25
### Added
- Added `:if` and `:unless` options for `:transaction` and `:after_commit` methods at `:sequel_models` plugin

## [1.0.0] - 2025-05-19
### Changed
- Removed support for `Ruby` versions older than 3.0
Expand Down
2 changes: 1 addition & 1 deletion lib/pathway.rb
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def wrap(obj) = Result.result(obj)

def _callable(callable)
case callable
when Proc
when Proc # unless (callable.binding rescue nil)&.receiver == @operation
->(*args, **kwargs) { @operation.instance_exec(*args, **kwargs, &callable) }
when Symbol
->(*args, **kwargs) { @operation.send(callable, *args, **kwargs) }
Expand Down
36 changes: 28 additions & 8 deletions lib/pathway/plugins/sequel_models.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ module Pathway
module Plugins
module SequelModels
module DSLMethods
def transaction(step_name = nil, &dsl_bl)
raise 'must provide a step or a block but not both' if !step_name.nil? == block_given?
def transaction(step_name = nil, if: nil, unless: nil, &)
cond, dsl_bl = _transact_opts(step_name, *%i[if unless].map { binding.local_variable_get(_1) }, &)

if step_name
transaction { step step_name }
if cond
if_true(cond) { transaction(&dsl_bl) }
else
around(->(runner, _) {
db.transaction(savepoint: true) do
Expand All @@ -20,11 +20,11 @@ def transaction(step_name = nil, &dsl_bl)
end
end

def after_commit(step_name = nil, &dsl_bl)
raise 'must provide a step or a block but not both' if !step_name.nil? == block_given?
def after_commit(step_name = nil, if: nil, unless: nil, &)
cond, dsl_bl = _transact_opts(step_name, *%i[if unless].map { binding.local_variable_get(_1) }, &)

if step_name
after_commit { step step_name }
if cond
if_true(cond) { after_commit(&dsl_bl) }
else
around(->(runner, state) {
dsl_copy = self.class::DSL.new(State.new(self, state.to_h.dup), self)
Expand All @@ -35,6 +35,26 @@ def after_commit(step_name = nil, &dsl_bl)
}, &dsl_bl)
end
end

private

def _transact_opts(step_name, if_cond, unless_cond, &bl)
dsl = if !step_name.nil? == block_given?
raise ArgumentError, 'must provide either a step or a block but not both'
else
bl || proc { step step_name }
end

cond = if if_cond && unless_cond
raise ArgumentError, 'options :if and :unless are mutually exclusive'
elsif if_cond
if_cond
elsif unless_cond
_callable(unless_cond) >> :!.to_proc
end

return cond, dsl
end
end

module ClassMethods
Expand Down
17 changes: 11 additions & 6 deletions spec/plugins/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ def notify(state)
allow(notifier).to receive(:call)
end

let(:input) { { foo: 'FOO' } }
let(:result) { operation.call(input) }
let(:valid_input) { { foo: 'FOO' } }

describe ".process" do
it "defines a 'call' method wich saves operation argument into the :input key" do
Expand All @@ -99,16 +98,19 @@ def notify(state)
operation.call(:my_input_test_value)
end

let(:result) { operation.call(valid_input) }

it "defines a 'call' method which returns a value using the key specified by 'result_at'" do
expect(back_end).to receive(:call).and_return(:SOME_RETURN_VALUE)

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

describe ".call" do
let(:result) { OperationWithSteps.call(ctx, input) }
let(:result) { OperationWithSteps.call(ctx, valid_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)

Expand All @@ -133,7 +135,7 @@ def notify(state)
expect(state).to_not be_equal(old_state)
end

operation.call(input)
operation.call(valid_input)
end
end

Expand All @@ -146,7 +148,7 @@ def notify(state)
expect(state.to_hash).to include(result_value: :SOME_VALUE)
end

operation.call(input)
operation.call(valid_input)
end

it "defines an updating step which sets the specified key" do
Expand All @@ -160,10 +162,13 @@ def notify(state)
expect(state.to_hash).to include(aux_value: :RETURN_VALUE)
end

operation.call(input)
operation.call(valid_input)
end
end


let(:result) { operation.call(valid_input) }

describe "#step" do
it "defines an non updating step" do
expect(notifier).to receive(:call) { { result_value: 0 } }
Expand Down
11 changes: 3 additions & 8 deletions spec/plugins/dry_validation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -201,30 +201,25 @@ class OperationWithAutoWire < Operation
let(:ctx) { { user: double("User", role: role), repository: repository } }
let(:role) { :root }
let(:params) { { name: "Paul Smith", email: "psmith@email.com" } }
let(:result) { operation.call(params) }
let(:repository) { double.tap { |repo| allow(repo).to receive(:fetch).and_return(double) } }

context "when calling with valid params" do
it "returns a successful result", :aggregate_failures do
expect(result).to be_a_success
expect(result.value).to_not be_nil
expect(operation).to succeed_on(params).returning(anything)
end
end

context "when finding model fails" do
let(:repository) { double.tap { |repo| allow(repo).to receive(:fetch).and_return(nil) } }
it "returns a a failed result", :aggregate_failures do
expect(result).to be_a_failure
expect(result.error.type).to eq(:not_found)
expect(operation).to fail_on(params).with_type(:not_found)
end
end

context "when calling with invalid params" do
let(:params) { { email: "psmith@email.com" } }
it "returns a failed result", :aggregate_failures do
expect(result).to be_a_failure
expect(result.error.type).to eq(:validation)
expect(result.error.details).to eq(name: ['is missing'])
expect(operation).to fail_on(params).with_details(name: ['is missing'])
end
end

Expand Down
Loading