Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker

Monthly Archives: March 2009

Pivotal Labs

New York Standup 03/19/2009

Pivotal Labs
Thursday, March 19, 2009

Interesting

  • We’ve created a
    Pivotal fork of the Ctags
    bundle for TextMate
    (the original).

    Ctags provides a mechanism for indexing your source code
    files for the purpose of locating and navigating between bits of code;
    something IntelliJ/RubyMine is very good at, while TextMate is found lacking.

    The forked bundle aims to be more inline with Pivotal practices, where
    developers are often switching between IDE/editor worlds. For example, code
    completion has been re-mapped to ctrl-space, to be more like IntelliJ.

    Have you played with Ctags? Take a look at the bundle; see what you think.

Help Wanted

  • We’re on the hunt for quality burritos in NYC. Ben gave
    Burrito Loco a try last night; thought the
    burritos were “supple”. Dave is desperately seeking breakfast burritos.

    Suggestions?

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

What Is Your Workstation Tag?

Joe Moore
Thursday, March 19, 2009

At Pivotal, we love our large, shared workspace and homogeneous workstations. As we move from project to project, our workstations are pretty much the same: iMacs with TextMate, RubyMine, Quicksilver (bound to ⌘+space, of course!) a full Ruby/Rails stack, and a few other applications. Given this minimal setup, I can figure out which developers have used a particular workstation given the extra applications installed upon it. I’ve come to see certain applications as a developer’s “tag,” like a graffiti signature.

  • Silverflow for Quicksilver: Pivot David G. was here!
  • iStat menues: David S. been here!
  • Microsoft IntelliType for Mac: Jonathan B. tagged this machine.

As for me, if you see Jumpcut and Skitch, then I’ve tagged your machine.

What are your tags?

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

DRY, Targeted, and Reusable Testing of ActiveRecord Extensions

Joe Moore
Wednesday, March 18, 2009

At Pivotal, we are passionate about test driven development, keeping things DRY, and writing readable and understandable code. Satisfying all of these desires can be challenging, especially when writing test code. In particular, ActiveRecord extensions present several challenges: which models using an extension should we test? How do we both test our extension in isolation while also testing all model’s usage of that extension? Is it even worth it?

The answer is yes, it is worth it, and it’s also fairly easy, readable, understandable, and DRY. I will present both a common problem and a solution, using a cumulation of technologies and techniques from multiple Pivotal projects, in particular using acts_as_fu to create laser-targeted, isolated, and disposable ActiveRecord models for testing extensions and RSpec shared behaviors to minimize the amount of duplicated test code.

Often we find common patterns in ActiveRecord models and we wish to share that functionality by mixing in a module of shared code, or even mixing that module in to ActiveRecord::Base itself. How should we go about testing these ActiveRecord extensions? Not only do we want to test the extension, but also test that models using that extension are doing so properly. We’ve blogged about dynamically creating ActiveRecords to test extensions in the past, but Pivot Pat Nakajima’s acts_as_fu plugin is a far better tool for this.

The Setup

Let’s say we have three models: Pivotal, Lab, and Pivot. Both a Pivot and a Lab belongs_to Pivotal, have a name, nickname, some common validation, etc:

# app/models/pivotal.rb
class Pivotal < ActiveRecord::Base
end

# app/models/lab.rb
class Lab < ActiveRecord::Base
  RANDOM_NICKNAMES = ["New Hotness", "LOL-Cat Factory", "Tweet Machine"]
  belongs_to :pivotal
  validates_presence_of :name

  def nickname
    "#{self.name}, 'The #{RANDOM_NICKNAMES[rand(RANDOM_NICKNAMES.length)]}'"
  end
end

# app/models/pivot.rb
class Pivot < ActiveRecord::Base
  RANDOM_NICKNAMES = ["New Hotness", "LOL-Cat Factory", "Tweet Machine"]
  belongs_to :pivotal
  validates_presence_of :name

  def nickname
    "#{self.name}, 'The #{RANDOM_NICKNAMES[rand(RANDOM_NICKNAMES.length)]}'"
  end
end

The specs for Lab and Pivot might look like the following:

