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

Rails requests missing the HTTP body

Adam Milligan
Saturday, August 15, 2009

This is a bug in Rails that quite likely affects you, but which you’ve even more likely never experienced. I’ve posted it here for the benefit of the small number of people who will run into this problem and turn to Google for help.

In short, if you use Mongrel app servers (this may affect Passenger as well, I don’t know), the first HTTP request to your Rails app after you restart your servers, or otherwise reload your environment, will have an empty HTTP body.

I say you’ve likely never experienced this because the majority of HTTP requests to your Rails app are likely GET requests, which always have empty HTTP bodies. After that first request everything will work just fine. Even if you’re unlucky enough to receive a POST or a PUT request containing a body immediately after restart it will only fail once, which you could easily write off an an anomaly. You also won’t see this behavior in your development environment, or any environment in which you use Mongrel as a web server rather than just an app server.

If you’re interested in a patch for the bug, I’ve submitted one to Rails here.

The source of the problem lies in how ActionController initializes itself. In the actionpack gem you’ll find the lib/action_controller/cgi_ext.rb file, which does little more than load the three files in the cgi_ext directory:

require 'action_controller/cgi_ext/stdinput'
require 'action_controller/cgi_ext/query_extension'
require 'action_controller/cgi_ext/cookie'
...

The cgi_ext/query_extension.rb file is the interesting one:

require 'cgi'

class CGI #:nodoc:
  module QueryExtension
    # Remove the old initialize_query method before redefining it.
    remove_method :initialize_query

    # Neuter CGI parameter parsing.
    def initialize_query
      # Fix some strange request environments.
      env_table['REQUEST_METHOD'] ||= 'GET'

      # POST assumes missing Content-Type is application/x-www-form-urlencoded.
      if env_table['CONTENT_TYPE'].blank? && env_table['REQUEST_METHOD'] == 'POST'
        env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
      end

      @cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE'])
      @params = {}
    end
  end
end

This replaces the default #initialize_query method provided by Ruby’s CGI library:

    def initialize_query()
      if ("POST" == env_table['REQUEST_METHOD']) and
         %r|Amultipart/form-data.*boundary="?([^";,]+)"?|n.match(env_table['CONTENT_TYPE'])
        boundary = $1.dup
        @multipart = true
        @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
      else
        @multipart = false
        @params = CGI::parse(
                    case env_table['REQUEST_METHOD']
                    when "GET", "HEAD"
                      if defined?(MOD_RUBY)
                        Apache::request.args or ""
                      else
                        env_table['QUERY_STRING'] or ""
                      end
                    when "POST"
                      stdinput.binmode if defined? stdinput.binmode
# =====>              stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
                    else
                      read_from_cmdline
                    end
                  )
      end

      @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
    end

The interesting line is the one I’ve marked with a comment rocket. Notice how it reads from stdinput; this leaves the read pointer at the end of the input stream. Now look back at the Rails override for this method, and notice how it does not read from stdinput, thus leaving the read pointer at the start of the input stream.

This is all fine and dandy as long as all of the ActionController code loads up and patches the CGI library properly. However, ActionController doesn’t load the cgi_ext.rb file (or its dependencies) until it references either the CgiRequest or CGIHandler classes (which require cgi_process.rb, which require cgi_ext.rb), as part of the first request, which is after the default Ruby CGI library has read the input stream containing the request body. ActionController then tries to read the request body assuming the read pointer is at the start of the stream. Oops. Subsequent requests work fine, because everything has now been loaded.

Finding the source of this bug took some doing (Chris Heisterkamp, aka “The Hammer” and I tracked it down together), but the fix is easy. If you look at the patch you’ll see it’s simply a single require in action_controller.rb. You can achieve the same result by requiring ‘action_controller/cgi_ext’ in an initializer file in your app.

Like many problems, this one should go away in Rails 3. Rails has deprecated use of the CGI library, and the CGI extensions have already been removed from the Rails master branch. However, it’s a real problem now, and will remain so for at least some amount of time.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Glenn Jahnke

Standup 8/13/2009: BART Strike, etc

Glenn Jahnke
Friday, August 14, 2009

Ask for Help

“BART will be striking for an indefinite period of time starting Monday”

Somewhat scary for those of us trying to get into the city… good luck to everyone else in the same predicament.

“Has anyone used geminstaller to install Passenger?”

It appears not.

Interesting

“EngineYard’s Solo offering now has a feature called quickstart”

The Solo Linux OS image which is built to run on Amazon’s EC2 apparently has a new quickstart feature that lets people drop a git repository link into their web interface and self configures things appropriately. Pretty interesting.

“RabbitMQ is a viable option for projects”

RabbitMQ has been vetted and so far has proven to be stable, fast, and reliable, despite a few things we had to iron out first. Pretty cool new technology with some good Ruby integration in the form of the amqp gem. This website had a similar experience with RabbitMQ, AMQP, and Rails.

“Rails code committers inside Pivotal now get t-shirts denoting that fact for their efforts”

Sweet ;).

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Glenn Jahnke

Standup 8/13/2009: Java, Java, Java

Glenn Jahnke
Thursday, August 13, 2009

Despite being a Ruby shop, we do have our hands in some Java issues…

Ask for Help

“Anyone know of a good Rhino Javascript book?” (He was looking for more information on the Javascript implementation using Java and related information)

