From c36f52b79811b46fd2f267fc25b326b9ecc78dd4 Mon Sep 17 00:00:00 2001 From: Steve Richert Date: Fri, 28 Apr 2017 08:45:17 -0400 Subject: [PATCH] Back out the concept of magically assigning positional call arguments There were edge cases that weren't yet covered by the specs that would fail for combinations of positional and keyword arguments depending on what's available in the context. For example: class MyInteractor include Interactor def call(a, b: "bears") context.output = [a, b] end end MyInteractor.call(b: "beets").output # => [{ b: "beets" }, "bears"] Plus, this simplifies the interface by giving the developer one choice rather than multiple competing choices that achieve the same thing. --- lib/interactor.rb | 18 ++--- spec/interactor_spec.rb | 160 ++-------------------------------------- 2 files changed, 14 insertions(+), 164 deletions(-) diff --git a/lib/interactor.rb b/lib/interactor.rb index 8cf246f..0109494 100644 --- a/lib/interactor.rb +++ b/lib/interactor.rb @@ -166,24 +166,22 @@ def rollback private - # Internal: Determine what arguments (if any) should be passed to the "call" - # instance method when invoking an Interactor. The "call" instance method may - # accept any combination of positional and keyword arguments. This method - # will extract values from the context in order to populate those arguments - # based on their names. + # Internal: Determine what keyword arguments (if any) should be passed to the + # "call" instance method when invoking an Interactor. The "call" instance + # method may accept any number of keyword arguments. This method will extract + # values from the context in order to populate those arguments based on their + # names. # # Returns an Array of arguments to be applied as an argument list. - def arguments_for_call # rubocop:disable Metrics/MethodLength + def arguments_for_call positional_arguments = [] keyword_arguments = {} method(:call).parameters.each do |(type, name)| + next unless type == :keyreq || type == :key next unless context.include?(name) - case type - when :req, :opt then positional_arguments << context[name] - when :keyreq, :key then keyword_arguments[name] = context[name] - end + keyword_arguments[name] = context[name] end positional_arguments << keyword_arguments if keyword_arguments.any? diff --git a/spec/interactor_spec.rb b/spec/interactor_spec.rb index 2153bfc..d85b8db 100644 --- a/spec/interactor_spec.rb +++ b/spec/interactor_spec.rb @@ -4,54 +4,6 @@ describe "#call" do let(:interactor) { Class.new.send(:include, described_class) } - context "positional arguments" do - it "accepts required positional arguments" do - interactor.class_eval do - def call(foo) - context.output = foo - end - end - - result = interactor.call(foo: "baz", hello: "world") - - expect(result.output).to eq("baz") - end - - it "accepts optional positional arguments" do - interactor.class_eval do - def call(foo = "bar") - context.output = foo - end - end - - result = interactor.call(foo: "baz", hello: "world") - - expect(result.output).to eq("baz") - end - - it "assigns absent positional arguments" do - interactor.class_eval do - def call(foo = "bar") - context.output = foo - end - end - - result = interactor.call(hello: "world") - - expect(result.output).to eq("bar") - end - - it "raises an error for missing positional arguments" do - interactor.class_eval do - def call(foo) - context.output = foo - end - end - - expect { interactor.call(hello: "world") }.to raise_error(ArgumentError) - end - end - context "keyword arguments" do it "accepts required keyword arguments" do interactor.class_eval do @@ -60,9 +12,9 @@ def call(foo:) end end - result = interactor.call(foo: "baz", hello: "world") + result = interactor.call(foo: "bar", hello: "world") - expect(result.output).to eq("baz") + expect(result.output).to eq("bar") end it "accepts optional keyword arguments" do @@ -98,115 +50,15 @@ def call(foo:) expect { interactor.call(hello: "world") }.to raise_error(ArgumentError) end - end - - context "combination arguments" do - it "accepts required positional with required keyword arguments" do - interactor.class_eval do - def call(foo, hello:) - context.output = [foo, hello] - end - end - - result = interactor.call(foo: "baz", hello: "world") - - expect(result.output).to eq(["baz", "world"]) - end - - it "accepts required positional with optional keyword arguments" do - interactor.class_eval do - def call(foo, hello: "there") - context.output = [foo, hello] - end - end - - result = interactor.call(foo: "baz", hello: "world") - - expect(result.output).to eq(["baz", "world"]) - end - - it "accepts required positional and assigns absent keyword arguments" do - interactor.class_eval do - def call(foo, hello: "there") - context.output = [foo, hello] - end - end - - result = interactor.call(foo: "baz") - - expect(result.output).to eq(["baz", "there"]) - end - - it "accepts optional positional with required keyword arguments" do - interactor.class_eval do - def call(foo = "bar", hello:) - context.output = [foo, hello] - end - end - - result = interactor.call(foo: "baz", hello: "world") - - expect(result.output).to eq(["baz", "world"]) - end - it "accepts optional positional with optional keyword arguments" do + it "raises an error for call definitions with non-keyword arguments" do interactor.class_eval do - def call(foo = "bar", hello: "there") - context.output = [foo, hello] - end - end - - result = interactor.call(foo: "baz", hello: "world") - - expect(result.output).to eq(["baz", "world"]) - end - - it "accepts optional positional and assigns absent keyword arguments" do - interactor.class_eval do - def call(foo = "bar", hello: "there") - context.output = [foo, hello] - end - end - - result = interactor.call(foo: "baz") - - expect(result.output).to eq(["baz", "there"]) - end - - it "assigns absent positional and accepts required keyword arguments" do - interactor.class_eval do - def call(foo = "bar", hello:) - context.output = [foo, hello] - end - end - - result = interactor.call(hello: "world") - - expect(result.output).to eq(["bar", "world"]) - end - - it "assigns absent positional and accepts optional keyword arguments" do - interactor.class_eval do - def call(foo = "bar", hello: "there") - context.output = [foo, hello] - end - end - - result = interactor.call(hello: "world") - - expect(result.output).to eq(["bar", "world"]) - end - - it "assigns absent positional and absent keyword arguments" do - interactor.class_eval do - def call(foo = "bar", hello: "there") - context.output = [foo, hello] + def call(foo) + context.output = foo end end - result = interactor.call - - expect(result.output).to eq(["bar", "there"]) + expect { interactor.call(foo: "bar") }.to raise_error(ArgumentError) end end end