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 2007

Pivotal Labs

<strike>Ten Things I Hate about Proxy Objects</strike>, Part I

Pivotal Labs
Sunday, August 26, 2007

Has Many Through Has Many Through Has Many Through …

In which the author relates several things he hates about Rails’ association Proxies, along with workarounds to fix them. Part I of VIII

From time to time, you want to join through more than one join table. Consider the following example:

class Essay < ActiveRecord::Base
  has_many :chapters
  has_many :pages, :through => :chapters
  has_many :paragraphs, :through => :pages
  has_many :words, :through => :pages
end

This kind of scenario has arisen in many of the Rails Apps that I’ve worked on. In the current version of ActiveRecord, has_many :through cannot go through another has_many :through. There are various ways to work around this, like mapping through the associations, but most workarounds are either inefficient or are difficult to extend with pagination and such.

In theory, it’s easy to make ActiveRecord support this; we need merely to walk down a has_many :through chain, joining tables as we go. This took me a long time to implement though, as I had to decipher the opaque Reflection object model, and that’s where all the magic happens.

What is a Reflection?

Every particular association declaration, like has_many :chapters, etc., is represented as an Object by means of the Reflection class. From an instance of a Reflection, you can access all of the details of the has_many declaration as well as information about the database schema that ActiveRecord was able to infer. Reflections look different for each kind of association, and (unsurprisingly) the has_many :through Reflection is the scariest. Consider this example:

has_many :words, :through => :paragraphs

The Reflection representing this declaration is composed of two sub-reflections. The first, called the :source, represents the Word class. From it we know all about the words table and its foreign keys. The second, is called the :through; it represents the Paragraph class. Now let’s dive into some code.

Mucking through ActiveRecord

So our goal is to join a bunch of tables together. For convenience ActiveRecord joins using the INNER JOIN ... ON ... form rather than the more traditional FROM ... WHERE ... form. The method that currently does the work is:

class ActiveRecord::Associations::HasManyThroughAssociation
  def construct_joins(custom_joins = nil)

This method handles a number of complex cases dealing with the directionality of the foreign key relation and polymorphic relations. In the simplest case, the source code looks like this (I’ve added comments indicating the values of the perplexing expressions):

reflection_primary_key = @reflection.source_reflection.primary_key_name # paragraphs_id
source_primary_key     = @reflection.klass.primary_key # id
"INNER JOIN %s ON %s.%s = %s.%s" % [
  @reflection.through_reflection.table_name, # paragraphs
  @reflection.table_name, reflection_primary_key, # words, paragraphs_id
  @reflection.through_reflection.table_name, source_primary_key, # paragraphs, id
]

The current algorithm is hard-coded to deal with exactly one join. Note the use of @reflection! Imagine taking the same source code, but parameterizing it to deal with an arbitrary reflection. Let’s call this new function construct_one_join:

def construct_one_join(reflection)
  reflection_primary_key = reflection.klass.primary_key
  ...

As you can see, all we really need to do is remove the @ characters and we’re good to go. Next we need to walk the line, iterating down the through chain until the end. Let’s overwrite the old function to do this:

def construct_joins(custom_joins = nil)
  reflection = @reflection
  joins = []
  while reflection.through_reflection
    joins << construct_one_join(reflection)
    reflection = reflection.through_reflection
  end
  "#{joins.join(' ')} #{custom_joins}"
end

There’s still a little more work to do. Ultimately all these joins need to terminate at some particular record’s primary key. That is, when we say:

my_essay.words

Ultimately there should be some clause in the query saying

AND essay_id = '#{my_essay.id}

The old code to effect this is:

def construct_conditions
  table_name = @reflection.through_reflection.table_name
  conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value|
    "#{table_name}.#{attr} = #{value}" # paragraphs.essay_id = #{my_essay.id}
  end
  conditions << sql_conditions if sql_conditions
  "(" + conditions.join(') AND (') + ")"
end

As you can see from the comment in the above code, this is incorrect since paragraphs doesn’t have an essay_id column; rather, chapters does. We really want to say chapters.essay_id = #{my_essay.id}

If we had a function that could get us chapters (i.e., the last through reflection):

