Simple Ruby pleasures

I think I first discovered the joy of take and drop in my journeys through Haskell. But it appears that, since 2008 at least, we have had the pleasure of using them in Ruby too.

Need the first or last N elements from an Enumerable. Easy!

ary = (1..100).to_a
ary.take(5) # => [1, 2, 3, 4, 5]
ary.drop(95) # => [96, 97, 98, 99, 100]

range = (1..100)
range.take(5) # => [1, 2, 3, 4, 5]
range.drop(95) # => [96, 97, 98, 99, 100]

hsh = {:foo => 1, :bar => 2, :baz => 3}
hsh.take(1) # => [[:bar, 2]]
hsh.drop(2) # => [[:foo, 1]]

The real magic is when you use take along with other Enumerable goodies like select and map. Here’s one of my personal favorites amongst the code I wrote in 2010:

class QueryTracer < ActiveSupport::LogSubscriber

  ACCEPT = %r{^(app|config|lib)}.freeze
  FRAMES = 5
  THRESHOLD = 300 # In ms

  def sql(event)
    return unless event.duration > THRESHOLD
    callers = Rails.
      backtrace_cleaner.
      clean(caller).
      select { |f| f =~ ACCEPT }.
      take(FRAMES).
      map { |f| f.split(":").take(2).join(":") }.
      join(" | ")

    # Shamelessly stolen from ActiveRecord::LogSubscriber
    warning = color("SLOW QUERY", RED, true)
    name = '%s (%.1fms)' % [event.payload[:name], event.duration]
    sql  = event.payload[:sql].squeeze(' ')

    warn "  #{warning}"
    warn "    #{name} #{sql}"
    warn "    Trace: #{callers}"
  end

end

QueryTracer.attach_to :active_record

This little ditty is awesome because:

  • It’s super-practical. Drop this in your Rails 3 app, tail your production log, see the slow queries, go to the method in your app calling it, and fix it. Easy.
  • It only activates itself when it’s needed. Queries that execute quickly return immediately.
  • No framework spelunking required. Rails 3’s notification system handles all of it. Rails’ backtace cleaner gizmo even makes the backtraces much nicer to read.
  • It chains methods to make something that reads like a nice, concise functional program.

For more Enumerable joy, read up on each_cons.