Steve Conover's blog



Better array assertions using collect

edit Posted by Steve Conover on Monday October 27, 2008 at 10:00AM

You could do this:

Person.tall_people.length.should == 3
Person.tall_people[0].should == people(:linda)
Person.tall_people[1].should == people(:dwane)
Person.tall_people[2].should == people(:rick)

This is better, because it's clearer, and because in one stroke you prove bounds, content, and order:

Person.tall_people.should == [
  people(:linda), 
  people(:dwane), 
  people(:rick)
]

I argue that this is best:

Person.tall_people.collect{|p|p.first_name}.should == [
  "Linda", 
  "Dwane", 
  "Rick"
]

Failures messages are a pleasure to read

  expected: ["Linda", "Dwane", "Rick"],
  got: ["Juliette", "Jeanne"] (using ==)

And code is clearer overall (the price is a "collect")

Collecting away from the original object (and into primitives) is not only clearer, in most cases your aim is not to re-prove that the element objects are fully and properly configured. You've already done that elsewhere. You only want to prove, in the simplest possible terms, that the group of things you got is what you expected to get.

Let's say you don't intend to care about order. Sorting on primitives is a snap:

Person.tall_people.collect{|p|p.first_name}.sort.should == [
  "Dwane", 
  "Linda", 
  "Rick"
]

Slightly more controversial:

Person.short_people.collect{|p|p.first_name}.should == []

The collect seems silly at first glance, but you're making present and future assertion failures much friendlier. You'll be happy about brain cycles saved and sanity kept during big refactorings.

Peer to Patent in its second pilot year

edit Posted by Steve Conover on Tuesday September 16, 2008 at 05:00PM

From CNN.com:

Program brings Web's collective wisdom to patent process

"The concept behind the program, called Peer-to-Patent, is straightforward: Publish patent applications on the Web for all to see and let anyone with relevant expertise -- academics, colleagues, even potential rivals -- offer input to be passed along to the Patent Office.

By using the power of the Internet to tap the wisdom of the masses, Peer-to-Patent aims to dig up hard-to-find "prior art" -- evidence that an invention already exists or is obvious and therefore doesn't deserve a patent."

More Peer to Patent articles available on the Pivotal clients page.

Standup: Sep 9th,10th,11th 2008

edit Posted by Steve Conover on Thursday September 11, 2008 at 03:58PM

Interesting Things

  • ActiveRecord::Base.connection.select_all reads all records into an array, which is not good if you have a very large result set. Use a combination of ActiveRecord::Base.connection.execute with .each, or .each_hash if you want the same column<=>value mappings you get with select_all, only streamed.

  • There was some confusion about BlueCloth:

  • A recent MySql trigger experience:

    • In summary, think twice before using them when you have a viable application code alternative
    • They're not cloned from the dev to test database
    • The hosting provider this project is using requires that we submit a change request each time we want to add/change/delete triggers. The problem with that is your trigger changes aren't in sync with your code deployments.
    • It wasn't hard to write the application code
  • During a discussion about humanize, a couple other nifty transforms were mentioned:

    • parameterize: You have a string, and you want to strip out characters that aren't url-friendly. (follow the link for good example/discussion)
    • auto_link a Rails helper that takes text and links up all urls and email addresses.

use photobooth to exchange ideas quickly

edit Posted by Steve Conover on Friday May 02, 2008 at 01:02AM

  1. Draw something on an index card
  2. Run photobooth
  3. Edit => Auto Flip New Photos
  4. Take a photo of your index card
  5. Attach to email

(Don't take the finger personally)

Rails, Slashdotted: no problem

edit Posted by Steve Conover on Wednesday June 27, 2007 at 03:29AM

By Steve Conover and Brian Takita

Peer-to-Patent, one of Pivotal Labs' clients, got Slashdotted last week, and we had no trouble handling the load. The site was just as responsive as it always is, and we didn't come close to having a scale problem.

Moral of the story: the technology for serving static web pages is old, boring, and extremely scalable. If you have the type of site that can be page-cached, do so aggressively, starting with the front page and any pages likely to be linked to. We got a huge payoff for the engineering time that we invested in our page-caching strategy.

Highlights:

  • We moved away from Rails page-caching and developed our own "holeless cache", which uses a symlink trick (see below) to instantly and "holelessly" switch to a new version of a cached page. (The cache "hole" is the time between the expiration or purge of a cached page and the time when it's regenerated. The danger is that in that time your Mongrels can be saturated with requests - something we proved to ourselves could easily happen.)
  • Here's our symlink trick, using the front page as an example:

    1. Have index.html point to index.html.current
    2. If (index.html.current is >= 20 minutes old)
      1. Copy index.html.current to index.html.old
      2. Point index.html to index.html.old
      3. Rewrite index.html.current by asking Rails for the page (using the process method)
      4. Repoint index.html back at index.html.current
    3. Repeat step 2 every minute using a cron job.
  • For cache expiration that's model-based, we make a call from the model observer class to our holeless cache routine, instead of using Rails cache sweepers. So, instead of just deleting the cached page we regenerate it in place.

  • It was important to write tests that proved that the HTML we generated for cached pages looked exactly the same in different "modes" (user logged in vs not, for example). This forced us to push modal decision logic out of Markaby templates and into JavaScript, meaning that view-oriented Rspec tests asserting modal differences became useless. We rewrote them as Selenium tests.

  • Performance/load testing: we tried several tools and approaches and found that a simple Ruby script that launches wget requests (that write to /dev/null) in many separate threads worked best for us.

  • We send down exactly one .js and one .css file. If you are sending down more than one of each of these to the browser, you have a performance problem. Fix it with asset packager.

Update: one clarification about the cron job: we deploy this "automatically" using capistrano.

Using JSON on the request with Ajax.Request postBody

edit Posted by Steve Conover on Wednesday May 16, 2007 at 03:52PM

This is nice in a heavy Ajax application - you can use the same data format on the request and the response.

You might not have been aware that prototype'sprototype's Ajax.Request can send a postBody, instead of parameters.

So if you don't mind all of your Ajax requests being POSTs (I don't), you can just ignore serializing parameters and just stick JSON in the postBody.

This makes my jsunit tests (and therefore my production code) nicer:

BEFORE:

<code>
function testRequest() {
  var settings = {volume:"high", color:"red"}

  var command = new SaveSettings(settings);

  assertEquals("/save_settings", command.asAjaxPayload().url);
  assertEquals("volume=high&color=red", command.asAjaxPayload().parameters);
}
</code>

AFTER:

<code>
function testRequest() {
  var settings = {volume:"high", color:"red"}

  var command = new SaveSettings(settings);

  assertEquals("/save_settings", command.asAjaxPayload().url);
  assertEquals(settings, JSON.parse(command.asAjaxPayload().postBody));
}
</code>

(and of course, you can push the stringifying of the post body down a layer, to get rid of the JSON.parse at this level)