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
  • Tools
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker

Monthly Archives: January 2010

DelegateClass rocks my world

Jeff Dean
Wednesday, January 20, 2010

If you notice that your classes have more than one responsibility, you can easily split them up into multiple, more cohesive classes using Ruby’s DelegateClass.

Let’s say that you have a Person class, and that people in your system can sell things and/or publish articles. You can’t use subclasses, because a person can be an author and a seller at the same time. At first you might start with something like this:

class Person < ActiveRecord::Base
  has_many :articles
  has_many :comments, :through => :articles
  has_many :items
  has_many :transactions

  def is_seller?
    items.present?
  end

  def amount_owed
    # => some fancy math
  end

  def is_author?
    articles.present?
  end

  def can_post_article_to_homepage?
    # => some fancy permissions
  end
end

This might seem OK at first. You might say “Well, it’s the responsibility of Person to know about both the items they’ve sold, as well as the articles they’ve published.” I say that’s hogwash.

Imagine a new requirement: People can be buyers as well as sellers / authors

The way this is setup, you’d have to re-open the person class and add things like:

class Person < ActiveRecord::Base
  #  ...
  has_many :purchased_items
  has_many :purchased_transactions

  def is_buyer?
    purchased_items.present?
  end

  # ...
end

The first thing to notice is that this violates the open / closed principle (open for extension but closed to modification) because you’ve modified the class. Next, you’ll notice that naming can get very confusing in places where you’ve got a person who is on both sides of a transaction. Finally, this code has poor separation of concerns.

Imagine another new requirement: The Person class is now driven by an xml web service, or a non-ActiveRecord class

Now that you can’t use ActiveRecord and your has_many code doesn’t work, you have to rewrite all kinds of code, and feature development grinds to a halt.

Enter DelegateClass

Let’s say instead of modifying Person, you extended Person by creating delegate classes, like so:

class Person < ActiveRecord::Base
end

class Seller < DelegateClass(Person)
  delegate :id, :to => :__getobj__

  def items
    Item.for_seller_id(id)
  end

  def transactions
    Transaction.for_seller_id(id)
  end

  def is_seller?
    items.present?
  end

  def amount_owed
    # => some fancy math
  end
end

class Author < DelegateClass(Person)
  delegate :id, :to => :__getobj__

  def articles
    Article.for_author_id(id)
  end

  def comments
    Comment.for_author_id(id)
  end

  def is_author?
    articles.present?
  end

  def can_post_article_to_homepage?
    # => some fancy permissions
  end
end

The calls to this involve one extra step, so instead of:

person = Person.find(1)
person.items

You add:

person = Person.find(1)
seller = Seller.new(person)
seller.items
seller.first_name # => calls person.first_name

Now that this is in place, adding a Buyer is as simple as creating a Buyer delegate class like so:

class Buyer < DelegateClass(Person)
  delegate :id, :to => :__getobj__

  def items
    Item.for_buyer_id(id)
  end

  def is_buyer?
    purchased_items.present?
  end
end

Now when you need to make Person driven by something other than ActiveRecord::Base, your delegate classes don’t change at all.

Delegate classes aren’t the solution to every problem, and certain behavior, such as #reload can be very confusing at first:

person = Person.find(1)
seller = Seller.new(person)
seller.class # => Seller
seller.reload.class # => Person

Another gotcha is that id doesn’t delegate by default, so you have to add the following line to make sure you get the ActiveRecord id:

  delegate :id, :to => :__getobj__

However, delegate classes can go a long way to making your code more supple.

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

NYC Tech Talk: 19 Jan 2010

Pivotal Labs
Tuesday, January 19, 2010

Pivotal NYC was lucky enough to have Ben Stein in the
office to give a beta presentation entitled “Beyond the Hype: What it
Really Takes to Build a Technology Business on the Cloud”. Ben has
built his own successful startup, Mobile Commons,
using some of the principles he discussed.

Boiled down to its essence, Ben’s theme was (with apologies to Chris
Carter): Trust No One.

More specifically, Ben talked about ways to build your business and
technology to better handle the inevitable failures of your vendors,
partners and infrastructure providers.

Technical highlights from Ben’s talk included a recipe for
constructing a vendor-agnostic library (heretofore known as the “Stein
Stack”), and actionable rules-of-thumb to make API consumers and
producers more robust and debuggable.

