Monday, January 25, 2016

Arbitrary SQL ordering

I ran across an issue the other day where I had 4 records from the database that were being displayed in order of creation date, but I wanted them displayed in an arbitrary order - not alphabetical by name or title, or chronological, or sorted by id.

Before, I probably would have used Ruby to loop through the records and build an array by comparing ids. Now, though, I know better than to use Ruby for this type of thing, because it’s faster and better for memory to do it in SQL if possible (which I learned the hard way).

Turns out, querying using SQL looks a lot like what I would have written in Ruby anyhow:

SELECT * FROM foo ORDER BY
CASE
  WHEN id=67 THEN 1
  WHEN id=23 THEN 2
  WHEN id=1362 THEN 3
  WHEN id=24 THEN 4
END

Pretty cool! Thanks to cpjolicoeur on Github for this one.

Monday, January 18, 2016

Migrating data in Rails using Rails migrations

Put this one in the “should’ve been obvious” column…

For an embarassingly long while, when I had a feature ready for deploy that required production data be modified for a whole bunch of records – for example, downcasing all name attributes – I would write a one-off rake task to update those records, then delete that task.

Not only is this bad code since it doesn’t keep a record of what happened, it’s also tedious because it’s an extra step that must be taken by whoever deploys the code.

Luckily there’s a better way - just put the updating login in a migration!

Example:

class DowncaseName < ActiveRecord::Migration
  def up
    User.where.not(name: nil).find_each do |user|
       user.update!(name: user.name.downcase)
    end
  end
end

This example isn’t great because 1. there’s almost certainly a way to do this with pure SQL without instantiating every user record, and 2. it’s not reversible. But since I’m lazy and since it illustrates the point of the post, I’m leaving it.

Monday, January 11, 2016

No return value in rails console

A quick tip when working in rails console. If you're testing commands to run and don't want the console to output the return value, just append a semicolon to your ruby code.

  User.all
  # => [#<user:0x007fd72a408630> {:id => 1, :name => "Mr. T", ...}, tons and tons of other records]
  User.all;
  # => nothing!

OK, so that's a contrived example, but this really comes in handy

  • when creating records, or 
  • if you need to update several records in one shot and don't want to see the return values, or
  • POSTing to an external API and avoiding all the cruft that comes in the response

Monday, January 4, 2016

Classy Ruby enumerating

Given two users:

User.create(first_name: "Joe", last_name: "Louis")
#=> User id: 1, first_name: "Joe", last_name: "Louis"

User.create(first_name: "Muhammed", last_name: "Ali")
#=> User id: 2, first_name: "Muhammed", last_name: "Ali"

Say you want to get an array of arrays, with each individual array containing two elements: the first and last name, and the id. You could do something like this, which is perfectly valid but also gross to read:

User.pluck(:id, :first_name, :last_name).map do |user|
  id = user[0]
  first = user[1]
  last = user[2]
  ["#{first} #{last}", id]
end

However, it’s much more readable to use tuple syntax:

User.pluck(:id, :first_name, :last_name).map do |(id, first, last)|
  ["#{first} #{last}", id]
end
#=> [
      ["Joe Louis", 1],
      ["Muhammed Ali", 2]
    ]