Skip to content

Instantly share code, notes, and snippets.

@rhiroyuki
Last active July 24, 2019 20:07
Show Gist options
  • Save rhiroyuki/f01e7af10367b5b922fc0328803d9d71 to your computer and use it in GitHub Desktop.
Save rhiroyuki/f01e7af10367b5b922fc0328803d9d71 to your computer and use it in GitHub Desktop.
dont use merge!

WIP

One of the problems I've faced recently was a ruby class that heavily used merge! through all their methods. If you don't know what .merge do, let me explain it first:

If you have a ruby hash and want to add a new key with a value you have two ways to do it:

product = {
  sku: 'SKU1234567'
}

# first way
product[:title] = 'Yoyo'
product[:description] = 'An awesome Yoyo'

#or

# second way
product = product.merge(
  title: 'Yoyo',
  description: 'An awesome Yoyo'
)

product[:title] # 'Yoyo'
product[:description] # 'An awesome Yoyo'

As you can see, the first way seem to be much more easy to read because there is an assignment for every value we want to provide to product. The second way doesn't seem too complicated but it can make our work easier when we want to merge two hashes together to pass to another class or method.

And how does .merge! behave? What's the difference between .merge and .merge!? Well, .merge and .merge! have the same behavior except for one thing. While .merge returns a new merged hash, .merge! modifies the actual hash that invoked the method. Let me show you an example:

# If we have again:
product = {
  sku: 'SKU1234567'
}

# Using .merge
product.merge(title: 'Yoyo')
product[:title] # nil

# Using .merge!
product.merge!(title: 'Yoyo')
product[:title] # 'Yoyo'

As you can see if we invoke the method .merge! we don't need to assign the return of the method in order to keep the value generated by it.

So it looks like merge! is actually a pretty hand method right? But problems/complexity arise if we use merge!. Problems like your hash modifying/adding/deleting your keys without you expecting it could raise bugs that are really hard to track as well as making it harder to do refactoring.

So how we can avoid this? Well, the first thing would be to not use methods that mutates the current hash. Also, do not change the hash if you received it as a parameter to your class/module.

You could also .freeze your hash to avoid it being mutated, raising the RuntimeError: can't modify frozen Hash exception.

sources: https://thoughtbot.com/upcase/videos/mutation-in-ruby http://vaidehijoshi.github.io/blog/2015/03/03/bundle-up-and-let-your-objects-do-the-freezing-frozen-hashes/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment