0

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?

Gabriel
  • 621
  • 7
  • 19
  • What were the inputs that produced the surprising behavior? – D. SM May 22 '20 at 22:27
  • The only input into the method is always a user object. There only thing I can think that might cause weird issues is I am using a `before_create` to assign the address fields. After creating the user I then send it into my service class here (I format the data before) and then in the service class there is another method where it is then sent to an API endpoint. The service method itself is calling this formatting method here. – Gabriel May 22 '20 at 22:53

1 Answers1

1

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.

kimrgrey
  • 562
  • 2
  • 10
  • Yeah that is what is so odd. Nothing besides the array was getting passed through. I will look into it a bit more and update my question with anything I can find. But thanks for the link to the extract method. Helps to know that the Array arg shouldn't be working – Gabriel May 26 '20 at 15:54
  • Ok I think I finally figured it out. It wasn't actually extracting the attributes when the array argument was sent through. The reason I thought it was extracting them is I was checking on the API and saw the home address was being created. But there was an underlying method that was assigning the mailing address I was sending to be the home address. It is a lot of code to show for other processes that were happening and a bit hard to explain. TLDR it was an issue happening on the API client. sending an array in the .extract! does not work whatsoever – Gabriel May 26 '20 at 17:04