If you’re interested in attending a future Pivotal NYC Tech Talk,
please subscribe to the mailing list.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Colin Shield

Standup 1/14 actionmailer content type issue

Colin Shield
Monday, January 18, 2010

We were bitten by a Rails 2.3 bug related to ActionMailer today. It took us a good part of the day to hunt down due to the fact that it only happened in production and even then only occasionally.
Basically ActionMailer occasionally sends your multipart emails as text/plain with html content.

Here’s the ticket we found:

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Dan Podsedly

Pivotal Tracker API – new version (V3) to be released on Jan. 23

Dan Podsedly
Sunday, January 17, 2010

We’re planning a Pivotal Tracker upgrade on Jan 23. As part of this release, we will be introducing a new API version (V3), which will make it easier to follow project activity, allow you to add file attachments, move (re-prioritize) stories, associate source commits with stories, and more.

The current API version (V2) will not change, but V1 will no longer work. If you’re still using V1, you will need to change your client code to use V2 or V3.

To find out what’s changing in V3, continue reading.

How to tell what version of the API you’re using?

The API version identifier is part of the request URLs. For example, this is a V2 request:


http://www.pivotaltracker.com/services/v2/projects

What’s New or Changed in V3

Activity

The response for the activity queries will change significantly. It will include a version # (to allow you to keep track of unique events and their order), event type, when the activity occurred (with time zone), and a nested element with all story attributes that changed as part of the activity. Example:

<activities type="array">
  <activity>
    <id type="integer">1031</id>
    <version type="integer">175</version>
    <event_type>story_update</event_type>
    <occurred_at type="datetime">2009/12/14 14:12:09 PST</occurred_at>
    <author>James Kirk</author>
    <project_id type="integer">26</project_id>
    <description>James Kirk accepted &quot;More power to shields&quot;</description>
    <stories>
      <story>
        <id type="integer">109</id>
        <url>https://www.pivotaltracker.com/services/v3/projects/26/stories/109</url>
        <accepted_at type="datetime">2009/12/14 22:12:09 UTC</accepted_at>
        <current_state>accepted</current_state>
      </story>
    </stories>
  </activity>
</activities>

You’ll also be able to query for all activity since a particular date or version #, and you limit how many entries to return (up to 100):

curl -H "X-TrackerToken: TOKEN" -X GET http://www.pivotaltracker.com/services/v3/activities?newer_than_version=13
curl -H "X-TrackerToken: TOKEN" -X GET http://www.pivotaltracker.com/services/v3/activities?occurred_since_date=2009/12/14&limit=50

Activity Web Hook

This will allow you to specify a URL per project (in project settings), which Tracker will post story activity to, in the same XML format as above. You’ll be able to “pull” story activity out of Tracker via normal API GET requests, or have it POSTed to your client as it occurs via the activity web hook.

Projects

The project XML response will include the current and initial velocity, last activity date, and a list of all labels in the project.

Example:

<project>
  <id>1</id>
  <name>Sample Project</name>
  <iteration_length type="integer">2</iteration_length>
  <week_start_day>Monday</week_start_day>
  <point_scale>0,1,2,3</point_scale>
  <account>James Kirks Account</account>
  <velocity_scheme>Average of 4 iterations</velocity_scheme>
  <initial_velocity>10</initial_velocity>
  <last_activity_at type="datetime">2010/01/16 17:39:10 CST</last_activity_at>
  <number_of_done_iterations_to_show>12</number_of_done_iterations_to_show>
  <labels>shields,transporter</labels>
  <allow_attachments>true</allow_attachments>
  <public>false</public>
  <use_https>true</use_https>
  <bugs_and_chores_are_estimatable>false</bugs_and_chores_are_estimatable>
  <commit_mode>false</commit_mode>
  <memberships>
    <membership>
      <id>1006</id>
      <person>
        <email>kirkybaby@earth.ufp</email>
        <name>James T. Kirk</name>
        <initials>JTK</initials>
      </person>
      <role>Owner</role>
    </membership>
  </memberships>
</project>

When creating a project via the API, the user represented by the API token will be made an owner of that project by default. To leave the new project without an owner (because your client is acting on behalf of a different user, for example), you’ll need to include <no_owner type="boolean">true</no_owner> in the post data.

Stories

You’ll be able to move (re-prioritize) stories via the API. To move a story to after another story:

curl -H "X-TrackerToken: TOKEN"
    -X POST http://www.pivotaltracker.com/services/v3/projects/PROJECT_ID/stories/STORY_ID/moves?move[move]=after&move[target]=TARGET_STORY_ID"

Or, move it before a story:

curl -H "X-TrackerToken: TOKEN"
    -X POST http://www.pivotaltracker.com/services/v3/projects/PROJECT_ID/stories/STORY_ID/moves?move[move]=before&move[target]=TARGET_STORY_ID

As part of the new integrations feature (watch for more on that later), you’ll be able to associate a story with a ticket or issue in an external system, such as Lighthouse or JIRA. You’ll need to specify a ticket/issue ID, and optionally which specific integration to use (a project may be set up with multiple):

curl -H "X-TrackerToken: TOKEN" -H "Content-type: application/xml"
    -d "<story><lighthouse_id>54</lighthouse_id></story>"
    -X PUT http://www.pivotaltracker.com/services/v3/projects/PROJECT_ID/stories/STORY_ID"

Stories that are linked to a ticket or issue in an external system (for example JIRA or Lighthouse) will include the external ID as an attribute, as well as the URL to the linked ticket/issue:

<story>
  <id type="integer">STORY_ID</id>
  <story_type>feature</story_type>
  <url>http://www.pivotaltracker.com/story/show/STORY_ID</url>
  <estimate type="integer">1</estimate>
  <current_state>unstarted</current_state>
  <lighthouse_id>43</lighthouse_id>
  <lighthouse_url>http://mylighthouseapp.com/projects/100/tickets/43</lighthouse_url>
  <name>More power to shields</name>
  <requested_by>James Kirk</requested_by>
  <created_at type="datetime">2008/12/10 00:00:00 UTC</created_at>
</story>

File attachments

Here’s how you’ll be able to upload a file attachment to a story:

curl -H "X-TrackerToken: TOKEN" -X POST -F Filedata=@/path/to/file http://www.pivotaltracker.com/services/v3/projects/PROJECT_ID/stories/STORY_ID/attachments

The story response will include information about file attachments in this nested XML element:

<attachments type="array">
  <attachment>
    <id type="integer">17</id>
    <filename>Picture_36.png</filename>
    <description></description>
    <uploaded_by>Rob</uploaded_by>
    <uploaded_at type="datetime">2010/01/17 14:57:57 CST</uploaded_at>
  </attachment>
</attachments>

Note: Attachments in the story response XML will most likely not include a URL to the actual AWS S3 file, since these URLs are only valid temporarily. You’ll need to make a separate API call (details TBD) to get the S3 URL for a given story file attachment.

Source Control Post Commit Hooks

This will allow you to set up post-commit hooks in git, github, subversion, etc., to link commits to stories (and optionally mark them as finished) based on this message syntax:

“Torpedoes now sufficiently powered [fixes #123456]“.

Curl example, of what you might do in a custom post-commit hook script for subversion or git:

curl -H "X-TrackerToken: TOKEN" -H "Content-type: application/xml"
    -d "<source_commit><message>$MESSAGE</message><author>$AUTHOR</author><commit_id>$REV</commit_id><url>http://trac.yourcompany.com/browser/?rev=$REV</url></source_commit>"
    -X POST http://www.pivotaltracker.com/services/v3/source_commits

Stories will show associated source commits as comments, with a link to the commit if you include a URL in the post body:

Commit comment

Github Support

The V3 version of the API will also support native Github post-commit hooks, allowing you to configure your Github repo to send commit information directly to Tracker, along with the [fixes #12345] message syntax.

The format of the Github post-commit request will be:


https://www.pivotaltracker.com/services/v3/github_commits?token=API_TOKEN_OF_THE_USER_TO_ATTRIBUTE_ACTIONS_TO

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Colin Shield

Standup 1/13 Successful completion with SQS Internal Error

Colin Shield
Thursday, January 14, 2010

The RightScale SQS gem returned an exception from SQS multiple times, including retries. Not an unusual event. This could have been caused by the SQS service being unavailable. However, the team noticed that despite the failure the message was actually successfully added to the queue and processed as normal.

ActiveSupport logger appears to open the default ruby logger and remove everything except the basic log message passed through. This is done for all subsequent uses of the logger. Perhaps this is done so that the log message could be passed to a syslog service which will add timestamps.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Colin Shield

Standup Jan 12 2010 DateJS timestring parsing

Colin Shield
Tuesday, January 12, 2010

Whilst trying to parse differently formatted date strings from rss feeds a pivot found that date.now is overridden by DateJS to return a new date.
There was a suggestion, that later proved useful, to use google’s rss reader to first clean up the different rss feeds to ensure that they all can be parsed in much the same way.

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

NYC Standup Round-up for Dec 28th – Jan 8th

Pivotal Labs
Monday, January 11, 2010

Points of Interest

  • Following a recipe from Dan Chak‘s Enterprise
    Rails
    , we came across code following this
    pattern:

    class Thing
      def foo
        def bar(args)
          # some code
        end
        # some code that calls bar()
      end
    end
    

    The structure of this code suggests that bar is scoped only within
    the context of foo. But alas, that is not the case. Ruby simply
    defines Thing#bar the first time foo is called, analogous to
    define_method :bar. Misleading syntax, for sure.

  • Ryan Davis‘s Flay is awesome. If you’re not
    familiar with it, Flay parses your Ruby and compares subtrees with
    each other to find where code has been duplicated (or nearly so).
    Run on a codebase of over 20,000 lines of Ruby, Flay was able to
    quickly indicate places where we had duplicate code lying around, as
    well as many likely targets for refactoring work. We’ve found it to
    be helpful in keeping code DRY.

  • This looks interesting: MagicPrefs gives you gestures
    for your Magic Mouse (Yes, Pivotal NYC has Magic Mice, as well as
    27″ iMacs. Apply today!).

  • In Snow Leopard, mapping CapsLock => Ctrl when two keyboards are
    plugged in is problematic. You can do it by plugging in one keyboard
    at a time and mapping each one individually. If anyone knows the
    story behind why this is, or how to deal more easily, please comment!

  • Another interesting link: Open Frameworks is a
    “creative coding” toolkit (like Processing) that’s
    implemented in C++. Why ask why?

  • Another lesson learned the hard way: starting up a bunch of leopard
    machines at the same time wreaks havoc on the network for a few
    minutes. Packet storm, dropped packets, etc. Is Bonjour to blame?
    Inquiring minds want to know.

  • If you’re trying to stub a subclass of ActionMailer::Base with
    Double Ruby (also known as RR), and you’re having issues, try
    stubbing the method on ActionMailer::Base directly. There’s some
    weirdness there with method_missing in Rails 2.3.

Help Wanted

  • Does anyone know how to make command-1 through command-9 switch
    tabs inside of Term.app? By default, these keys are bound to
    switching windows, and we’d love to be able to do this on tabs
    instead.

  • Rails’s select_tag(..., :multiple => true) option doesn’t properly
    set selected on the generated options. This appears to be a boog,
    and anyone who’s interested in helping a Pivot write a patch, please
    comment below!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Will Read

Agile Pyramids

Will Read
Sunday, January 10, 2010

I recently returned from a trip to Egypt with Pivotal’s own Lara Owen. Like all good tourists, we went and saw the Pyramids of Giza. Like far fewer tourists, we also went to Saqqara to see the Step Pyramid and then on to Dashur, home of the Bent Pyramid and the Red Pyramid.

The Step Pyramid in Saqqara

In the 3rd Dynasty, the Step Pyramid is constructed under the rule of Djoser. The pyramid has entered the market. By the 4th Dynasty, under the rule of Snofru, six steps are no longer enough, users want seven steps and smooth sides.

The Bent Pyramid in Dashur

In an attempt to meet these new market demands, the Bent Pyramid construction begins. Half way through, they realize they made a mistake, and adapt their plan accordingly by changing the angle down from 54° to 45°. The next attempt, still within Snofru’s lifetime, becomes the gold standard of pyramids and is known as the Red Pyramid. With it’s consistent sloping smooth sides and added height, this pyramid was fit for any king.

The Red Pyramid in Dashur

What I thought about, as you might have guessed by now, was the methodology used to construct each of these pyramids. In the case of the Step Pyramid form the 3rd Dynasty, it would be easy to see a king proclaim, “I want this kind of pyramid and I want it ready by the time I die.” And he’ll get something twenty years later that resembles what he wanted twenty years ago.

Snofru had a different plan. He was going to shake up the way people think about royal tombs. He was going to do something no one else had done. This man of foresight said to his people, “We will release early, and we will release often. We will have meetings along the way to reflect on how we have progressed, and we will then plan ahead only as far as is practical, adjusting as we go. Most importantly, we will trust each other such that mistakes can be made without causing great harm to the project.” It was with this spirit that Snofru began work on the Bent Pyramid.

He didn’t know what he was doing, there was no model to copy, no patterns to solve this problem. So he guessed 54°. And they built, and they talked and said “Hey Snofru, this looks great, but it won’t be stable if we continue at this angle.” To which Snofru probably replied, “I will trust your expertise here, can we make the angle less steep and see how that works out?” So 45° it was.

When the finished, they stood back and looked at the work they had done. It was taller, and it did have smooth sides, but it wasn’t quite right. Snofru, being a wise king, knew that this was a great success.

  1. A taller pyramid could indeed be built.
  2. 45° was a sustainable angle
  3. He was still drawing breath and now had the knowledge to fully realize his goal.

Snofru still had plenty of time on his hands, and because he released early instead of letting construction drag on, he had new insight from the retrospective that he wouldn’t possess otherwise. So he built the Red Pyramid with complete success.

Snofru was, in my opinion, the first adopter of Agile principles. His “employees” trusted him enough to be able to push back when the plan needed revising. He trusted them right back not to yank his chain about things being too steep. He made his mistakes up front, which taught him lessons about his domain, and it enabled him to be successful in the long run.

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

Standup 1/7-8: Bundler, MySql vs Sqlite on disk vs Sqlite in-memory

Pivotal Labs
Friday, January 8, 2010

Bundler 0.8.1 is out. There have been some significant changes around how the vendor directory is organized, so you’ll want to rm -rf vendor/gems and re-run bundle.

A team tried swapping in Sqlite to see if it made any difference in test suite runtime. It was actually slightly slower than MySql. In-memory Sqlite didn’t help either.

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

Git and Pairing Statistics

Pivotal Labs
Thursday, January 7, 2010

At stand-up this past Monday after the whirlwind of the holidays, our team anchor encouraged us to pair with somebody we hadn’t paired with much recently. While this was good advice I really didn’t have a clear idea in my mind of how often I’d been pairing with specific developers on the team.

On our project we use Bryan Helmkamp’s pair script to change our git author name to a pair of developers joined with “and.” Since both developer names are included on a commit in a consistent way, I was able to get a vague idea of with whom I’d been paired over the past couple of weeks with a quick git log | grep Pignata | less but I couldn’t really see any patterns from this output.

To better keep track I threw together a quick script to quickly parse out these results and show me at a glance what my pairing habits have been over the past 30 days. Now I can check every couple of weeks to see if I’ve inadvertently become part of a sticky pair or if there’s a developer I haven’t been pairing with often enough:

 jp@populuxe:~/Projects/lorem(master)$ ./pair-stats Habitasse Platea
 Pairing stats for Habitasse Platea since 2009-12-06

 Developer              Days   Commits  %
 ---------------        ----   -------  ---
 Tincidunt Nec          8      55       33%
 Congue vel Mauris      5      40       20%
 Sed Facilisis          4      21       16%
 Etiam Blandit          3      16       12%
 Solo                   1      2        1%
 ...

You can find the script on GitHub.

What tricks does your team have to ensure proper circulation of developer pairs?

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (783)
  • rails (117)
  • testing (90)
  • ruby (86)
  • ruby on rails (71)
  • jobs (62)
  • javascript (59)
  • techtalk (44)
  • ironblogger (42)
  • rspec (39)
  • bloggerdome (34)
  • productivity (34)
  • activerecord (30)
  • rubymine (30)
  • git (29)
  • gogaruco (29)
  • nyc (27)
  • design (24)
  • mobile (23)
  • pivotal tracker (22)
  • process (21)
  • cucumber (21)
  • jasmine (19)
  • ios (18)
  • tracker ecosystem (17)
  • webos (17)
  • objective-c (17)
  • fun (16)
  • android (16)
  • palm (16)
  • ci (16)
  • "soft" ware (16)
  • bdd (15)
  • tdd (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • css (14)
  • gem (13)
  • mouse-free development (12)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • api (12)
  • keyboard (11)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
Subscribe to Community Feed
  1. ←
  2. 1
  3. 2
  4. 3
  5. →
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Tools
  • 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 >