def last_through_reflection
  reflection = @reflection
  while reflection.through_reflection
    reflection = reflection.through_reflection
  end
  reflection
end

Then we could replace construct_conditions with the following:

def construct_conditions
  table_name = last_through_reflection.table_name
  conditions = construct_quoted_owner_attributes(last_through_reflection).map do |attr, value|
    "#{table_name}.#{attr} = #{value}"
  end
  conditions << sql_conditions if sql_conditions
  "(" + conditions.join(') AND (') + ")"
end

That’s almost it. There’s just a little more work to handle the :conditions on queries in the chain. See the attached source code for this final detail.

Whew. The idea is of the algorithm is almost trivial: walking down a through chain, joining tables as we go, but the implementation is complex because of the impenetrable interface to Reflection. But it took very few modifications to the Rails source to make this happen… And voila! Now we can has_many :through a has_many :through.

I’ll conclude this article with an exercise for the reader (I’d do it myself but I’m a bit lazy). The clumsy iteration patterns I’ve used (while reflection = reflection.through_reflection) would look much nicer if we implemented Enumerable on Reflection. Then, without any lack of clarity we can rewrite #construct_joins using #inject; similarly last_through_reflection becomes a trivial call to #last. Anyone up for it?

Download all of the source code:
http://www.pivotalblabs.com/files/associations_on_steroids2.rb

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

Treetop: Bringing the Elegance of Ruby to Syntactic Analysis

Pivotal Labs
Monday, August 20, 2007

(This is my proposal for this year’s RubyConf. Fingers crossed!)

Treetop is a parsing framework that brings the elegance and simplicity of Ruby to syntactic analysis. Rather than being just another copy of classic LALR/LR based generators like Lex and Yacc, Treetop blends the unique expressive power of Ruby with cutting edge parsing research. Its “packrat” implementation enables recognition of parsing expression grammars, which dispense with the need for lexical scanning and can take advantage Ruby’s mixin and inheritance model for composition.

I will begin with an overview of recursive descent parsing, explaining why the exponential time complexity of backtracking make naive implementations impractical. Packrat parsers solve this problem with memoization, exchanging heap space for linear time performance. In the process, they obviate the look-ahead techniques that have traditionally been used to reign in the time complexity of parsing.

This opens a new world for grammar definitions.

Without lookahead, a compositionality-destroying tokenization process is no longer necessary. Combined with the embrace of backtracking, this results in a grammar definition language that is more intuitive and free of the hidden obstacles that plague more restrictive conceptual frameworks. In the literature, these definitions are called parsing expression grammars. They are free of ambiguity and explicit operator precedence annotations. They can also be freely composed.

Imagine taking the Ruby grammar and the SQL grammar and combining them to yield a grammar for Ruby that recognizes the syntax of SQL statements embedded in strings. A token-free, compositional approach is the key to this sort of reusability.

The rest of the talk explores the manifestation of these expressive advantages in the Treetop framework. Treetop’s grammar definition language is a superset of Ruby, conservatively extending its syntax with two keywords (grammar and rule), along with inline parsing expressions. It blends parsing expression constructs with Ruby method declarations, which are automatically installed on nodes of the syntax tree produced during the parse. I will introduce these fundamental concepts by example, creating a toy parser that recognizes and evaluates arithmetic expressions. Here is a shortened sample:

module RubyConf
  grammar Arithmetic
    rule additive
      number '+' additive {
        def value
          number.value + additive.value
        end
      }
      /
      number
    end

    rule number
      [1-9][0-9]* {
        def value
          text_value.to_i
        end
      }
    end
  end
end

I will then discuss the system’s implementation. Important issues include:

  • The metagrammar and the system’s metacircular bootstrapping process
  • The code generator and the mapping of parsing expressions to Ruby code
  • Riding on Ruby’s semantics for grammar composition, including the use of mixins and the super keyword.

Time permitting, I will explore more advanced usage by writing a simple interpreter for the untyped lambda calculus atop the framework, which I will extend with facilities for arithmetic by mixing in the grammar from the first example.

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

How I Learned to Stop Hating and Love Action Mailer

Pivotal Labs
Monday, August 20, 2007

