arraysruby-on-railsrubysplat

Why was I able to get away with not using a splat operator sometimes


I have some ruby code from my Ruby on Rails project.

I am formatting some data so I am calling attributes.extract! to get the fields I need from my model.

I noticed recently that every now and then the data wouldn't get extracted as expected. I realized that I needed a splat operator. But it is strange because I do notice that sometimes when the method is called in my Rails project it will sometimes extract the data without the use of the splat operator. But when I run the code from my Rails console it never extracts the data unless I add the splat operator.

Here is the code in question

  # in a service file, let's call it service.rb
  def self.format_user_home_address_data(user)
    # This doesn't work in the console but sometimes works when run in my Rails project
    home_address_data = user.attributes.extract!(User::HOME_ADDRESS_FIELDS)

    home_address_data[:address_type] = "home"
    home_address_data
  end

  # at the end this method will sometimes return { address_type: "home" } or 
  # sometimes it'll actually return the extracted attributes as expected

HOME_ADDRESS_FIELDS is just an array with the values ["address_line_1", "city", "state", "zip"]

Anyway I know that to get it to run correctly I need to do this

    home_address_data = user.attributes.extract!(*User::HOME_ADDRESS_FIELDS)

But does anyone know why I was able to get away without adding the splat operator for so long? Is there some Ruby on Rails magic that is only sometimes happening? What's the deal?


Solution

  • Well, let's check it out. There is no any magic behind attributes.extract! in the end. Here is an actual implementation of this method from Rails source code:

    def extract!(*keys)
      keys.each_with_object(self.class.new) { |key, result| 
        result[key] = delete(key) if has_key?(key) 
      }
    end
    

    Link: click. As you can see, it creates new hash, goes over the keys one by one and moves value from self to this new array. So, if you give an Array argument to this method then key in the block will be an Array as well. So, it won't be found. So, no way it may work for array argument. The only one possibility is that something else is passed instead of User::HOME_ADDRESS_FIELDS.