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: August 2009

Adam Milligan

Little, shiny robots

Adam Milligan
Saturday, August 29, 2009

One of my favorite computer games when I was growing up was Robot Odyssey; I imagine it will come as a surprise to no one that I was a nerdy kid. This article is a little bit of a tribute to that game, and the coolness of solving complex problems with a handful of simple concepts combined in clever ways.

Imagine you want to write a web-based game that involves robots. The robots in your game are a bit like the robots in Robot Odyssey: you program them with a list of simple instructions and when you turn them on they follow those instructions faithfully. Let’s say, for the sake of argument, that your robots can Walk Forward, Turn Left, Turn Right, Jump, and Beep.

You model each of the moves your Robot can make as an Action, and a Program as a list of Actions. Since each Robot can execute any number of Actions as part of its Program, and any number of Robots can execute Actions, you join Programs and Actions through the Instructions table. Like so:

class Robot < ActiveRecord::Base
  has_one :program
end

class Program < ActiveRecord::Base
  belongs_to :robot
  has_many :instructions
  has_many :actions, :through => :instructions
end

You build a whiz-bang interface for programming each Robot using JavaScript, which will PUT a collection of Action IDs to programs#update (each Robot creates a blank program on initialization, so no need for programs#create).

This seems pretty straightforward, doesn’t it? Unfortunately, it won’t work; the update will fail to reliably record the uploaded Actions. Here are some examples:

@program.action_ids  # []
@program.action_ids = [1, 4] # Forward, Jump

@program.action_ids # [1, 4]
@program.action_ids = [3, 1, 2, 5] # Right, Forward, Left, Beep

@program.action_ids # [3, 1, 2, 5]
@program.action_ids = [5, 5, 5] # Beep, Beep, Beep

@program.action_ids # [5]   ?????

What happened here? The problem lies with the way ActiveRecord collection associations update themselves. Here is the interesting code (slightly paraphrased) from association_collection.rb:

# Replace this collection with +other_array+
# This will perform a diff and delete/add only records that have changed.
def replace(other_array)
  ...
  delete(@target.select { |v| !other_array.include?(v) })
  concat(other_array.select { |v| !@target.include?(v) })
end

As you can see (or you could just read the comment), this only adds an element to a collection if that element isn’t already in the collection (and it acts similarly for deletion). Sadly, it doesn’t take into account how many times an element appears. So, above, when I tried set the action_ids to [5, 5, 5] it saw that the action_ids collection already contained that ID and moved on. If I had set the action_ids collection to [5, 5, 5] when it already contained [1, 4], the result would have been the expected [5, 5, 5].

Now, I’m on the fence with regard to whether I consider this a bug or just a somewhat inconsistent, but expected, behavior. To begin with, the fix is annoyingly nontrivial, and would potentially have a noticeable performance impact. Far more importantly, I’m not sure how often this might reasonably cause a real problem. In the case of your Robots game, you’d probably care quite a lot about what order your Robot executes its Actions in, so you’d likely have a position column, or something similar, on the instructions table. Given that, you’d probably update the Program by sending in a list of nested attributes which would create Instructions, each with the correct position and associated to the correct Action.

Even so, this is behavior worth knowing about. In the infinitude of possible update scenarios someone will want to update their HMT associations this way. I know I initially wrote code to do this as a temporary experiment, and ended up spending the rest of the day trying to figure out why my updates did the wrong thing a small percentage of the time.

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

Shared Behaviors in Screw.Unit or how to DRY up your javascript specs

Pivotal Labs
Thursday, August 27, 2009

The project that I’m working on is using Screw.Unit for Javascript testing. We recently ran into a case where we found ourselves copying and pasting some code. We wanted to DRY up our specs and found a neat way to do it that I figured I’d share with everyone. Here’s a really simple example to demonstrate how we did it.

Given a cat model that keeps track of the number of lives the cat has left:

function Cat() {
    var lives = 9;
    this.die = function(num) {
        lives = lives - 1
    };
    this.lives = function(){
        return lives;
    };
    this.isDead = function() {
        return lives <= 0
    };
}

Lets make up a spec that checks that isDead works for some values of lives:

Screw.Unit(function() {
    describe('Cat', function() {
        var cat;
        describe('isDead', function(){
            var shouldNotBeDeadBehavior = function(num){
                describe("when the cat has " + num + " lives left", function(){
                    it("it should not be dead", function(){
                        cat = new Cat({lives: num});
                        expect(cat.isDead()).to(be_false);
                    });
                });
            }

            for (i=3;i>0;i--){
                shouldNotBeDeadBehavior.call(Screw.Specification, i);
            }

            describe("when the cat has 0 lives left", function(){
                it("it should be dead", function(){
                    cat = new Cat({lives: 0});
                    expect(cat.isDead()).to(be_true);
                });
            });
        });

    });
});

The neat part in here is the line:

shouldNotBeDeadBehavior.call(Screw.Specification, i);

If you’re not familiar with it, the call function in javascript allows you to define what “this” is for that function call. By calling our shared behavior with Screw.Specification, we’re saying that we want to execute this function within the context of the Screw.Unit Specifications. This lets us execute our specs as though they were written in various places. The test results from this spec look like this
screw unit screenshot

This is one way to DRY up some of your Screw.Unit specs. If you find yourself copying and pasting code, consider refactoring the spec out into a shared behavior instead.

Have other good ways to clean up Srew.Unit specs? Share them in the commments!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
David Stevenson

Sandup for 08/27/2009: Single Table Inheritance and the becomes method

David Stevenson
Thursday, August 27, 2009

Interesting Things

  • Be careful when extending classes from structs. Their superclasses are essentially anonymous classes, so reopening them can be difficult. If you attempt to reopen them by extending them from “the same” struct, it will actually be a different anonymous class.
  • Ever had an STI model but wanted the views and controllers to pretend like it all extended the base class? You can have rails change the params[] namespace that it uses like so:

    form_for :user, @admin_user, :url => user_path(@admin_user)

Or you can be super-cool and use polymorphic routes:

form_for @admin_user.becomes(User)

The becomes method is part of ActiveRecord, and it actually creates a 2nd copy of the object with the same attributes and a different class (shallow copy). Due to this implementation, it has limitations so use it carefully.

  • Upgrading to rails 2.3.3 breaks HopToad. This is related to filter_parameter_logging, and it’s technically rails’ fault. It has been fixed in 2.3.3 stable (which I assume will be released as 2.3.4). You can also fix it yourself with a one-line-patch. Personally, I’d wait to upgrade till 2.3.4 comes out.
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

Desert 0.5.2 Released

Joe Moore
Thursday, August 27, 2009

Desert 0.5.2 focuses on speed improvements and bug bixes.

Pivotal Tracker Stories:

  • “Fix mailer templates — very slow with Desert”: ActionMailer::Base.view_paths are now cached. This speeds up ActionMailer tests/specs.
  • “Plugin schema migrations should successfully convert from the ‘old’ scheme to the ‘new’ scheme”: Pulled cauta/desert at master and fixed specs.
  • “Add sweepers to Desert’s load path”: Pulled willcodeforfoo/desert at master and fixed specs.
  • “Specs should handle Rails version incompatibilities”
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Chad Woolley

[ANN] GemInstaller 0.5.3 Released

Chad Woolley
Tuesday, August 25, 2009

This fixes several bugs that people have complained about for quite a while. Please let me know if anything is broken.


GemInstaller 0.5.3 has been released!

GemInstaller

  • by Chad Woolley
  • http://geminstaller.rubyforge.org

CHANGES

  • 0.5.3 / 2009-08-25
  • Many long overdue bugfixes and patches, see
    http://tinyurl.com/geminstaller-0-5-3-release for details.
  • Thanks to Greg Fitzgerald, Britt Crawford, John Trupiano, Gabriel
    Gironda, and Eric Hodel for patches and assistance.
  • Issues with case statement under Ruby 1.9
  • GemInstaller cannot distinguish between gems that have the ame name
    but capitalized differently.
  • add ./ci as default location for config file
  • Disable GemInstaller install in default rails preinitializer.rb, but
    fork if it is used
  • autogem() fails when run for newly-installed gem
  • Sometimes installing fails due to RubyGems cache not being cleared
    between multiple API calls

DESCRIPTION

Automated Gem installation, activation, and much more!

FEATURES