My biggest gripe with ActionMailer is how difficult it is to generate URL’s. It’s common enough when sending an email that it includes a link. But ActionMailer, by default, gives you no access to url_for and named routes. Ugh!

Even if you’re clever enough to do something like:

class ActionMailer::Base
  include ActionController::UrlWriter
end

You’re still screwed as you need to know the host, port, protocol, etc. to generate links. These data can be set globally, but by far the easiest and most flexible way is to get them is from the request object.

Passing around the host, port, etc.

The request object, is (of course) only available to Controllers. So the data need to be passed from the Controller to the mailer, like:

class UsersController < ApplicationController
  def create
    ...
    if @user.save
      MyMailer.deliver_foo(..., request.host, ...)
    end
  end
end

Of course, if you’ve read my previous post you know I HATE polluting my Controllers with business logic like this. I strongly prefer pushing this “triggered action” into the model:

class User
   after_create :send_email
end

The cost of this is that I now need to past the host, etc. down into my model so it can pass it on to the mailer! The Java Programmers over here just laugh at me for not having a real Dependency Injection framework, which they say would solve this handily. Some stupid Java framework solving this problem better than Rails?! This makes me MAD AS HELL!

Enter the Global Variable

Screw Dependency Injection. I’m going to use a Global Variable like every other God Fearing Rails programmer. His Excellency DHH said let there be cattr_accessor and it was Good.

Step one, set the around-filter in your ApplicationController:

around_filter :retardase_inhibitor

Step two,

THERE IS NO STEP TWO

You don’t need to pass around host. You can generate URL’s in ActionMailer no problem. Let’s look at how this is done:

module UrlWriterRetardaseInhibitor
  module ActionController
    def self.included(ac)
      ac.send(:include, InstanceMethods)
    end

    module InstanceMethods
      def inhibit_retardase
        begin
          request = self.request
          ::ActionController::UrlWriter.module_eval do
            @old_default_url_options = default_url_options.clone
            default_url_options[:host] = request.host
            default_url_options[:port] = request.port unless request.port == 80
            protocol = /(.*):///.match(request.protocol)[1] if request.protocol.ends_with?("://")
            default_url_options[:protocol] = protocol
          end
          yield
        ensure
          ::ActionController::UrlWriter.module_eval do
            default_url_options[:host] = @old_default_url_options[:host]
            default_url_options[:port] = @old_default_url_options[:port]
            default_url_options[:protocol] = @old_default_url_options[:protocol]
          end
        end
      end
    end
  end

  module ActionMailer
    def self.included(am)
      am.send(:include, ::ActionController::UrlWriter)
      ::ActionController::UrlWriter.module_eval do
        default_url_options[:host] = 'localhost'
        default_url_options[:port] = 3000
        default_url_options[:protocol] = 'http'
      end
    end
  end
end

ActionController::Base.send(:include, UrlWriterRetardaseInhibitor::ActionController)
ActionMailer::Base.send(:include, UrlWriterRetardaseInhibitor::ActionMailer)
(from url_writer_retardase_inhibitor.rb)

“Oh no!,” you exclaim, “Class Variables!! Get behind me, Satan!”.

Well, get over it. How do you think with_scope works? How do you think you can call a Finder like User.find or User.new wherever you feel like? It’s called Global Variables, man. Embrace it. I call this liberation theology.

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

Perplexing constant scope behavior

Pivotal Labs
Friday, August 17, 2007

Why can’t I refer to constants nested inside a class within a module eval?

