Nacho
Nacho

Reputation: 1009

Is there a more idiomatic way to update an ActiveRecord attribute hash value?

Given a person ActiveRecord instance: person.phones #=> {home: '00123', office: '+1-45'}

Is there a more Ruby/Rails idiomatic way to do the following:

person_phones = person.phones
person_phones[:home] = person_phones[:home].sub('001', '+1')
person.update_column :phones, person_phones

The example data is irrelevant.

I only want to sub one specific hash key value and the new hash to be saved in the database. I was wondering if there was a way to do this just calling person.phones once, and not multiple times

Upvotes: 1

Views: 1410

Answers (2)

3limin4t0r
3limin4t0r

Reputation: 21120

Without changing much behaviour:

person.phones[:home].sub!('001', '+1')
person.save

There are a few important differences here:

  1. You modify the string object by using sub! instead of sub. Meaning that all other variables/objects that hold a reference to the string will also change.
  2. I'm using save instead of update_column. This means callbacks will not be skipped and all changes are saved instead of only the phones attribute.

From the comment I make out you're looking for a one liner, which isn't mutch different from the above:

person.tap { |person| person.phones[:home].sub!('001', '+1') }.save

Upvotes: 2

Paulo Fidalgo
Paulo Fidalgo

Reputation: 22296

You can use the before_validation callback on your model.

Like this:

class Phone < ApplicationRecord

 validates :home, US_PHONE_REGEX

 before_validation :normalize_number

private

 def normalize_number
  home.gsub!(/^001/, '+1')
 end
end

Note: I haven't tested this code, it's meant to show an approach only.

If you're looking to normalize also an international number, evaluate if the use of a lib like phony wouldn't make more sense, or the rails lib https://github.com/joost/phony_rails based on it.

EDIT since the comment clarify you only want to change the values of the hash in one like you can use Ruby's method transform_values!:

phones.transform_values!{|v| v.gsub(/^001/, '+1')}

Upvotes: 0

Related Questions