From ae3a86485973997632cb3589a9eab3cee3d83e4e Mon Sep 17 00:00:00 2001 From: Taylor Fausak Date: Thu, 8 Sep 2016 11:26:40 -0500 Subject: [PATCH] Allow ActionController::Parameters as inputs This fixes #381. --- lib/active_interaction/base.rb | 19 +++++++++++++++++-- spec/active_interaction/base_spec.rb | 17 +++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/active_interaction/base.rb b/lib/active_interaction/base.rb index 3dee8f27..99d6e26b 100644 --- a/lib/active_interaction/base.rb +++ b/lib/active_interaction/base.rb @@ -169,8 +169,7 @@ def eagerly_evaluate_default(filter) # # @private def initialize(inputs = {}) - raise ArgumentError, 'inputs must be a hash' unless inputs.is_a?(Hash) - + inputs = normalize_inputs!(inputs) process_inputs(inputs.symbolize_keys) end @@ -234,6 +233,22 @@ def run_validations! private + # We want to allow both `Hash` objects and `ActionController::Parameters` + # objects. In Rails < 5, parameters are a subclass of hash and calling + # `#symbolize_keys` returns the entire hash, not just permitted values. In + # Rails >= 5, parameters are not a subclass of hash but calling + # `#to_unsafe_h` returns the entire hash. + def normalize_inputs!(inputs) + return inputs if inputs.is_a?(Hash) + + klass = 'ActionController::Parameters'.safe_constantize + if !klass || !inputs.is_a?(klass) + raise ArgumentError, 'inputs must be a hash' + end + + inputs.to_unsafe_h + end + # @param inputs [Hash{Symbol => Object}] def process_inputs(inputs) @_interaction_keys = inputs.keys.to_set & self.class.filters.keys diff --git a/spec/active_interaction/base_spec.rb b/spec/active_interaction/base_spec.rb index 1723d86a..87c84ded 100644 --- a/spec/active_interaction/base_spec.rb +++ b/spec/active_interaction/base_spec.rb @@ -1,6 +1,7 @@ # coding: utf-8 require 'spec_helper' +require 'action_controller' InteractionWithFilter = Class.new(TestInteraction) do float :thing @@ -58,6 +59,22 @@ def execute end end + context 'with non-hash inputs' do + let(:inputs) { [[:k, :v]] } + + it 'raises an error' do + expect { interaction }.to raise_error ArgumentError + end + end + + context 'with ActionController::Parameters inputs' do + let(:inputs) { ActionController::Parameters.new } + + it 'does not raise an error' do + expect { interaction }.to_not raise_error + end + end + context 'with a reader' do let(:described_class) do Class.new(TestInteraction) do