# spec/models/lab_spec.rb
describe Lab do
  before(:each) do
    @lab = Lab.new
  end

  it "should require name" do
    @lab.should have(1).errors_on(:name)
    @lab.name = 'Stealth Startups'
    @lab.should have(0).errors_on(:name)
  end

  it "should generate a random nickname" do
    @lab.name = 'Stealth Starup'
    @lab.nickname.should_not be_blank
    @lab.nickname.should include(@lab.name + ", 'The ")
  end

  it "should belong to Pivotal" do
    @lab.should respond_to(:pivotal)
    @lab.should respond_to(:pivotal=)
    @lab.should respond_to(:pivotal_id)
    @lab.should respond_to(:pivotal_id=)
  end
end

# spec/models/pivot_spec.rb
describe Pivot do
  before do
    @pivot = Pivot.new
  end

  it "should require name" do
    @pivot.should have(1).errors_on(:name)
    @pivot.name = 'Joe Moore'
    @pivot.should have(0).errors_on(:name)
  end

  it "should generate a random nickname" do
    @pivot.name = 'Joe Moore'
    @pivot.nickname.should_not be_blank
    @pivot.nickname.should include(@pivot.name + ", 'The ")
  end

  it "should belong to Pivotal" do
    @pivot.should respond_to(:pivotal)
    @pivot.should respond_to(:pivotal=)
    @pivot.should respond_to(:pivotal_id)
    @lab.should respond_to(:pivotal_id)
  end
end

DRYing Up the Models

Yuck, look at all that duplication! Let’s start eliminating it by pulling the common model code into an ActiveRecord extension named belongs_to_pivotal:

# lib/belongs_to_pivotal.rb
module BelongsToPivotal
  RANDOM_NICKNAMES = ["New Hotness", "LOL-Cat Factory", "Tweet Machine"]
  module ClassMethods
    def belongs_to_pivotal
      belongs_to :pivotal
      validates_presence_of :name

      instance_eval do
        include BelongsToPivotalInstanceMethods
      end
    end
  end

  module BelongsToPivotalInstanceMethods
    def nickname
      "#{self.name}, 'The #{BelongsToPivotal::RANDOM_NICKNAMES[rand(BelongsToPivotal::RANDOM_NICKNAMES.length)]}'"
    end
  end

  def self.included(base)
    base.extend(ClassMethods)
  end
end

Now our Models look like this:

# app/models/lab.rb
class Lab < ActiveRecord::Base
  belongs_to_pivotal
end

# app/models/pivot.rb
class Pivot < ActiveRecord::Base
  belongs_to_pivotal
end

You’ll need to add “ActiveRecord::Base.send :include, BelongsToPivotal” to config/initializers/new_rails_defaults.rb or some other initializer.

Testing the Extension with acts_as_fu

The models are looking better, but what about the specs? In the “old days” I would create a spec named belongs_to_pivotal_spec.rb and use one of the two Models in that spec. But, when you do that, you get all the the “baggage” from that Model, such as any other methods, associations, inherited methods and properties, etc. Let’s use acts_as_fu to write a spec that tests BelongsToPivotal in isolation.

# spec/lib/belongs_to_pivotal_spec.rb
describe BelongsToPivotal do
  before(:all) do
    # Using acts_as_fu to create a model specifically for our extension
    build_model :belongs_to_pivotal_models do
      # we will need these columns in the database
      string :name
      integer :pivotal_id

      # Call our extension here
      belongs_to_pivotal
    end
  end

  before(:each) do
    @pivotal_model = BelongsToPivotalModel.new
  end

  # Look, it's all of the model specs!
  it "should require name" do
    @pivotal_model.should have(1).errors_on(:name)
    @pivotal_model.name = 'Pivotal Model'
    @pivotal_model.should have(0).errors_on(:name)
  end

  it "should generate a random nickname" do
    @pivotal_model.name = 'Pivotal Model'
    @pivotal_model.nickname.should_not be_blank
    @pivotal_model.nickname.should include(@pivotal_model.name + ", 'The ")
  end

  it "should belong to Pivotal" do
    @pivotal_model.should respond_to(:pivotal)
    @pivotal_model.should respond_to(:pivotal=)
    @pivotal_model.should respond_to(:pivotal_id)
    @pivotal_model.should respond_to(:pivotal_id=)
  end
end

Now that our ActiveRecord extension is well tested, how do we make sure that our two models are actually using it? One technique is to check that each model responds to the specific methods added by our extension:

