Ruby / Rails Tips
A collection of ruby / rails tips I’ve picked up over the years
Using Find Each
PaymentOrders.all.find_each(batch_size: 1000) do |po| puts po.id end
This method (opens in a new tab) runs through the collection in batches of a specified batch size (default 1000) which is more efficient than using .each
as it does not need to hold all the objects in memory while looping.
Memoized Instance Variables
#counterparty_exporter.rb
def counterparty @counterparty ||= ledger_record.ledgerable end
These are used when the variable value involves a database query. In order to prevent duplicate queries we set an instance variable which then gets called after the initial call. Note: Rubocop requires the variable name to be the same as the method name.
Use .compact
to remove hash items with nil values
fruits = { apples: 2, oranges: nil, bananas: 3 }
fruits.compact
{ apples: 2, bananas: 3 }
Use .iso8601
to convert a date to the ISO8601 format YYYY-MM-DD
date = Date.today
date.iso8601
"2020-05-31"
date = Time.now
date.iso8601
"2020-05-31T13:35:53-07:00"
Use find_by
instead of where(...).first
Post.find_by name: 'Spartacus', rating: 4
This method (opens in a new tab) finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself. If no record is found, returns nil
.
Use insert_all / upsert_all
to do bulk db writes
A common pattern in web apps, particularly in refresh jobs is loop over records, make updates and save them. It is easy to fall into the trap of writing to the db once for each record, which leads to some nasty traces. A solution to this is to keep the records in memory and write them all to the db in one go.
upserts = records_from_ledger.map do |record| record.data["name"] = name record end
Accounting::LedgerEntity.upsert_all(upserts.map(&:attributes))
Avoid using .where/.find_by
on associations which cause more db reads
def existing_records @existing_records||= Record.where(first_name: "sean") end
existing_records.where(last_name: "bolton")
existing_records.to_a.find { |r| r.last_name == "bolton" }
Create an ActiveStorage Blob locally
file_name = "*OR0063*.txt.20191239092219603"
blob = ActiveStorage::Blob.create_after_upload!(io: File.open(Rails.root.join("tmp", "ach", file_name)), filename: file_name)
Attach a file to an object locally
image = open("https://via.placeholder.com/150 (opens in a new tab)")
organization.email_logo_file.attach(io: image , filename: "foo.jpg")
organization.save!
Use `.max_by` to returns the object in enum that gives the maximum value from the given block.
link to docs (opens in a new tab)
a = %w(albatross dog horse)
a.max_by {|x| x.length } #=> "albatross"
Querying Nested JSONB data
-> returns a json object
->> returns the text value
IncomingPaymentDetail.where("incoming_payment_details.data -> 'batch_header_record' ->> 'standard_entry_class_code'= ?", "CIE")
Use .partition to split an array
ar = %w(a b a a b c d) as, rest = ar.partition { |i| i == "a" }
[7] pry(main)> as [ [0] "a", [1] "a", [2] "a" ]
[8] pry(main)> rest [ [0] "b", [1] "b", [2] "c", [3] "d" ]
.index_by
Link to docs (opens in a new tab)
apples = Apple.all ap apples.index_by(&:id)
{ "id_1": { Apple Object 1 }, "id_2": { Apple Object 2 } }
© Sean Bolton.RSS