GemInstaller provides automated installation, loading and activation
of RubyGems. It uses a simple YAML config file to:

  • Automatically install the correct versions of all required gems
    wherever your app runs.
  • Automatically ensure installed gems and versions are consistent
    across multiple applications, machines, platforms, and environments
  • Automatically activate correct versions of gems on the ruby load
    path when your app runs (’require_gem’/'gem’)
  • Automatically reinstall missing dependency gems (built in to RubyGems > 1.0)
  • Automatically detect correct platform to install for multi-platform
    gems (built in to RubyGems > 1.0)
  • Print YAML for “rogue gems” which are not specified in the current
    config, to easily bootstrap your config file, or find gems that were
    manually installed without GemInstaller.
  • Allow for common configs to be reused across projects or
    environments by supporting multiple config files, including common
    config file snippets, and defaults with overrides.
  • Allow for dynamic selection of gems, versions, and platforms to be
    used based on environment vars or any other logic.
  • Avoid the “works on demo, breaks on production” syndrome
  • Find lost socks.

Quick Start

See http://geminstaller.rubyforge.org/documentation/index.html

INSTALL

  • [sudo] gem install geminstaller
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Christian Sepulveda

New Version of Tweed: 0.9.14 Now Available

Christian Sepulveda
Tuesday, August 25, 2009

0.9.14 of Tweed is now available in the App Catalog.

Bugs:

  • timeout didn’t actually timeout
  • re-launch app created new card

Features/Change:

  • loading spinner/scrim no longer full screen — allows for user interaction/cancel if Twitter is slow
  • option to disable links in tweet list (still available from tweet popup)
  • ability to re-tweet own tweets
  • Nearby timeline (can search within nearby, configure Nearby radius)
  • Photo View
  • Support for French (for Canadian users)
  • preference to open multiple cards per account

Photo Viewing

  • if tweet contains photo link, icon indicator will be present
  • can view photos within Tweed, no need for external browser load
  • twitpic.com, tweetphoto.com (and pic.gd), yFrog.com and twitgoo.com supported

Multiple Cards

If enabled, you can tap Open from App Menu to open another card for an account. (This allows multiple timelines to be simultaneously open, similar to TweetDeck)

Tweed
Tweed

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
David Stevenson

Standup 08/25/2009: Mechanical turk for data matching

David Stevenson
Tuesday, August 25, 2009

Interesting Things

  • One of our projects is using mechanical turk to match and normalize their ugly data. After building some automatic matching with little success, the group is simply using the template web interface. The task can be trivially decomposed into tiny tasks, and accuracy does not need to be 100% perfect, so it’s a great fit. They only spent a few hours and have results already!
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
David Stevenson

Rails finders are smart about Single Table Inheritance hierarchies

David Stevenson
Tuesday, August 25, 2009

When creating associations to classes that use Single Table Inheritance (STI), rails is smarter about the hierarchy than I would have expected. A call to find on a base class yields a query that does not filter on type. But what about a call to find on a subclass? Instead of making a single filter on type, rails finds all the subclasses of that subclass and creates an “OR” expression in the “WHERE” clause.

Generated SQL respects class heirarchy

# ActiveRecord::Base < User < Admin < SuperAdmin
something.admin    # has_one :admin

Generates the following SQL:

# SELECT * FROM users WHERE (type = 'Admin' OR type = 'SuperAdmin')

When it doesn’t work

Since it relies on the class hierarchy, the query is only accurate if every subclass has been loaded. If class preloading is off, for example, very weird things can happen. The query will depend non-deterministically on which subclasses have been loaded.

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

BizConf

Pivotal Labs
Monday, August 24, 2009

I just spent a wonderful weekend with 75 of the brightest folks I know in the Ruby community. My hat’s off to Obie and the Hashrocket crew for putting together a really great, intimate conference in a beautiful location. It’s refreshing to really have to struggle to choose which talk to attend from so many choices at each session. I know too many choices are a Bad Thing™, but the format made for great small sessions, and a wonderful thing happened: Everyone got to really meet and get to know everyone.

Among many others, I had the pleasure of meeting CJ Kihlbom, who nails a lot of why these conferences are so important in his post, The Business Value of Conferences.

It was really pleasant to present to a community of business leaders who understand the value of agile, and who are serious practitioners in their own practices.

A lot of you have asked for the slides from my talk, Agile, Rails and the Cloud, so I’ve posted them here.

Those of you who thought about coming but didn’t really missed out. Come next year. You’ll be glad you did.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
David Stevenson

Standup 08/24/2009: How do you redefine css classes in javascript?

David Stevenson
Monday, August 24, 2009

Ask for Help

“How do I redefine a CSS class in javascript”

You can create a new <style> element and append it to the head. This should probably be avoided if you can help it.

If you simply want to toggle between 2 states, consider putting both sets of rules in the CSS and toggling a class on the body or other container.

*”Should I use BOSH for XMPP on the iPhone?”

Probably not. If you have a long-running low latency XMPP connection, you’ll probably want to use a socket from the CFNetwork package. That’s the most we know about iPhone development right now.

Interesting Things

  • New tracker updates with better burndown charting!
  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (781)
  • 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 (21)
  • cucumber (20)
  • design (19)
  • jasmine (19)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • tracker ecosystem (16)
  • palm (16)
  • "soft" ware (16)
  • fun (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. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. →
  • 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 >