#spec/models/lab_spec.rb
describe Lab do
  ...
  it "should belong_to_pivotal" do
    @lab.should respond_to(:pivotal)
    @lab.should respond_to(:pivotal=)
    @lab.should respond_to(:pivotal_id)
    @lab.should respond_to(:pivotal_id=)
    @lab.should respond_to(:name)
    @lab.should respond_to(:name=)
    @lab.should respond_to(:nickname)
  end
  ...

This does not feel very satisfying. We are duplicating some of the tests from belongs_to_pivotal_spec.rb and not verifying that we are getting the validations. A crazy coincidence could result in these methods all being defined without actually using our extension.

Another technique, though some would call it a hack, is to provide a hook within the extension itself so we can check for it later:

# lib/belongs_to_pivotal.rb
module BelongsToPivotal
  ...
  module ClassMethods
    ...
    # We can check this to see if a model uses this extension
    def belongs_to_pivotal?
      self.included_modules.include?(BelongsToPivotalInstanceMethods)
    end
  end
  ...
end

Let’s update belongs_to_pivotal_spec.rb to test this method:

# spec/lib/belongs_to_pivotal_spec.rb
describe BelongsToPivotal do
  before(:all) do
    # Using acts_as_fu to create a model specifically for our extension
    build_model :belongs_to_pivotal_models do
      ...
    end

    # Create a model that does not use our extension
    build_model :never_belongs_to_pivotal_models do
      # do nothing
    end
  end
  ...
  it "should know if it belongs_to_pivotal" do
    BelongsToPivotalModel.belongs_to_pivotal?.should be_true
    NeverBelongsToPivotalModel.belongs_to_pivotal?.should be_false
  end
end

#spec/models/lab_spec.rb
describe Lab do
  it "should belong_to_pivotal" do
    Lab.belongs_to_pivotal?.should be_true
  end
end

#spec/models/pivot_spec.rb
describe Pivot do
  it "should belong to Pivotal" do
    Pivot.belongs_to_pivotal?.should be_true
  end
end

Using RSpec Shared Behaviors

How much further can we go? Notice that our two Model specs are 5 whole lines long! Unacceptable! All kidding aside, we can DRY this up just a bit more by using RSpec’s shared behaviors.

In spec/spec_helper.rb

# spec/spec_helper.rb
describe 'it belongs to pivotal', :shared => true do
  it "should belongs_to_pivotal" do
    described_class.belongs_to_pivotal?.should be_true
  end
end

Now we can use this shared behavior in our specs:

#spec/models/lab_spec.rb
describe Lab do
  it_should_behave_like "it belongs to pivotal"
end

#spec/models/pivot_spec.rb
describe Pivot do
  it_should_behave_like "it belongs to pivotal"
end

I hope that these techniques are helpful. Feel free to post your own!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Standup – 03/18/2009

Pivotal Labs
Wednesday, March 18, 2009

Help

None today

Interesting

Windows Service Tip – If you’re using Win32Utils and your process keeps dying, make sure you’ve put any code that takes more than 2 seconds into Daemon#service_init. See the Daemon documentation for more info.

Tech Talk on Compass – Chris Eppstein is giving a talk on Compass today at 12:30pm. If you can’t make it today, you can catch it in a few weeks on the Talks page.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Standup – 03/17/2009

Pivotal Labs
Tuesday, March 17, 2009

Help

Windows Service – A project was trying to make a daemon for windows using the Ruby Win32Utils. It didn’t work. The solution they came up with was to just run the thing they need in a command window and reboot the EC2 instance its on if it dies.
Some suggestions for how to run a real service should it prove necessary included:

  • FireDaemon
  • Using visual studio

Ajax chat feature – Implemented as a first pass with a polling system, does anyone know a better way?
Suggestions included:

  • Comet with Rack
  • Juggernaut

Interesting

Rails 2.3 is out! Excitement abounds!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Avoid collisions by naming asset packages

Pivotal Labs
Monday, March 16, 2009

Rails has a handy feature to easily package multiple CSS or JavaScript files into a single asset. You can use the :cache option with stylesheet_link_tag or javascript_include_tag to bundle several files into a single file (requires config.action_controller.perform_caching to be set to true). This is good because it reduces page download times by eliminating the latency from multiple requests, among other things.

For example:

<%= stylesheet_link_tag "main", "nav", "blog" %>

The above snippet creates the following HTML:

<link href="/stylesheets/main.css?1234567890&48a6bc" media="screen" rel="stylesheet" type="text/css" />
<link href="/stylesheets/nav.css?1234567890&48a6bc" media="screen" rel="stylesheet" type="text/css" />
<link href="/stylesheets/blog.css?1234567890&48a6bc" media="screen" rel="stylesheet" type="text/css" />

But this:

<%= stylesheet_link_tag "main", "nav", "blog", :cache => true %>

produces:

<link href="/stylesheets/all.css?1234567890&48a6bc" media="screen" rel="stylesheet" type="text/css" />

Using the :cache => true option packaged all those files into a single one, and generated only a single link tag to use it on the page. However, this is not exactly what you want to do. Consider this…

views/layouts/application.html.erb:

<%= stylesheet_link_tag "main", "nav", "blog", :cache => true %>

views/layouts/admin.html.erb:

<%= stylesheet_link_tag "main", "nav", "admin", :cache => true %>

It makes total sense to want to create several bundled packages of the same kind of asset. In this example, an application may have a generic user style, and a different set of styles for the admin console. But using :cache => true will get you in trouble, since each layout will try to generate an all.css file with its own set of css files. That’s why you should always use a string for the option value to give a particular name to the package file.

views/layouts/application.html.erb:

<%= stylesheet_link_tag "main", "nav", "blog", :cache => "blog_all" %>

views/layouts/admin.html.erb:

<%= stylesheet_link_tag "main", "nav", "admin", :cache => "admin_all" %>

That creates a link like:

<link href="/stylesheets/admin_all.css?1234567890&48a6bc" media="screen" rel="stylesheet" type="text/css" />

that links to blog_all.css or admin_all.css

I’ve taken to naming packages with a name like LAYOUT_all.css (where LAYOUT is the name of the layout template) to make it easy to tell what’s going on.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Best Buy Remix @ SXSW

Pivotal Labs
Saturday, March 14, 2009

I’m here as part of the Best Buy Remix crew, hanging out in Mashery’s Circus Mashimus all weekend. Come by, have a beer, and check out Remix and other interesting API stuff if you’re at SXSW.

We’re in a room near the front of the convention center, not far from the Pepsi booth.

-Steve

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Mike Grafton

Standup 3/13/2009

Mike Grafton
Friday, March 13, 2009

Help

What is better for optimizing the load time of pages on the iPhone: minifying or packing your javascript?

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

New York Standup 03/12/2009

Pivotal Labs
Friday, March 13, 2009

Interesting

  • Ben noted, you can give your Ruby command-line tools some (RDoc::)usage
  • Rails Boost, where one may “generate a … bootstrapped Rails app”, now includes fixjour as an option to the generator
  • Joe and Ryan recently created a Pivotal fork of Pat‘s acts_as_fu to get it to play nicely with your application and its database connections
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Mike Grafton

Standup 3/12/2009

Mike Grafton
Thursday, March 12, 2009

Interesting

  • Selenium removes the If-Modified-Since header. This gets in your way if you are testing ETags.

  • Speaking of ETags – they are awesome. See Ryan Daigle’s article for code.

Help

Just how slow is Mongrel when serving static images?

The answer is – pretty slow. The reason for the question was that Google’s crawler tends to keep a single connection open and fire lots and lots of http requests over a period of several hours. With a standard Nginx/Mongrel setup, this would tie up a mongrel for this entire period.

A proposed solution to this problem is to use HAProxy between Nginx and Mongrel.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (780)
  • rails (113)
  • testing (88)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (55)
  • techtalk (44)
  • rspec (38)
  • ironblogger (32)
  • productivity (30)
  • activerecord (29)
  • gogaruco (29)
  • git (28)
  • nyc (27)
  • rubymine (26)
  • bloggerdome (23)
  • mobile (22)
  • process (21)
  • pivotal tracker (20)
  • cucumber (20)
  • jasmine (19)
  • design (18)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • palm (16)
  • "soft" ware (16)
  • fun (15)
  • tracker ecosystem (15)
  • ci (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • bdd (14)
  • gem (13)
  • css (13)
  • tdd (13)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • api (10)
Subscribe to Community Feed
  1. ←
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. →
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >