diff --git a/lib/interactor.rb b/lib/interactor.rb index 2423630..135195f 100644 --- a/lib/interactor.rb +++ b/lib/interactor.rb @@ -75,6 +75,85 @@ def call(context = {}) def call!(context = {}) new(context).tap(&:run!).context end + + # Public: Declare input parameters. This method will simply delegate + # specified methods to the to the context. + # + # input_parameters_list - zero or more Symbol argument names that are + # expected to present at the context. Private instance methods + # with corresponding names will be created. + # + # Examples + # + # class MyInteractor + # include Interactor + # + # parameters :message + # + # def call + # puts message + # end + # end + # + # MyInteractor.call(message: "Hello!") + # # Hello! + # # => # + # + # Returns array of Symbols, representing expected arguments' names + + def parameters(*input_parameters_list) + input_parameters_list.each do |parameter_name| + define_method parameter_name do + context.public_send(parameter_name) + end + private parameter_name + end + end + + # Public: Declare input parameters. This method will simply delegate + # specified methods to the to the context. If any of the specified + # parameters is missing on call, it will fail the context. + # + # input_parameters_list - zero or more Symbol argument names that are + # expected to present at the context. Private instance methods + # with corresponding names will be created. All these arguments + # will be verified to present in the context in before hook. + # + # Examples + # + # class MyInteractor + # include Interactor + # + # parameters! :foo, :bar + # + # def call + # puts "#{foo} #{bar}" + # end + # end + # + # MyInteractor.call(foo: "foo") + # # => # + # + # MyInteractor.call!(foo: "foo") + # # => #> + # + # MyInteractor.call(foo: "foo", bar: "bar") + # # foo bar + # # => # + # + # MyInteractor.call!(foo: "foo", bar: "bar") + # # foo bar + # # => # + + def parameters!(*input_parameters_list) + parameters(*input_parameters_list) + + before do + input_parameters_list.each do |parameter_name| + context.fail! if context.public_send(parameter_name).nil? + end + end + end end # Internal: Initialize an Interactor. diff --git a/spec/support/lint.rb b/spec/support/lint.rb index bfbc97c..764babe 100644 --- a/spec/support/lint.rb +++ b/spec/support/lint.rb @@ -61,6 +61,53 @@ end end + describe ".parameters" do + let(:context) { double(:context, foo: "foo", bar: "bar") } + let(:instance) { interactor.new } + + it "defines instance parameter methods" do + expect(Interactor::Context).to receive(:build) { context } + + interactor.parameters(:foo, :bar) + + expect(instance.private_methods).to include(:foo, :bar) + expect(instance.send(:foo)).to eq "foo" + expect(instance.send(:bar)).to eq "bar" + end + end + + describe ".parameters!" do + it "defines instance parameter methods" do + expect(interactor).to receive(:parameters).with(:foo, :bar).once + + interactor.parameters!(:foo, :bar) + end + + context "with missing parameter" do + let(:context) { double foo: "foo", bar: nil, called!: double } + + it "fails the context" do + expect(Interactor::Context).to receive(:build) { context } + expect(context).to receive(:fail!) + + interactor.parameters!(:foo, :bar) + interactor.call + end + end + + context "with all required parameters specified" do + let(:context) { double foo: "foo", bar: "bar", called!: double } + + it "does not fail the context" do + expect(Interactor::Context).to receive(:build) { context } + expect(context).not_to receive(:fail!) + + interactor.parameters!(:foo, :bar) + interactor.call + end + end + end + describe "#run" do let(:instance) { interactor.new }