Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Don’t Be Liberal When Being Aggressive: Ruby Hash’s Fetch With Defaults

DZone's Guide to

Don’t Be Liberal When Being Aggressive: Ruby Hash’s Fetch With Defaults

Hash is an extremely flexible structure and we can't imagine our code without it. However, this flexibility comes with a hidden cost—uncertainty.

· Web Dev Zone ·
Free Resource

Bugsnag monitors application stability, so you can make data-driven decisions on whether you should be building new features, or fixing bugs. Learn more.

Hash is an extremely flexible structure and we can't imagine our code without it. However, this flexibility comes with a hidden cost—uncertainty. Consider this code:

def find_exchange_rate(data)
  amount = data[:amount]
  from_currency = data[:from_currency]
  to_currency = data[:to_currency]

  ... #persistence access code
end


In order to prevent situations where  data  attributes aren't defined, fetch comes to help.

def show_exchange_rate(data)
  amount = data.fetch(:amount)
  from_currency = data.fetch(:from_currency)
  to_currency = data.fetch(:to_currency)
end


If from_currency or to_currency isn't defined, the application fails. Now, the code is much more secure.

Here's the moment when you have to decide what the policy will be. One option is to go with exception raised when a key is not present, but another approach is to use a default value.

from_currency = data.fetch(:from_currency) { DEFAULT_CURRENCY }


At first glance, it's a very convenient way to make the application work. However, it's not. It's rather a first step to making your application a source of falsehood. It's just like I said in the title—we're both aggressive and liberal because we enforce you to pass the param, but in case you forget we don't tell you about it.

It's enough if your client makes a misspelling in his client code, for example this:

data = {
  value: 1000,
  from_curency: 'EUR', #missing r!
  to_currency: 'THB'
}

service.find_exchange_rate(data)


He'll get the rate of exchange. Moreover, it may seem that this rate is a valid value (USD is a bit cheaper than EUR but not enough to be easily noticeable). I don't have to tell what effects this may have!

In this situation, a lot of developers would say: If you had unit tests in your client code you wouldn't have a problem. Maybe we wouldn't have problems, but the world is not perfect, and if you can minimize effects of such errors, you should. Eventually, the API owner is responsible for data correctness, not the client. Usually, it's better to not deliver data at all than to deliver improper data.

More Secure But Still Vulnerable

The approach with fetch  is more secure but this code is not fully secured yet. When I forget to assign a correct value to any of  data  attributes, but all keys are present  nilw will be introduced.

data = {
  value: 1000,
  from_currency: 'EUR',
  to_currency: nil
}

service.find_exchange_rate(data)


Obviously, in order to guarantee everything is okay, we would have to introduce a dedicated validator.

Monitor application stability with Bugsnag to decide if your engineering team should be building new features on your roadmap or fixing bugs to stabilize your application.Try it free.

Topics:
ruby ,hash ,secure code

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}