Script for opening the latest migration

April 17th, 2013

Often after generating a Rails migration I want to edit it, but it’s annoying to have to type in those dang timestamps. So I wrote a little script that just opens the newest one:

#!/bin/sh
s db/migrate/`ls db/migrate | tail -n 1`

Put that in ~/bin/latest-migration, chmod u+x it, and add ~/bin to your PATH and you can just run latest-migration from within any Rails folder. Of course you have to replace ’s’ with the command for invoking your text editor of choice.

env: ruby_bundler_wrapper: No such file or directory

September 20th, 2012

I spent bunch of time scratching my head, trying to figure out why my ruby executables (rails, heroku, etc) weren’t working. I thought it was because I had an old custom shebang in my .gemrc, but that didn’t seem to change anything. I ran gem regenerate binstubs and that didn’t help either. I still to this day don’t even know what package provides ruby_bundler_wrapper, or even where it lives.

BUT, I figured out the problem for me was that I had my gems stored in a global rvm gemset, so I had to regenerate the binstubs in there:

rvm @global do rvm regenerate binstubs

And that seemed to do the trick. Long morning of system administration today.

“libxml2 is missing” error when installing Nokogiri gem on Mac OS X Lion

September 4th, 2012

I’m not 100% sure what did the trick for me, but the last thing I did was symlink my gcc binary to gcc-4.2, which for some reason is hardcoded in Nokogiri:

sudo ln -s /usr/bin/gcc /usr/bin/gcc-4.2

As suggested here. The Nokogiri gem is totally unhelpful with the error messages. It just tells you that libxml2 can’t be found, no matter what the build problem is. If you are having trouble getting Nokogiri to install, your friend is the mkmf.log. On an RVM sytem, that’ll be in ~/.rvm/gems/[your gemset]/gems/nokogiri-1.5.5/ext/nokogiri/mkmf.log. Without RVM it’ll be wherever gem sticks unbuilt gems by default.

Quoting quotes in Ruby

June 21st, 2012

Cucumber makes heavy use of quotes to wrap data:

When I purchase "Oryx and Crake"
Then I should receive an email with the subject "Thank you for your purchase!"

I sometimes need to to programmatically generate these strings, when invoking steps-within-steps:

When /^I purchase "([^""]*)"$/ do |title|
  step "Search for \"#{title}\""
  ...
end

Thankfully, Ruby has a metric shit ton of ways to quote strings. But what I haven’t seen mention of is the fact that you can just use the % operator with an arbitrary character to get a fully interpolated string:

# These are all equivalent:
step %~Search for "#{title}"~
step %(Search for "#{title}")
step %<Search for "#{title}">
# I doesn't matter which symbol you use.  Just use whatever 
# symbol is not present in your string.

All the references I’ve seen tell you to use %Q[whatever] for interpolated strings. The Q seems unnecessary.

But if you want an uninterpolated string you do still need to do %q?whatever?.

Stupid Ruby Tricks 3: Injecting an operator

June 1st, 2012

Just found out that you can inject an operator into a collection. For example:

[[1,2],[3,4]].inject([], :+)
#=> [1, 2, 3, 4]

You don’t even need the starting value. Rad.

Edit: If you leave out the starting value, injecting an empty array will return nil. Include an empty array as the first parameter if you want an empty array back in that case.

Previous trick: Using find and find_all on ActiveRecord collections

Custom rspec matcher for Delayed::Job performable methods

May 22nd, 2012

I’ve been working on code that delays a bunch of work using Delayed Job’s :delay method. Stuff like this:

class Album
  after_create :send_emails
 
  def send_emails
    recipients.each do |recipient|
      recipient.delay.send_email
    end
  end
end

I tried a number of testing strategies:

  • checking that the Delayed::Job.count changed
  • testing for properties on Delayed::Job.last
  • working off the jobs and testing that the work got done.

But they all seemed too indirect. In the end I decided to just write a custom matcher that lets me do this:

expect { 
  Album.create :recipients => [janelle_monáe]
}.to delay_method(janelle_monáe, :send_email)

Another reasonable route would be to mock :delay on the appropriate object, and then do a :should_receive on the mock. That only works if the object that’s getting the method delayed exists in your test scope. It won’t work if the object is created or looked up inside the method you’re testing…. unless you stub out the relevant :create and :find methods.

I don’t know. I’m still (3+ years in) learning how to decide how intensively to mock my unit tests. Some part of me thinks I’m just not using mocks enough. But the part that likes unit tests to be a little more flexible in the face of refactoring resists. We’ll see how it goes.

