giant robots smashing into other giant robots

Jan 25

Inject that Rails Configuration Dependency!

Setting up configuration settings in a Rails app has been fairly straightforward for a while now:

# config/environments/development.rb
Doit::Application.configure do
  config.default_creator = "Person 1"
end

# config/environments/test.rb
Doit::Application.configure do
  config.default_creator = "Test Person"
end

# config/environments/production.rb
Doit::Application.configure do
  config.default_creator = "John Doe"
end

To access this setting, call Doit::Application.config.default_creator within your app. Pretty straightforward, right?

class TodoItem < ActiveRecord::Base
  before_create :assign_default_creator, unless: :creator?

  private

  def assign_default_creator
    self.creator = Doit::Application.config.default_creator
  end
end

Let’s imagine you have a todo item and you want to test that the value gets assigned if no creator is provided.

Imagine how you’d test this.

describe TodoItem do
  it "assigns the default creator when no creator is assigned" do
    Doit::Application.config.stub(:default_creator).and_return("default creator from config")
    subject.save
    subject.creator.should == "default creator from config"
  end

  it "does not assign the default creator if it has been set" do
    Doit::Application.config.stub(:default_creator).and_return("default creator from config")
    subject.creator = "Jane Doe"
    subject.save
    subject.creator.should == "Jane Doe"
  end
end

There’s a few things that are gross. First, we’re referencing Doit::Application in the model spec. Second, we’re stubbing in both examples. The latter is a low-hanging fruit so it can be extracted.

describe TodoItem do
  it "assigns the default creator when no creator is assigned" do
    config_default_creator_returns("default creator from config")
    subject.save
    subject.creator.should == "default creator from config"
  end

  it "does not assign the default creator if it has been set" do
    config_default_creator_returns("default creator from config")
    subject.creator = "Jane Doe"
    subject.save
    subject.creator.should == "Jane Doe"
  end

  def config_default_creator_returns(value)
    Doit::Application.config.stub(:default_creator).and_return(value)
  end
end

Now, interacting with Doit::Application is confined to one method. Some people may stub the application in an RSpec before block, but I don’t like doing that because one of the specs cares about the stubbed value. I want that stub right in the example so it’s obvious that the stub and assertion are close (in number of lines).

Even though Doit::Application is confined to one call in the spec, I really don’t like that the spec cares about its config at all. What I’d love to do is assign a custom configuration on my TodoItem in my test intsead of caring about Doit::Application and having to stub on config. I can do this with dependency injection.

Right now, TodoItem has a dependency on Doit::Application.config. Dependency injection would mean TodoItem gets a class_attribute :config that defaults to Doit::Application.config but can be overridden (say, in our tests).

describe TodoItem do
  it "assigns the default creator when no creator is assigned" do
    subject.config = stub("config", default_creator: "default creator from config")
    subject.save
    subject.creator.should == "default creator from config"
  end

  it "does not assign the default creator if it has been set" do
    subject.config = stub("config", default_creator: "default creator from config")
    subject.creator = "Jane Doe"
    subject.save
    subject.creator.should == "Jane Doe"
  end
end

With the class attribute, I’m able to override config on the instance and replace it with a stub that has a default_creator method, which I’ve assigned to the string I expect. I was able to remove my reference of Doit::Application from the spec. Perfect!

Here’s the model code:

class TodoItem < ActiveRecord::Base
  class_attribute :config
  self.config = Doit::Application.config

  before_create :assign_default_creator, unless: :creator?

  private

  def assign_default_creator
    self.creator = config.default_creator
  end
end

The callback assign_default_creator now doesn’t care about Doit::Application.config, only that config has a default_creator method.

This post actually stemmed from my interaction with a developer in Factory Girl GitHub Issues who asked a pretty interesting question about reloading classes in Factory Girl after removing the constant and loading the Ruby file again (it seemed like a code smell and not an issue with Factory Girl).

At the end of the thread, I suggested he attend my Test-Driven Rails workshop next week, January 30th and 31st, because I’ll be talking about RSpec and dependency injection (among other things like Cucumber, how, when, and what to test in a Rails app). It’s perfect for Rails developers who are interested in writing more cleaner, more stable applications.

See you there!

Jan 23

Apprentice.io Three Week Retrospective

Over the past three weeks I’ve begun my apprenticeship at thoughtbot. The apprenticeship lasts until the end of March.  I’m joined by designers Paul Webb and Edwin Morris, and fellow coder apprentice Alex Patriquin. Each of us is assigned to a mentor, who makes sure we absorb as much as possible of the thoughtbot way of doing things and achieve our specific goals. For me, the apprenticeship is a couple of things: it’s a chance to work in coder nirvana (TDD, heavy refactoring, bookshelf full of great literature, 5-minute meetings, pair programming, investment days, open source - the works), and it’s a chance to vastly increase my Rails skills in a bootcamp-style training environment.

My head is swimming with new knowledge. I entered this state the first day, and I’ve been there constantly for the past three weeks. Each evening I ride the T home with my poor neurons about to burst with new activity. At night while I sleep my brain indexes all this new knowledge, and the next day I dive in again. If you’ve ever read Ender’s Game, it’s sort of like Battle School for Geeks.

Let’s get specific. This list is long because I’ve been truly busy. In the past three weeks I have:

Did I mention I’ve been busy? That’s just the first three weeks - and I’ve got ten weeks left. I feel excited, involved, and challenged in a completely invigorating way. The team here is universally smart and helpful, and while I’m here I simply can’t help but get better by osmosis at what I do. This is the most fun I’ve had in an office in a long time.

For more information about this program, and to sign up to sponsor a pool of apprentices, visit apprentice.io

More later on,

Every Two Weeks

Our most popular gems—paperclip, capybara-webkit, factory_girl, and clearance—will see new releases every two weeks.

Trying to be reasonable

Release early, release often. In the past we’ve often released new versions of gems in a “when we feel like it” fashion: either we needed the gem to exist for a personal project, or we’re proud of some change we just made, or someone complained to us about an old version. This lead to a problem: when we were just taking pull requests we didn’t think to release a new version.

By setting up a regular schedule this means that contributions will be more public more quickly. We chose two week cycles as a compromise between releasing very often (every time we take a pull request) and in a wide duration (every three months). Too often and we’d annoy people when they upgrade; too infrequently and the bugs are simply not being fixed for the largest number of people.

It’s a process

Setting aside time to make a release elevates it from “OK I made these changes gem push woo!” to “I am going to make a proper release”—it reifies the release as a first-class action for us to concentrate on. Changelogs, blog posts, and tweets are side effects of putting the right amount of care into releasing a new version of a gem.

When‽

Factory Girl (factory_girl and factory_girl_rails) and Paperclip are on a two week release cycle starting January 27th, 2012. Clearance and capybara-webkit are on a two week release cycle starting February 3rd, 2012.

You can track gem releases using the RubyGems Web site and your favorite feed reader. It’s like this:

  1. Sign up for RubyGems, or sign in if you already have an account.
  2. Navigate to the page for the gem you care about.
  3. Click “Subscribe”.
  4. Click the “Dashboard” link at the top of the Web page.
  5. There’s a sweet orange RSS icon in the middle of that page. Subscribe to that link!

This blog post would make no sense in The Land Before Time

When you say “every two weeks”…

Sometimes it doesn’t make sense to cut a release only every two weeks. Important security fixes or bugs that block everyone from using the gem are examples where getting something out as soon as possible is the only way to do it. On the other hand, sometimes there simply haven’t been any changes.

If we release a new version of a gem on the 26th and the 27th is when a scheduled release is to happen, it’s likely that we’ll skip the schedule. In general we’ll continue the “do I feel like making a release?” thought process, but every two weeks and with a bias towards a positive answer.

Or whenever

The bi-weekly cycle is new and, like everything else in software and life, worth reconsidering at all times. Please let us know if it’s too often, too seldom, just right, or completely useless to you!

Jan 20

Factory Girl 2.5 Gets Custom Constructors

Today marks a big day in the life of Factory Girl; you can now override the constructor for factories! This is great news for people who’ve been upset that they can’t use Factory Girl with objects who have constructors with required arguments, as Factory Girl would previously just call new without passing any arguments.

Here’s the syntax:

# app/models/report_generator.rb
class ReportGenerator
  def initialize(name, data)
    @name = name
    @data = data
  end

  # ...
end

# spec/factories.rb
FactoryGirl.define do
  factory :report_generator do
    ignore do
      name "Generic Report"
      data { {:foo => "bar", :baz => "buzz"} }
    end

    initialize_with { ReportGenerator.new(name, data) }
  end

  # ...
end

Note that I wrapped the name and data attributes in an ignore block. Factory Girl doesn’t differentiate between attributes passed in the custom constructor and normal attributes to assign, so moving them to the ignore block ensures that I don’t instantiate the report generator and then attempt to assign name and data again.

Grab a copy of 2.5.0 and start using Factory Girl with your other objects today!

This week in open source

shoulda-context

Hey! shoulda-context has a maintainer! His name is Travis Jeffery (travisjeffery) and he’s got commit rights and everything! Thank you, Travis.

Yeah!

cocaine

One feature was added to cocaine over the past week: Daniel Mircea (viseztrance) made it such that you can now pass blank argument values (a2d01c4). For example, this now works:


command_line = Cocaine::CommandLine.new(
  "curl",
  "-X POST -d :data :url",
  :data => "",           # Hey note this!
  :url => "http://localhost:9000",
  :swallow_stderr => false)

puts command_line.command

…which will produce:

curl -X POST -d '' 'http://localhost:9000'

paperclip

The paperclip project saw a few neat commits this week. Jeremy McNevin (jmcnevin) and ralph (ralph) pass the file type to Fog now, always (839b98c and 201f02e). Luke Griffiths (Sporky023), one of our new apprentices, got his first open source contribution in: you can pass an :s3_encrypted option for controlling which encryption type S3 should use (65e0338 and af6d343). And Jon Yurek (jyurek) fixed mixing Paperclip with non-ActiveRecord codebases (bc6ed16).

factory_girl

The fixture replacement gem, factory_girl, is now at version 2.4.2 (845578c). It includes bug fixes, a deprecation, and more Rails support. All commits were by Joshua Clayton (joshuaclayton).

There was an issue where using an inline trait would cause it not to be reset (70a80fd), and an issue with the precedence of inline traits (065c6c1).

Josh also deprecated the attributes_for class method, which once upon a time produced all the attributes that a factory would set (a883315). He provided no reliable workaround.

Today, Rails 3.2 was released, so we made sure to test against that—and it works! (5555f14)

capybara-webkit

A new release for capybara-webkit, yeay! Joe Ferris (jferris) cut the release (59ba861), first removing a long-standing debugging print statement (0bac05e).

Sweet features, bro: Yuri Gadow (ylg) added support for the fancy text inputs in HTML5 like email, number, search, and so on (7e5e99b).

John Barker (excepttheweasel) updated the README to point out that capybara-webkit does not, in fact, listen on port 8200 (2285621).