diff --git a/Gemfile b/Gemfile index 8b1f642..952fad1 100644 --- a/Gemfile +++ b/Gemfile @@ -17,7 +17,7 @@ end group :development, :test do gem "bundler" - gem "rake", "~> 11.2", ">= 11.2.2" + gem "rake", ">= 12.3.3" gem "rspec" gem "yard" end diff --git a/lib/dry/transaction/callable.rb b/lib/dry/transaction/callable.rb index 2259214..4150dda 100644 --- a/lib/dry/transaction/callable.rb +++ b/lib/dry/transaction/callable.rb @@ -14,8 +14,7 @@ def self.[](callable) end end - attr_reader :operation - attr_reader :arity + attr_reader :operation, :arity def initialize(operation) @operation = case operation @@ -49,7 +48,10 @@ def call(*args, &block) # In this case, it's better to leave the object as it's existing type, rather than implicitly # convert it in to a hash with the double-splat (**) operator. def ruby_27_last_arg_hash?(args) - args.last.instance_of?(Hash) && Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0") + kwargs = args.last + kwargs.instance_of?(Hash) && + !kwargs.empty? && + Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0") end end end diff --git a/spec/integration/transaction_spec.rb b/spec/integration/transaction_spec.rb index c994182..83ce4b1 100644 --- a/spec/integration/transaction_spec.rb +++ b/spec/integration/transaction_spec.rb @@ -9,7 +9,7 @@ before do container.instance_exec do - register :process, -> input { { name: input["name"], email: input["email"] } } + register :process, -> input { {name: input["name"], email: input["email"]} } register :verify, -> input { Success(input) } register :validate, -> input { input[:email].nil? ? raise(Test::NotValidError, "email required") : input } register :persist, -> input { self[:database] << input and true } @@ -26,7 +26,7 @@ tee :persist, with: :persist end.new(**dependencies) } - let(:input) { { "name" => "Jane", "email" => "jane@doe.com" } } + let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} } it "calls the operations" do transaction.call(input) @@ -68,7 +68,7 @@ before do class Test::ContainerNames extend Dry::Container::Mixin - register :process_step, -> input { { name: input["name"], email: input["email"] } } + register :process_step, -> input { {name: input["name"], email: input["email"]} } register :verify_step, -> input { Dry::Monads::Success(input) } register :persist_step, -> input { Test::DB << input and true } end @@ -101,7 +101,7 @@ class Test::ContainerNames } let(:dependencies) { - { verify_step: -> input { Success(input.merge(foo: :bar)) } } + {verify_step: -> input { Success(input.merge(foo: :bar)) }} } it "calls injected operations" do @@ -176,7 +176,7 @@ def process(input) end let(:dependencies) do - { process: -> input { Failure(input) } } + {process: -> input { Failure(input) }} end # FIXME: needs a better description @@ -201,14 +201,14 @@ def process(input) let(:dependencies) do { - process: -> input { { name: input["name"], email: input["email"] } }, + process: -> input { {name: input["name"], email: input["email"]} }, verify: -> input { Success(input) }, validate: -> input { input[:email].nil? ? raise(Test::NotValidError, "email required") : input }, persist: -> input { database << input and true } } end - let(:input) { { "name" => "Jane", "email" => "jane@doe.com" } } + let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} } it "calls the injected operations" do transaction.call(input) @@ -230,13 +230,13 @@ def process(input) let(:dependencies) do { - process: -> input { { name: input["name"], email: input["email"] } }, + process: -> input { {name: input["name"], email: input["email"]} }, verify: -> input { Success(input) }, validate: -> input { input[:email].nil? ? raise(Test::NotValidError, "email required") : input } } end - let(:input) { { "name" => "Jane", "email" => "jane@doe.com" } } + let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} } it "raises an exception" do expect { transaction }.to raise_error(Dry::Transaction::MissingStepError) @@ -312,7 +312,7 @@ def persist(input) it "executes succesfully" do transaction_class.new.call("name" => "Jane", "email" => "jane@doe.com") - expect(database).to include([["name", "Jane"], ["email", "jane@doe.com"]]) + expect(database).to include([%w[name Jane], %w[email jane@doe.com]]) end it "allows replacement steps to be injected" do @@ -333,7 +333,7 @@ def persist(input) tee :persist, with: :persist end.new(**dependencies) } - let(:input) { { "name" => "Jane" } } + let(:input) { {"name" => "Jane"} } it "does not run subsequent operations" do transaction.call(input) @@ -382,7 +382,7 @@ def persist(input) transaction.call(input) do |m| m.success {} - m.failure :some_other_step do |value| + m.failure :some_other_step do |_value| results << "Some other step failure" end @@ -396,14 +396,14 @@ def persist(input) end context "failed in a raw step" do - let(:input) { { "name" => "Jane", "email" => "jane@doe.com" } } + let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} } before do class Test::ContainerRaw extend Dry::Container::Mixin extend Dry::Monads::Result::Mixin - register :process_step, -> input { { name: input["name"], email: input["email"] } } - register :verify_step, -> input { Failure("raw failure") } + register :process_step, -> input { {name: input["name"], email: input["email"]} } + register :verify_step, -> _input { Failure("raw failure") } register :persist_step, -> input { self[:database] << input and true } end end @@ -445,7 +445,7 @@ class Test::ContainerRaw end context "non-confirming raw step result" do - let(:input) { { "name" => "Jane", "email" => "jane@doe.com" } } + let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} } let(:transaction) { Class.new do @@ -459,8 +459,8 @@ class Test::ContainerRaw before do class Test::ContainerRaw extend Dry::Container::Mixin - register :process, -> input { { name: input["name"], email: input["email"] } } - register :verify, -> input { "failure" } + register :process, -> input { {name: input["name"], email: input["email"]} } + register :verify, -> _input { "failure" } register :persist, -> input { Test::DB << input and true } end end @@ -471,14 +471,14 @@ class Test::ContainerRaw end context "keyword arguments" do - let(:input) { { name: "jane", age: 20 } } + let(:input) { {name: "jane", age: 20} } let(:upcaser) do Class.new { include Dry::Monads::Result::Mixin def call(name: "John", **rest) - Success({ name: name[0].upcase + name[1..-1], **rest }) + Success({name: name[0].upcase + name[1..-1], **rest}) end }.new end @@ -532,6 +532,10 @@ def [](key) expect(transaction.call(input)).to be_a Dry::Monads::Result::Success end + it "returns a failure without deprecation warning" do + expect(transaction.call({})).to be_a Dry::Monads::Result::Failure + end + it "wraps the result of the final operation" do expect(transaction.call(input).value![:name]).to eq("Jane") end