AaronLasseigne / active_interaction

:briefcase: Manage application specific business logic.
MIT License
2.07k stars 137 forks source link

An `array` filter can mutate the state of the passed in Array instance #545

Closed jonkgrimes closed 1 year ago

jonkgrimes commented 1 year ago

During an upgrade from 4.1 to 5.1.1 we noticed a small change in behavior when using an array filter that transforms its values. It looks like the call to map! in ArrayFilter#process will mutate the original array passed into the interaction. This raises errors in cases when the original array is frozen as well as some ActiveRecord interactions (e.g., being passed a list ID's and then saving the object those ID's belong to).

I've written a script below to reproduce this behavior:

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  # gem 'active_interaction', '4.1'
  gem 'active_interaction', '5.1.1'
end

class TestInteraction < ActiveInteraction::Base
  array :internal_list, default: [] do
    string
  end

  def execute
    internal_list << "1"
    puts "internal_list = #{internal_list.inspect}"
  end
end

class Foo
  attr_reader :list

  def initialize
    @list = []
  end
end

foo = Foo.new
puts "foo.list = #{foo.list.inspect}"

TestInteraction.run!(switch: true, internal_list: foo.list)
puts "foo.list = #{foo.list.inspect}"
TestInteraction.run!(switch: true, internal_list: foo.list)
puts "foo.list = #{foo.list.inspect}"

When run under version 4.1 the output is:

foo.list = []
internal_list = ["1"]
foo.list = []
internal_list = ["1"]
foo.list = []

When run under version 5.1 the output is:

foo.list = []
internal_list = ["1"]
foo.list = ["1"]
internal_list = ["1", "1"]
foo.list = ["1", "1"]