irb(main):001:0> class A
irb(main):002:1>    class B
irb(main):003:2>    end
irb(main):004:1> end
=> nil
irb(main):005:0> A.module_eval { puts B }
NameError: uninitialized constant B
           from (irb):5
           from (irb):5:in `module_eval'
           from (irb):5
           from :0

This is frustrating. Can anyone explain why it has to be this way? Perhaps there’s a good reason I’m not considering.

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

Taking a break from Rspec

Pivotal Labs
Friday, August 17, 2007

On my next new project, I think I’m going back to Test::Unit. I’ve lost patience with Rspec, and it seems like I’m not alone. But I’ve spent more time praising and lobbying for it than some of its other current detractors, so I feel an explanation of my reversal is in order. First, I’ll start off with what I like about Rspec, what got me to spend the time and energy switching to it to begin with.

I’ve always stressed that Rspec brings nothing fundamentally new to the table. For **’s sake, it’s a testing framework. Setup. Teardown. Mocking. There’s not a hell of a lot more to see. And that’s okay. What’s great about Rspec is that it lets you use real strings to label your test fixtures and cases. When I learned about it, I was struggling to name my test cases like sentences.

test_that_foo_does_bar_when_it_has_been_bazzed

Luckily, I’m a Dvorak typist so the underbar is close at hand, but not being able to freely compose descriptions of what I’m testing in a natural way can be very limiting. Writing helps me gather my thoughts, so the act of labelling the test can really help me. I take special pride in well written “it” strings. The describe block strings are also helpful, but not as big a deal to me. The clever assertion hacks are also cute and fun to write. The built in mocking is nice, but I’m not a big mockofascist, so I don’t get too excited about it.

And… that’s basically it. My appreciation for Rspec can be broken down as follows:

  • 70%: String test fixture and case names
  • 20%: .should be_valid etc
  • 10%: Mocking

Now, what sucks about Rspec snuck up on me. It boils down to 2 things:

  1. The framework is too f-ing complicated in its implementation.
  2. The framework is too presumptive about how I wish to organize my tests.

The second is an aesthetic gripe, which I insist is fair game, since the framework’s merits are mainly aesthetic anyways. The first is a much deeper issue. The semantics of a Test::Unit test fixture are straightforward. The test fixture is a class. It contains methods beginning with the word test. A seperate instance of the class is created, in which each test runs. A setup and teardown method run before and after each method invocation.

And that is more or less all I need to know. There’s some stuff I’d like to have, like a global setup / teardown akin to before(:all), but I can what’s there without really understanding anything about the framework’s implementation. It rides on the semantics of Ruby, and I understand Ruby because I use it every day.

Rspec, on the other hand, sends me down a labyrinthine path full of Ruby meta-object-protocol tricks to accomplish even the simplest of tasks. And trust me, I have a pretty solid grasp of the MOP. I love it, use it, and cringe when people refer to it as “magic” (it’s like assembly programmers calling a for loop magic or something). But there’s use and then there’s overuse. I myself can be accused of both. The eval family of methods is great. Therein lies the power to implement DSL’s with their own semantics that can diverge quite dramatically from Ruby. Therein also lies the problem. I like classes. I like inheritance. I like the object oriented model. So if software written in an object oriented language can get away with employing the basic object oriented tools to accomplish its mission, well then it by all means should. I don’t have time to dig around the BehaviorEvalModule, or whatever else I’ve looked at in myriad diversions to get Rspec to do something of medium hardness.

So I guess that’s it. Rspec does the basics really well. Ridiculously beautiful stuff. But venture beyond the limited tracks they’ve laid and you’re in the jungle. So anyway. I’m not ready to write the whole thing off yet, but I am going to revisit Test::Unit for a while, see if I might not give myself what I miss from Rspec atop its simpler implementation. We’ll see if I come back.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Agile 2007 Presentations

Alex Chaffee
Tuesday, August 14, 2007

Writing and Testing JavaScript-Heavy Web 2.0 Apps with JSUnit

Alex Chaffee & Edward Hieatt
(presented by Alex Chaffee and Brian Takita)

Slideshow link expired. We’re hoping to get a fresh copy up soon.

JsUnit

Edward Hieatt
(presented by Alex Chaffee)

Slideshow link expired. We’re hoping to get a fresh copy up soon.

CI for the Web 2.0 Guy or Gal

Chad Woolley

Slides

Full-stack web app testing with Selenium and Rails

Alex Chaffee and Brian Takita

Slideshow link expired. We’re hoping to get a fresh copy up soon.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Agile 2007 Presentations

Alex Chaffee
Tuesday, August 14, 2007

Writing and Testing JavaScript-Heavy Web 2.0 Apps with JSUnit

Alex Chaffee & Edward Hieatt
(presented by Alex Chaffee and Brian Takita)

JsUnit

Edward Hieatt
(presented by Alex Chaffee)

CI for the Web 2.0 Guy or Gal

Chad Woolley

Full-stack web app testing with Selenium and Rails

Alex Chaffee and Brian Takita

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

Advanced Proxy Usage, Part I

Pivotal Labs
Wednesday, August 8, 2007

One of the more underutilized features of ActiveRecord is the Assocation Proxy. But they are also one of the most powerful weapons in the ActiveRecord armory, and Rails apps that take advantage of them are better organized and easier to maintain.

What is a Proxy?

When in an ActiveRecord you declare an Association:

class Hand < ActiveRecord::Base
  has_many :fingers
end

Instances of Hand now have a fingers method. Contrary to appearances, and contrary to the LIE told to you by hand.fingers.class, the fingers method does not return an Array of Fingers. Rather it returns a Proxy object, one that smells and tastes like an Array of fingers but actually has a rich creamy behavior all its own.

Scoped Access

The most basic use of Proxies is to “scope” the reading and writing of your ActiveRecords. For example, if you have a controller that allows CRUD on a User’s Assets, you can read and write to the collection of Assets as in the following examples:

@asset = current_user.assets.find(params[:id])
@asset = current_user.assets.create(params[:asset])
@asset = current_user.assets.build(params[:asset]) # equivalent to 'new'-ing an object rather than 'create'-ing it.

There are other ways of doing this, of course:

@asset = Asset.create({:user => current_user}.merge(params[:asset))

But the Proxy Code is much better: not only is the Proxy code terse, but it meaningfully expresses the relationship between objects in your domain: Users have many assets; this Asset is created in the context of this User.

Special Queries (or Custom Finders)

The various Association declarations--has_many, belongs_to, etc.--allow you to express much more than a simple Foreign Key relation. We can richly express in the Proxy Declarations concepts like 'Assets that belong to a User' and 'Assets that don't belong to a User':

current_user.my_assets
current_user.other_assets

simply by declaring:

class User
  has_many :my_assets, :class_name => 'Asset', :conditions => 'user_id = #{id}'
  has_many :other_assets, :class_name => 'Asset', :conditions => 'user_id != #{id}'
end

Notice, in this last example, something peculiar: the use of single quotes ('') with variable substitution (#{...}). This is not a typo: the use of double-quotes, would perform variable interpolation when the has_many declaration is invoked. This is in the class-context--i.e., there is no instance yet. Rails always calls eval with a Binding of self when a call to one of the Proxy methods is performed, ensuring that this all comes together.

Let's consider an alternative to this approach: declaring finders as instance methods.

class User
  def other_assets
    assets.find(:conditions => ["user_id != ?", id])
  end
end

What’s wrong with this approach? Well, if you want to use this query in anything non-trivial–such as selecting the first ten of a User’s Assets–you have to write fancy code:

def other_assets(options)
  assets.find({:conditions => ...}.merge(options))
end

But good luck using this strategy to do pagination. You need to define my_assets and my_assets_count, too–have fun keeping your code DRY. With a proxy, we can just do something like:

current_user.my_assets.count
current_user.my_assets.sum
current_user.my_assets.average(:price)

In fact, all the richness of ActiveRecord class methods (and any other class methods of the Target type) are available to you here. Want to find all of a User’s assets that are in State pending?

current_user.my_assets.find_by_state(State[:pending])

Another example:

class Asset
  def self.find_portrait_assets
    find(:all, :conditions => 'height > width')
  end
end

Then,

current_user.my_assets.find_portrait_assets

returns only those portrait assets owned by a user.

Proxy Options

Proxy declarations accept a number of interesting parameters. There are even “lifecycle” callbacks, like after_add, and before_destroy just like a normal ActiveRecord has before_create and so forth. You can hook into these by using an option.

class User
  has_many :assets, :after_add => [:send_email] do
  end
  def send_email(r)
  end
end

This after_add could be defined in the Asset class. But suppose Assets had a Polymorphic association. Both Users and Articles have many Assets. Our Business Rule is only to send email when a User adds an Asset, not an Article. We could write:

class Asset
  def after_create
     case owner
     when User
       # send email
     when Article
  end
end

But this is clumsy! When we have logic to express about the relationship between things, the Proxy is the right place for it. Anywhere else is just smearing logic throughout your code.

Proxy Extensions

Consider the following example:

has_many :assets do
  def to_s
    self.join(',')
  end
end

You can actually extend your Proxy Objects with an Anonymous module! When you have logic that applies to a Collection of ActiveRecords, your has_many Proxy is probably the proper place for it. For example:

class Table
  has_many :cells do
    def to_matrix
      # convert from list to matrix form.
    end
  end
end

Another example of this technique is the following. Suppose an Asset as many Versions, such as small, medium, etc. We’d prefer a shorter way of finding the proper version of an Asset than saying asset.versions.find_by_name('thumbnail'), we’d like to just say asset.versions[:thumbnail]. Just define the brackets ([]) operator on the Proxy:

class Asset
  has_many :versions, :class_name => 'Asset', :foreign_key => :parent_id do
    def [](version_name)
      find_by_name(version_name)
    end
  end
end

Suppose we want to go one step further. If a particular version doesn’t exist, it shall be created on-the-fly:

def [](version_name)
  if version = find_by_name(version_name)
    version
  else
    # create a new version here.
  end
end

Advanced Extensions

In some cases, we want to write generic Extensions–these should work regardless of the particular classes involved. In the context of a Proxy there are three methods you should be aware of: proxy_owner, proxy_target, and proxy_reflection.

Suppose we want to implement something like the build method, but one that doesn’t have the side effect of adding it to the owner in memory:

has_many :foo do
  def new(options = {})
    proxy_reflection.klass.new({proxy_reflection.primary_key_name => proxy_owner.id}.merge(options))
  end
end

Extensions are so useful–it just requires a little imagination–that I’m going to give one more example, this one apropos of Access Control:

class User
  has_many :draft_articles do
    def readable_by?(user)
      user == proxy_owner
    end
  end
end

Some Miscellany

  1. The has_one and belongs_to Proxies behave a bit oddly: here, cyclops.build_eye is used rather than the more obvious cyclops.eye.build.

  2. In general, has_one and belongs_to will shadow methods on the Target. Don’t name any database columns target or owner, for instance. This is one of the biggest complaints against the current implementation of Proxies!

  3. Both build and create will work even if the Proxy Owner is new. For example, u = User.new; u.assets.build; u.save. In this example, both objects will be saved with the Foreign Key set correctly.

  4. Both build and create can take an array of attributes hashes. For example: u.assets.build([{...}, {...}]). This will build two assets at once. (This is quite nice where in a Controller you have a form that allows the upload of multiple Assets at once. The Controller code looks identical (in simple cases) regardless of whether the form allows a single or multiple upload!)

That’s the basic idea. In part II of this Article (to be released in the coming weeks), I will discuss ‘static’ Proxy methods and I will release version 0.1 of a new plugin that builds upon a lot of exciting work in this area. In the meantime, check this out.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

doing it again and again

Alex Chaffee
Wednesday, August 8, 2007

Here’s an RSpec trick I discovered yesterday. Sometimes when you’re writing a test you want to loop over some precondition data. But if you do a loop inside your test (or spec), then all the cases will be subsumed in a single test method (or “it” block). This means you’ll have the following problems:

  • The first case to fail will cause the rest of the cases not to run. It’d be nice to see them all in a single test run.
  • You won’t take advantage of RSpec’s cool self-documenting trick of labeling each it block with a full description of the failure, and it’ll be harder to debug which case failed.
  • If you’re calling into Rails (e.g. in a View spec), you’ll only be able to call certain methods — especially render — once per test method. That means that you simply can’t use a loop inside a method to collapse redundant tests into a single block.

Ruby to the rescue! Instead of looping inside your it block, loop outside your it block.

require 'hpricot'

describe "navbar" do

  TABS = ["Home", "Articles", "Comments", "Preferences"]
  TABS.each do |tab|
    it "selects tab #{tab}" do
      assigns[:current_navbar_tab] = tab
      render "/shared/_navbar.mab"
      doc = Hpricot(response.body)
      doc.at("//li[@class=active]/a").inner_html.should == tab
    end
  end

end

When I mentioned this at standup, Nathan mentioned the eval module… maybe he or someone else can add more detail in a comment?

Note that this technique should be used sparingly. It’s kind of a test smell to have loops, but it’s useful in certain cases… In this example it’s actually different code rendering each separate tab. If we spent a bit more time and extracted a Tab object then we could possibly get away with just unit testing that class and trusting it to render properly on the page for each actual tab.

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

Cacheable Flash

Pivotal Labs
Wednesday, August 8, 2007

Page caching is an easy way to get massive performance and scalability increases with little up front effort.
Of course, when page caching, a number of design changes are necessary. For example, server side session data cannot be used to render data on cached pages.

Rails provides the flash hash to easily render alert messages and errors. In the controller you can write to the flash hash:

flash[:error] = "You cannot go there"
# or
flash[:notice] = "Welcome to eternity"

and render the flash hash in the view. Typically the rendering happens on a layout:

<div id="error_div_id" class="flash flash_error"><%= flash[:error] %></div>
<div id="notice_div_id" class="flash flash_notice"><%= flash[:notice] %></div>

There are some strange quirks with using flash such as needing to use FlashHash#now when rendering the response without redirecting.

Everything works great until you need to page cache the landing page.

For example, lets say you page cache your home page. After logging in, you are redirected to the home page with the flash notice “Logged in successfully”. When page caching, this solution does not work because the request is responded to by the Web Server (i.e. Apache) and does not reach the Rails App server (i.e. Mongrel).

This means the view does not get a chance to render the flash error and notice.

There are a couple of solutions to this problem.

  • Do an AJAX request back to the server to render the flash error and notice (i.e. using RJS)
  • Send the flash error and notice from the server with cookies and render it during page load

Introducing Cacheable Flash

To solve the problem using cookies on Peer to Patent, we wrote the Cacheable Flash plugin. The plugin allows you to set the flash hash as normal on the controller. It handles converting the flash hash into cookies. All you need to do is include the CacheableFlash module into your controller.

class ApplicationController < ActionController::Base
  include CacheableFlash
  # ...
end

On the view side, you will need to add some javascript.

<div id="error_div_id" class="flash flash_error"></div>
<div id="notice_div_id" class="flash flash_notice"></div>
<script type="text/javascript">
  Flash.transferFromCookies();
  Flash.writeDataTo('error', $('error_div_id'));
  Flash.writeDataTo('notice', $('notice_div_id'));
</script>

The Flash.transferFromCookies method:

  • Grabs the flash data from the cookies and saves it to Flash.data
  • Deserializes the flash hash from JSON to Javascript
  • Erases the flash data from the cookies

The Flash.writeDataTo method:

  • Writes data from the passed in key in Flash.data to the passed in element or element id

Here is how the life cycle of the login works:

  1. The user enters the correct login information
  2. Rails handles the web request. In the Login controller, flash[:notice] is written to

      if current_user
        flash[:notice] = "Welcome to Eternity"
      end
    
  3. An after filter serializes contents of the Flash Hash as JSON into cookies

  4. The after filter clears the flash hash
  5. The cached page is rendered
  6. The client side receives and clears the flash cookie data
  7. The client side javascript renders the flash messages

There is also a side benefit — you don’t have to use FlashHash#now because storing the flash in cookies to be rendered and erased by the client makes this unnecessary.

The Cacheable Flash plugin is on the Pivotal RB project page (http://rubyforge.org/projects/pivotalrb). You can install it by running:

ruby script/plugin install svn://rubyforge.org/var/svn/pivotalrb/cacheable_flash/trunk

It will copy flash.js, cookie.js, and json.js if you do not already have these files.

Happy Page Caching!!

Thanks to Josh Susser for pairing with me on this.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (778)
  • rails (113)
  • testing (86)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (54)
  • techtalk (44)
  • rspec (38)
  • activerecord (29)
  • productivity (29)
  • gogaruco (29)
  • ironblogger (29)
  • git (28)
  • nyc (27)
  • rubymine (25)
  • mobile (22)
  • bloggerdome (20)
  • cucumber (20)
  • process (19)
  • pivotal tracker (19)
  • 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)
  • selenium (12)
  • css (12)
  • goruco (12)
  • bundler (12)
  • tdd (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • rubygems (9)
Subscribe to Community Feed
  1. 1
  2. 2
  3. →
  • 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 >