Nope :(.

“What is the status of finding out how to sandbox Java”

Still in progress.

Interesting

Changing the Java version from 1.5 to 1.6 in Mac OSX is painful.

  • It may be tempting to manually install Java and then just change the link (in some file called “a”), but this is wrong and spits out warnings.

  • Most options can be set using:

    /Applications/Utilities/Java Preferences.app/Contents/MacOS/Java Preferences

and then setting $JAVA_HOME is up to you.

  • Arun Gupta seems to have documented more of the process for more reading and installation details.
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

When 2,147,483,647 just isn't enough.

Pivotal Labs
Wednesday, August 12, 2009

If only I had a paid a little more attention to any of the Facebook is growing more quickly than the Universe is expanding articles of late, I might have caught a bug in my app before I received multiple one star ratings.

That said, live and learn and be sure to create the uid column in your users table as a bigint.

t.integer :uid, :limit => 8

I will never know how many countless Facebookers with excessively large uids tried to play my game in vain, only to watch as MySQL mercilessly truncated their unique identifier to the eighth Mersenne prime.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Glenn Jahnke

Standup 8/12/2009: Using stub with before(:all)

Glenn Jahnke
Wednesday, August 12, 2009

Ask for Help

“My method stub only works on the first RSpec example and clearing in subsequent examples, what’s wrong?”

Using RSpec, it is common to mock out certain aspects of your code to change functionality for testing. This is accomplished using the “stub” method and passing the method name as a symbol.

SomeObject.stub(:some_method_to_modify).and_return do something_else() end

As we like to keep our code nice and DRY, we often pull things into “before” blocks. Unfortunately, this can cause some confusion as

describe "ObjectA" do
    before :all do
        ObjectB.stub!(:some_method_ObjectA_depends_on).and_return(15)
    end

    it "can test something" do
        ...
    end

    it "can test something else" do
        Objectb.some_method_ObjectA_depends_on
    end

end

will have ObjectB.some_method_ObjectA_depends_on actually executing ObjectB’s method instead of stubbing. This is because after each example (”it” block), all stubs are cleared from all objects, leaving the stubs only effective in the first example.

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

Boosting with Acts As Solr

Pivotal Labs
Monday, August 10, 2009

Probably my favorite feature of the Solr full text search engine and the acts_as_solr plugin is a feature called boosting. Boosting is a great tool that gives you the ability to wield some influence over how the results that are returned are going to be ordered. When boosting is applied properly the quality of the search results appears improve dramatically even though the same results are being returned, just in a different order. There are two different kinds of boosting that you need to be aware of: column boosting and document boosting.

Field Boosting

Field or column boosting allows you to specify that if a query matches on a boosted field, give that more weight than usual. In the app I am working on, I added a field boost to the name attribute because I want results that have the query string in the name to appear before those results that have it somewhere in their description or as a tag. Here is an example of how to do a field boost when using acts_as_solr.

acts_as_solr :fields => [{:name => {:boost => 3.0}}, :description, :tags]

Document Boosting

A document boost should be utilized there is a way of quantifying one result as being better than another result, regardless of the query. For example, there are two entries in my database that both have a tag of “twitter client”: Twitterific and Twitterfon. In the iPhone App Store, Twitterfon has a higher popularity rating than Twitterific so I want Twitterfon to appear above Twitterific if someone searches for “twitter client” within the app. To specify document boosting based on the app store popularity field I can pass a Proc object to acts_as_solr (rdoc) and return the member field that holds the popularity rating. A great thing about the Proc object is that I can execute any ruby code inside of it that I want. This is useful if the popularity score is not directly stored in the database and must be calculated on the fly.

acts_as_solr :fields => [:name, :description, :tags],
           :boost => Proc.new { |item| item.popularity_score.to_f }

Closing

If you are using Solr at all it is important to be aware of what boosting can accomplish. When using multiple boosts, finding the right boost values to produce the best search results is a bit of black magic. I have found that after achieving “pretty good” results the law of diminishing returns comes into play and slows down progress. With a single boost it is much easier because there is only one variable in play.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Glenn Jahnke

Standup 8/10/2009: Javascript Date-Time Picker

Glenn Jahnke
Monday, August 10, 2009

Ask for Help

“Does anyone know of the best current Date-Time picker for Javascript?”

*Tim Harper’s Calendar Date Select was recommended for its simplicity and rails integration.

Simply install the gem and add it as a project dependency:

sudo gem install calendar_date_select
config.gem "calendar_date_select"

And insert into your Rails code:

<%= calendar_date_select_includes "red" %>

And voila, you have an intuitive time and date selector:

Date and Time Selector GUIJ

Check out the live Demo.

*Yahoo’s Javascript toolkit was determined to work well for people in need of a date and time selection GUI.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Adam Milligan

The Rails identity crisis

Adam Milligan
Sunday, August 9, 2009

Imagine, if you will, that you’re a bookseller. You sell books. Big books, small books, serious books, silly books; if it’s got pages and a cover you’ll sell it. Times being what they are you’ve decided to harness the power of the intertubes to sell your books (a novel idea; ho ho ho). In fact, you’ve decided to build a website, and to expose an API with which your business partners can sell books through their websites. Huzzah.

As it turns out you’re an accomplished Rails developer as well as a thriving bibliophile, so you get to work. Fortunately, you thought ahead and already have information for all of your books in a database. Being well read as you are, you choose to make a RESTful Books resource to show off your books. Any customer can check out a book of their choosing by navigating their browser thusly:


http://www.amazzahn.com/books/1

Huzzah again. Sort of.

You’ve heard about this SEO thing, and you hate how ugly that URL is, so you override #to_param on your Book model to return a nice looking slug. Now that URL from above looks like this:


http://www.amazzahn.com/books/stickwick-stapers

You go about your business, quite pleased with yourself, until you receive a phone call from one of the business partners who use your API; it seems they can no longer look at books through your web service.

Here’s the problem: they’re using ActiveResource to consume your RESTful interface. To get a catalog of books they call Book#find(:all), which executes a books#index request. This returns some XML looking like this:

<books>
  <book>
    <id>1</id>
    <title>Stickwick Stapers</title>
    <author>Farles Wickens</author>
  </book>
  <book>
    <id>2</id>
    <title>Karnaby Fudge</title>
    <author>Darles Chickens</author>
  </book>
etc...
</books>

Now, if they’re interested in Stickwick Stapers by Farles Wickens they call Book#find(1), which returns a 404 error. Oops, of course it does, you’re not looking up books by their database ID any more, you’re looking them up by their URL slug. Your customer needs to call Book#find(’stickwick-stapers’).

Unfortunately, your book XML doesn’t include the URL slug, so your partners are in a bind. Back to work. You change the #to_xml method for your Book model to return something that looks like this:

<book>
  <id>stickwick-stapers</id>
  <title>Stickwick Stapers</title>
  <author>Farles Wickens</author>
</book>

After all, the consumers of your API aren’t really interested in the database ID; or, they shouldn’t be. All is well again, until you get another phone call. It seems now your partners can no longer purchase books through your service.

You’ve exposed the Purchases resource for your partners who want to buy books. A purchase involves simply POSTing to this resource with the ID of the book you want to buy and a quantity (you handle payment offline using a complicated barter system). The POST body looks like this:

<purchase>
  <book_id>ethel-the-aardvard-goes-quantity-surveying</book_id>
  <quantity>7</quantity>
</purchase>

OOPS! ActiveRecord doesn’t expect the URL slug for the book, it wants the database ID.

Well, crap. This is a big problem, and one that has no particularly satisfying solution. Here are the candidates:

  1. Send both the database ID and the URL slug in the API, and try to educate all of your API consumers about when to use one vs. the other. Get ready for some serious customer support time.

  2. Override the #book_id= method in the Purchase model to expect a URL slug for the book. Unfortunately, the web site you developed, at great expense, has all sorts of drop-downs and the like stuffed chock full of book IDs. Changing all of that would be a significant expense, never mind the bugs guaranteed to creep in as developers consistently forget that #book_id= doesn’t actually take an ID.

  3. Write the #book_slug= on the Purchase model, and ask your API uses to start using this method instead. Unfortunately, this means changing the web sites that they have developed, at great expense. You just cost them money, never mind the bugs guaranteed to creep in as developers consistently forget that the method to set the book ID is #book_slug=, not #book_id=.

  4. Stop using those silly slugs and just go back to database IDs. Integers are really quite beautiful, aren’t they?

This little ditty is just an example of a fairly serious problem with Rails:

Sometimes we reference domain objects by their database ID (when creating associations), sometimes we reference domain objects by their URL representation (when finding objects in a controller), but in both cases we call the reference that we use the ID.

ActiveResource is an obvious example of the problem. It expects that the XML it receives for an object will have the <id> attribute, and it uses this attribute to build the URL for that object.

Rails routing codifies the problem with its URL parameter naming convention:

map.resources :books # => /books/:id (show/update/destroy)

Is there any surprise this leads to code that looks like this?

def show
  @book = Book.find_by_id(params[:id])
  ...

Or this?

it "should succeed" do
  get :show, :id => @book.id
  ...

ActiveRecord, whether by intention or not, further enforces this fallacy with the unfortunate convenience that the default implementation of #to_param is simply id.to_s, and that #find_by_id will accept an integer, or a string, or even a string that starts with an integer[1]. So, oftentimes when a project chooses to start using something other than database IDs for URLs the code has a confusing mishmash of methods that use the two interchangeably. Have fun picking that apart.

So, what to do about it? The Rails conventions are largely set in stone, after all, it’s not likely the names of these references will ever change. But, we can be smarter about how we use them:

  1. Stop using #find_by_id in controllers. After all, you’re more than likely not looking for anything there by the database ID. I like the find_by_param plugin as a nice little helper for this. It gives you the #find_by_param and #find_by_param! methods, which you should use in your controllers. It also gives you methods for easily creating URL slugs, but you don’t need to use those until you want them.

  2. Stop writing broken tests. Every time you pass an ID to a routing parameter in a functional test you’re testing a lie. Your tests will pass with the default ActiveRecord behavior, but if you ever decide to override #to_param (most likely after you’ve written about 700 tests like this), they’ll break. My experiences dealing with just this problem on client projects was no small part of the reason I wrote the Wapcaplet plugin and this Rails patch.

  3. Know what you mean and say what you mean. The fact that Rails got this wrong just means that you have to pay closer attention when referencing anything by ID.

  4. Let me know if you come up with any clever solutions.

[1] Rails will treat any string that starts with a database ID the same as the database ID itself in many cases:

  Book.find_by_id("7-biggles-combs-his-hair")
  purchase.book_id = "11-thirty-days-in-the-samarkind-desert-with-the-duchess-of-kent"
  genre.book_ids = ["13-how-to-start-a-fight", "16-blogging-for-dummies"]

This fixes the symptom in many cases, but really just further conflates the IDs. And, if you want something without that ugly integer on the front you’re out of luck.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Christian Sepulveda

Recent Twitter DOS attack, problems and its impact on Tweed

Christian Sepulveda
Sunday, August 9, 2009

As many of you know, on Thursday, Twitter was hit by a Denial of Service (DOS or DDOS) attack.

The attack and its resulting problems have continued to plague Twitter. Twitter has blogged a bit about this on their site. (More can be found here and here.)

Twitter clients, such as Tweed, use Twitter’s API to provide access to Twitter’s service. So when Twitter is unavailable, Tweed will not work.

Twitter, as a defensive measure to protect itself, has limited API access for third party clients.

Thus, clients like Tweed, are being affected by both Twitter’s availability and the API restrictions. Complicating matters, it seems that different clients have been affected differently. For example, UberTwitter has been hit particularly hard. So, at times Tweed has not been working while other Twitter clients have. (The reverse has also been true.)

During this time, we have had trouble getting our own tweets posted to tell Tweed users about the situation.

Tweed could do some things to increase its own robustness. For example, Tweed does not currently cache tweets or timelines. Also, the spinner on the initial loading scene is full screen, so when the Twitter API is unresponsive, Tweed hangs.

We will adding enhancements to make Tweed more reliable in such cases, though we also hope such changes won’t be necessary in the future.

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

How to Not Test RabbitMQ Part 2

Will Read
Friday, August 7, 2009

This is Part 2 of my two part series on working with queues in Ruby. If you want some context please head over to part 1. In this post I’ll touch on Moqueue, using RSpec to stub out Bunny, and a few other hurdles along the way.

So getting started, we didn’t want our unit tests to hit RabbitMQ because we feel strongly that our unit tests shouldn’t depend on outside services any more than necessary (disclaimer: I do feel strongly that our integration tests should depend on those services to recreate the state of the world as accuratly as possible). To do this, there’s a great mock called Moqueue which Chris has kept in our field of vision.

In the example spec for what’ll become our subscriber daemon, it looks like something like this:

require "subscriber_daemon"
require 'moqueue'

describe SubscriberDaemon do
  before :all do
    overload_amqp
  end
  ...
end

The key is this line:

overload_amqp

The way I think about this is that it injects a overgrown array where I call MQ.new. Sweet, now I can leave RabbitMQ out of my tests.

Next, my SubscriberDaemon looks something like this:

require 'simple-daemon'
require 'mq'

class SubscriberDaemon < SimpleDaemon::Base
  SimpleDaemon::WORKING_DIRECTORY = "#{BOX.log_dir}"
  READ_QUEUE = "MyQueueOfWork"

  def self.start
    puts "STARTING #{classname} #{Time.now}"
    STDOUT.flush
    EM.run do
      subscribe_to_read_queue
    end
  end

  def self.stop
    puts "STOPPING #{classname} #{Time.now}"
    STDOUT.flush
  end

  def self.subscribe_to_read_queue
    amq = MQ.new
    queue = amq.queue(READ_QUEUE, :durable => true, :auto_delete => false, :exclusive => false)
    queue.subscribe(:ack => true) do |header, message|
      #stuff the message in the database
      ...
      header.ack
    end
  end

Some things to note: We used SimpleDaemon to get us daemon functionality. The start and stop methods are part of that interface. It needs you define a working directory for the log file and pid file it generates for you. Works great with Monit (make sure to use the latest version or Monit will start up multiple instances of your daemon). It also likes to spool up the messages so if you want feedback in your log that resembles real time events, make sure to flush your output.

You’ll also see that we created a durable queue, meaning stuff stays on the queue (in a “being worked on state” until it gets an ack back. you’ll also see that our subscribe method has to be called inside an EM.run. This is because of the async nature of the AMQP gem’s client queue subscribe method.

First potential pitfall: Sticking your subscribe code inside the EM.run. You’ll never know when it completes. It is also hard to stop. Just extract that logic out into a method and you’ll be as happy as two rabbits on their “bunnymoon”.

Onward, now lets look at some tests around the SubscriberDaemon.

before :all do
  overload_amqp
end

it "should read a item from the queue and stuff it in the database" do
    #put some stuff on the queue
    amq = MQ.new
    queue = amq.queue(SubscriberDaemon::READ_QUEUE)
    queue.publish("My message")

    #do the subscription
    SubscriberDaemon.subscribe_to_read_queue

    #assert on the expected outcome (eg look it up in the database)
    ...
  end

In this case, we’ve written the test to exercise the queue client, but not the server. We fake out RabbitMQ using Moqueue, and we never start the daemon, so we don’t have to worry about stopping it, we simply call the same method the daemon calls in the loop. You may also notice we explicitly put “My message” on the queue in the spec.

Done. SubscriberDaemon unit test is written.

On the other end, we want to publish messages with the string reversed to the queue. So here’s a MessageMaker:

require 'subscriber_daemon'

class MessageMaker
  SUCCESS_QUEUE = SubscriberDaemon::READ_QUEUE

  def self.log(message)
    puts caller.first
    puts Time.now
    puts message
    STDOUT.flush
  end

  def self.make_reversed_message(message="Hello Readers!")
    amqp_client = Bunny.new(:logging => false)
    amqp_client.start
    queue = amqp_client.queue(SUCCESS_QUEUE, :durable => true, :auto_delete => false, :exclusive => false)
    queue.publish(message.reverse, :persistent => true)
    MessageMaker.log("PUBLISHED #{message.reverse}")
    amqp_client.stop
  end
end

In our spec for MessageMaker, let us assume that we just want to test the make_reversed_message() logic and leave all the queue nonsense out of it. This means we’ll have to mock Bunny. For a little extra excitement, let’s call make_reversed_message() twice to ensure that making a message doesn’t pollute the next message that gets made.

require 'bunny'

describe MessageMaker, "#make_message" do
  it "doesn't pollute subsequent messages"
    bunnies = []
    bunnies << mock('bunny')
    bunnies[0].stub(:start)
    bunnies[0].stub(:stop)
    bunnies[0].stub(:exchange)
    bunnies[0].stub(:queue).and_return do |*args|
        queue = mock('queue', :name => 'my queue')
        queue.should_receive(:publish).with("?ykcits dna nworb s'tahW", :persistent => true)
        queue
    end

    bunnies << mock('bunny')
    bunnies[1].stub(:start)
    bunnies[1].stub(:stop)
    bunnies[1].stub(:exchange)
    bunnies[1].stub(:queue)
    bunnies[1].stub(:queue).and_return do |*args|
      queue = mock('queue', :name => 'my queue')
      queue.should_receive(:publish).with("!kcits A", :persistent => true)
      queue
    end

    Bunny.stub(:new).and_return do |*args|
      bunnies.shift #shifts off the first instance in the array and returns it
    end

   MessageMaker. make_reversed_message("What's brown and sticky?")
   MessageMaker. make_reversed_message("A stick!")
  end
end

The assertions happen in the mocked Bunny instances where you see “queue.should_receive(…)” We have two bunnies because each time you call make_reversed_message() it instantiates a new Bunny. We stubbed Bunny.initialize() to return different instances with different expectations when queue() is called.

So now we have a very focused unit test which only tests the logic of make_reversed_message(). It doesn’t test any queue related code because we mocked out Bunny, our AMQP Client that we use for adding our messages to the queue synchronously, but demonstrates that the message does get reversed. And that, is the way to not test your queues.

  • 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. ←
  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 >