Use it like this:

# On a specific object:
apple = Apple.new
expect {...}.to delay_method(apple, :core)
 
# On any instance of a class:
expect {...}.to delay_method(Apple.any_instance, :core)
 
# On whatever object the code returns:
expect { Apple.create }.to delay_method(returned_object, :core)

And here’s the code:

# spec/support/delayed_job.rb
module RspecDelayedJobMatcher
  class ReturnedObject
  end
end
 
def returned_object
  RspecDelayedJobMatcher::ReturnedObject
end
 
RSpec::Matchers.define :delay_method do |object, method_name|
 
  def jobs
    Delayed::Job.all.select do |job|
      (payload = job.payload_object) && equivalent_object?(payload.object) && payload.method_name == @method_name
    end.count
  end
 
  def equivalent_object?(payload)
    if @object == returned_object
      payload == @response
    elsif @object.is_a? RSpec::Mocks::AnyInstance::Recorder
      payload.is_a? @object.instance_variable_get("@klass")
    else
      payload == @object
    end
  end
 
  def description
    if @object == returned_object
      "the returned object (#{@response.inspect})"
    elsif @object.is_a? RSpec::Mocks::AnyInstance::Recorder
      "some instance of #{@object.instance_variable_get("@klass").to_s}"
    else
      @object.inspect
    end
  end
 
  match do |proc|
    @method_name = method_name
    @object = object
    count_before = jobs
    @response = proc.call
    (@count = jobs - count_before) == 1
  end
 
  failure_message_for_should do |actual|
    "Expected block to create a job to invoke #{@method_name} on #{description}, but it created #{@count}"
  end
 
  failure_message_for_should_not do |actual|
    "Block created #{@count} job(s) to invoke #{@method_name} on #{description}, but expected none"
  end
 
  description do
    "expect block to delay #{@method_name} on a #{@object.class.to_s}"
  end
end

Invoking methods on optional objects

May 18th, 2012

Lately I’ve been using pretty heavily an extension I wrote to Ruby’s Object class:

# lib/core_extensions/object.rb
class Object
  def defined_and(key=nil)
    return nil if nil?
    block_given? ? yield(self) : self[key]
  end
end

This lets me invoke methods on objects that may or may not exist. It’s really useful for the frequent situations where an optional child object has a method that also applies to it’s parent:

class ZipCode
  def start_for(vegetable)
    planting_specs.for(vegetable).defined_and &:start
  end
end

As opposed to the ugly alternative:

def start_for(vegetable)
  spec = planting_specs.for(vegetable)
  spec.start if spec
end

That will either return a start, or it’ll return nil if no planting_spec was found. It works with a block (&:start is a shortcut for {|x| x.start}) or an enumerable key:

{ :a => 1}.defined_and(:a) #=> 1
nil.defined_and(:a) #=> nil

To use stuff in the lib/core_extensions folder in your Rails app, as defined above, you need an initializer:

# config/initializers/core_extensions.rb
Dir[Rails.root.join("lib/core_extensions/*")].each {|f| require f}

Explain ActiveRecord queries in Rails console

May 15th, 2012

I keep missing this in Google somehow, but if you want ActiveRecord to explain your queries in the console, do:

ActiveRecord::Base.logger = Logger.new(STDOUT)

via shime on StackOverflow

Destructive (low memory) Ruby each

May 11th, 2012

As mentioned, if you have a loop that allocates a bunch of memory tied to an object, destroying the current object after each iteration can help save on memory. I found myself reusing this pattern, so I just extended Array:

class Array
  def destructive_each
    while item = shift
      yield(item)
    end
  end
end

It’s called “destructive” because after using it your array will be empty.

Simplify your conditional step definitions with should_unless

March 23rd, 2012

In cucumber, you often want to be able to use a step in both it’s positive and negative form. For example, in different universes I might want to ensure that “pigs can fly”, or that “pigs can not fly”. Typically I’ve either written two step matchers for this, or added an awkward conditional:

Then /^pigs can (not |)fly$/ do |negated|
  if negated.blank?
    Pig.new.should have_the_power_of_flight
  else
    Pig.new.should_not have_the_power_of_flight
  end
end

But today I decided to factor that out into a little helper method in my cucumber config:

# features/support/env.rb
class Object
  def should_unless(missing, matcher)
    missing.match(/not/) ? should_not(matcher) : should(matcher)
  end
end

This allows me to write my matcher much more simply:

Then /^pigs can (not |)fly$/ do |negated|
  Pig.new.should_unless negated, have_the_power_of_flight
end

So fresh and so DRY!