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

Adam Milligan

Beware the frumious nested attribute

Adam Milligan
Sunday, September 20, 2009

Nested attribute assignment is one of the recent additions to Rails that made a great deal of sense, and made a lot of people happy. Chances are you’ve either used nested attribute assignment by now, or you worked on an older project that really could have used it. If you haven’t yet, check it out and see what you think.

Unfortunately, not all is well in Railstown. Nested attribute assignment is slick, and the related implementation of #fields_for makes it even slicker, but #fields_for can cause you some headaches if you’re not careful. Possibly if you are careful as well.

Consider a standard example of where you might want nested attribute assignment:

class CatLady < ActiveRecord::Base
  has_many :cats
  accepts_nested_attributes_for :cats

  def crazy?
    true
  end
end

And in your edit view:

<% form_for @cat_lady do |cat_lady_form| %>
  <table>
    <tbody>
      <% cat_lady_form.fields_for :cats do |cat_fields| %>
        <tr>
          <td><%= cat_fields.text_field :name %></td>
          <td><%= cat_fields.text_field :nickname %></td>
          <td><%= cat_fields.text_field :burial_preferences %></td>
        </tr>
      <% end %>
    </tbody>
  </table>
<% end %>

This seems fine, but when you look in more detail you discover that #fields_for will emit a hidden <input /> element for each cat associated to our cat lady. It does this at the point where you make the #fields_for call, much like the way #form_for emits the <form /> element. Unfortunately, that means that #fields_for emits the <input /> element as a sibling of the <tr /> element for the related cat; and thus, as a direct child of the <tbody /> element. Oops. The HTML standard doesn’t allow <tbody /> elements to have <input /> elements as children.

Most browsers won’t complain about this, but Safari 4 will (and so, I’d guess, will any other WebKit-based browser, like Chrome). Safari not only complains, it helpfully moves the <input /> element to a valid position. So, instead of this (you’ll have to imagine a bit; the markdown renderer for this blog is actually modifying my invalid HTML example to try to make it valid):

<table>
  <tbody>
    </tbody></table><input name="cat_lady[cats_attributes][0][id]" type="hidden" value="423" />
    <tr>
      <td><input name="cat_lady[cats_attributes][0][name]" type="text" /></td>
      ...
    </tr>
  </tbody>
</table>

you end up with this:

<input name="cat_lady[cats_attributes][0][id]" type="hidden" value="423" />
<table>
  <tbody>
    <tr>
      <td><input name="cat_lady[cats_attributes][0][name]" type="text" /></td>
      ...
    </tr>
  </tbody>
</table>

Seems innocuous enough, doesn’t it? After all, it’s still inside the form, so the browser will still submit the value along with everything else. However, the HTML that you sent to the browser is still invalid, and Safari still spits out the errors, which is probably not the best way to gain your users’ confidence. Also, any JavaScript you’ve written that depends on the DOM structure you lay out might fail, but only in some browsers (and not, for a change, only in IE!).

Now, someone will point out that you can solve this problem by not using tables. True, but that solution has two drawbacks: first, it’s entirely reasonable, even potentially very desirable, to use a table for this type of data; second, the hidden ID input will end up outside whatever container element you create for your nested model. This may not generate invalid HTML, but it may generate conceptually improper HTML. For instance, what if we change the above HTML to look like this:

<div class="menagerie">
  <input name="cat_lady[cats_attributes][0][id]" type="hidden" value="423" />
  <div class="cat">
    <input name="cat_lady[cats_attributes][0][name]" type="text" /></td>
    ...
  </div>
</div>

It doesn’t take too much imagination in the drag-and-drop Web 2.1 world to come up with some form of DOM manipulation that will dissociate the cat div from its associated ID element. And, of course, if the server receives the nested cat attributes without an ID it will helpfully make a new cat model. We don’t want this; crazy cat lady has enough cats already.

So, what to do?

We knocked around some ideas, and the most reasonable seems to be to add the capability to manually insert the hidden ID field (and, potentially, the hidden _destroy field) to the form builder object created by #fields_for. So, the #fields_for block from the edit form above would look something like this:

<% cat_lady_form.fields_for :cats, :omit_hidden_fields => true do |cat_fields| %>
  <tr>
    <%= cat_fields.hidden_fields %>
    <td><%= cat_fields.text_field :name %></td>
    <td><%= cat_fields.text_field :nickname %></td>
    <td><%= cat_fields.text_field :burial_preferences %></td>
  </tr>
<% end %>

It’s also possible to automatically determine if the block for each nested model called the #hidden_fields method, which would obviate the need for the explicit option; I haven’t decided if I like that approach.

I’m open to suggestions for better fixes, or tweaks to this one. In any case, look for a Rails patch for this some time in the coming week.

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

Tweed 0.9.16: connection improvements

Christian Sepulveda
Thursday, September 17, 2009

0.9.16 of Tweed is now available in the App Catalog.

Changes:

  • loading spinner/scrim has been replaced with spinner/cancel button (like web browser app)
  • Tweed will auto-retry connecting to Twitter on failure response from Twitter API (this has been recently causing Tweed to hang))
  • option to cancel active request by tapping cancel button; tapping refresh will retry (instead of having to re-select timeline from nav menu)
  • shake for retry and refresh

Shake

We’ve added support for the accelerometer shake events. If you shake your phone while on a timeline:

  • if loading (Twitter is taking a long time), this will force a retry
  • if no active loading, then refreshes (same as hitting refresh button)

So, you could always force a retry by tapping cancel, then refresh, but we figure you can vent some frustration by shaking the phone. Tweed

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Nate Clark

Standup 9/17/2009: silly Ruby tricks, unstub!, iPivotal => PTCruiser

Nate Clark
Thursday, September 17, 2009

Interesting Things

  • silly Ruby trick of the day — does anyone know what this does?
while gets
  print if /foo/
end

This is an example from the Ruby “Pickaxe” book. No respected Rubyist actually writes such difficult-to-understand and obfuscated code. Though, it was mentioned, that terse syntax like this is useful for command-line hacking.

  • unstub! is a useful Rspec method that removes a stub from a previously stubbed object. This can be useful if you have stubbed, then mocked the same method, and you want to remove the stub.

  • The unofficial community-developed iPhone app for Pivotal Tracker, is changing its name from iPivotal to PTCruiser. See the author’s Twitter for more.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Nate Clark

Standup 9/16/2009: eycap :forward_agent, Rails 2.3 sends 2 content-types

Nate Clark
Thursday, September 17, 2009

Interesting Things

  • One team had trouble deploying via capistrano to Engineyard’s Solo environment because SSH keys weren’t being sent correctly. Setting ssh_options[:forward_agent] = false in their Capistrano config fixed the problem.

  • After upgrading to Rails 2.3, Rails started sending two content-types down with each page’s headers, text/html, application/json. This was causing Selenium tests to choke.

  • If you scp a file to /etc/cron.d, cron will not notice the new file until you touch it.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Nate Clark

Standup 9/15/2009: Hashing a String, Nginx Security Patch

Nate Clark
Tuesday, September 15, 2009

Interesting Things

  • String#hash does not always produce the same hash on different machines and/or different architectures. Don’t use the hash of a string across machines to identify it.

  • Nginx has released a security patch to fix a remote execution security vulnerability.

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

Explaining the Value of Agile, Rails and the Cloud

Pivotal Labs
Monday, September 14, 2009

Rob and I were recently asked by a client for some help justifying the choice of Ruby as an implementation language for their SaaS product. They were very happy with the results, but wanted to be prepared to answer investor and customer questions about why Rails was a good choice.

One way that I’ve been talking about Rails is in the context of what I’m calling “The ARC Model”: Agile + Rails + Cloud. I’ll borrow from my abstract from my talk at BizConf:

The Ruby on Rails Revolution has been one of productivity and efficiency, and has coincided with an equally powerful revolution in the ownership of technological infrastructure. The Rails approach combines agile methods with a highly productive language to allow developers to focus on developing business value, instead of developing plumbing. The Cloud Computing Revolution at the same time has changed the economics of infrastructure, allowing computation to become a commodity not worthy of developer attention, further enabling developers to focus on that which is truly valuable, Innovation. These three factors, Agile, Rails, and the Cloud, combine to revolutionize the economics of software development and information management, in ways that directly impact return on investment.

The question should not be, β€œis Rails a safe choice,” but β€œ[how long] can we justify the expense of traditional development approaches.”

I think this kind of approach plays nicely to the strengths of SaaS.

In terms of large enterprise deployments, it’s early yet: Enterprises tend to be conservative (about Rails as they are about SaaS) so most of the innovation has been in the startup space, with companies like Hulu being good examples of the disruptive power of Rails.

But that said, there have been some major mainstream Enterprise success stories. AT&T chose to dump a failing Java yellowpages effort in favor of Rails, with excellent results in terms of scalability, time to market, code quality, and performance. (There’s a decent write-up on BuildingWebApps.)

We are starting to see major companies develop ever more mission/business/revenue-critical components in Rails. BestBuy built Remix, their new public API app, with us using Rails. We have one major multinational client who is rewriting their entire ERP system in Ruby internally. We have another major hardware vendor building new products using Rails.

Large companies tend to be a bit shy about talking about new technology initiatives, and we suspect that most Fortune 500 companies have someone doing Rails somewhere in the organization. There are a number of others we’ve spoken to who are using the technology to their advantage, but who aren’t ready to talk about it publicly yet. But you can also search for job listings from major companies and see how many big companies are hiring Rails developers. We see them all the time.

Hard statistics are harder to come by, but Mark Driver at Gartner projected that there would be 4 million ruby programmers by 2013. We’re already seeing the smart companies get huge efficiencies out of these new development models: efficiencies of cost, flexibility, and time to market.

We’re very bullish on Agile, Rails and the Cloud. In the current economic climate, the reduction in risk alone is worth the cost of admission. Coupled with the qualitative benefits of being able to out-flank your competition, it’s no surprise that we’re continuing to see adoption grow so rapidly. The results are too compelling to ignore.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Nate Clark

Standup 9/14/2009: State Machines, comments in ERB, test-unit 2.0.3 breaks RSpec

Nate Clark
Monday, September 14, 2009

Ask For Help

“Anybody use an off-the-shelf State Machine plugin for Rails? Which one?”

Our team recommends using the rubyist-aasm gem from Github. It works well for our current applications, and it appears to be the most actively developed and frequently updated. This gem is the successor to the acts_as_state_machine plugin.

Interesting Things

Don’t use comments in ERB. Writing comments (or any ‘#’ character, for that matter) inside an ERB evaluation block was never right, but some versions of Ruby 1.8.6 allowed it anyway. This is trouble if you upgrade to Ruby 1.8.7 (for example, when upgrading to Snow Leopard) which does not allow comments within ERB.

This is wrong:

<%= link_to "/", home_path #link to the homepage %>

If you really need comments in ERB, do it this way:

<%# link to the homepage %>
<%= link_to "/", home_path %>

test-unit 2.0.3 breaks RSpec. If you upgrade to test-unit 2.0.3, you may notice that your RSpec specs no longer run. Roll back to test-unit < 2.0 (or remove the test-unit gem completely) and you should be back in business.

RubyMine 1.5 early release is available for trying out. Preliminary reports from one Pivot are positive. If you are using Snow Leopard, which ships with Java 1.6, you’ll need to do this hack to get it to run

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

Simple DRY Validations

Pivotal Labs
Monday, September 14, 2009

Here’s a handy trick for making custom validations easily reusable.

This is an extract from a customer model with three different street addresses, in which we validate all three of the zip codes. (In this code, the GeoState.valid_zip_code? method answers if something that looks like a zip code is an actual zip code – not all five digit combinations are in use as zip codes, and we want to make sure we’ve got a live one.)

def validate_home_zip_code
  validate_zip_code(:home_zip_code)
end

def validate_mailing_zip_code
  validate_zip_code(:mailing_zip_code)
end

def validate_previous_zip_code
  validate_zip_code(:previous_zip_code)
end

def validate_zip_code(field)
  errors.add(field, :inclusion) if errors.on(field).nil? && !GeoState.valid_zip_code?(send(field))
end

validates_presence_of :home_zip_code
validates_format_of :home_zip_code, :with => /^d{5}(-d{4})?$/, :allow_blank => true
validate :validate_home_zip_code

validates_presence_of :mailing_zip_code
validates_format_of :mailing_zip_code, :with => /^d{5}(-d{4})?$/, :allow_blank => true
validate :validate_mailing_zip_code

validates_presence_of :previous_zip_code
validates_format_of :previous_zip_code, :with => /^d{5}(-d{4})?$/, :allow_blank => true
validate :validate_previous_zip_code

That looks very wet to me. (WET == “Write Every Time”) But it’s not too hard to dry this up using just a tiny bit of knowledge of how ActiveRecord validations work.

Wouldn’t it be nice to get rid of those three different validation methods and just have one thing to use to validate the zip code is real?

def self.validates_zip_code(*attr_names)
  configuration = { }
  configuration.update(attr_names.extract_options!)

  validates_each(attr_names, configuration) do |record, attr_name, value|
    if record.errors.on(attr_name).nil? && !GeoState.valid_zip_code?(record.send(attr_name))
      record.errors.add(attr_name, :invalid_zip, :default => configuration[:message], :value => value)
    end
  end
end

validates_presence_of :home_zip_code
validates_format_of :home_zip_code, :with => /^d{5}(-d{4})?$/, :allow_blank => true
validates_zip_code :home_zip_code

validates_presence_of :mailing_zip_code
validates_format_of :mailing_zip_code, :with => /^d{5}(-d{4})?$/, :allow_blank => true
validates_zip_code :mailing_zip_code

validates_presence_of :previous_zip_code
validates_format_of :previous_zip_code, :with => /^d{5}(-d{4})?$/, :allow_blank => true
validates_zip_code :previous_zip_code

That’s a bit better. Before we had three custom validation methods that did all the same work using an extracted method. We have replaced those three methods with a single custom class-level validation, validates_zip_code, which validates a zip code attribute by name. It’s used just like any other standard validation, and as many times as you want. Nice and dry! The implementation of validates_zip_code is modeled on some of the standard validation methods found in ActiveRecord’s Validations module (in 'active_record/validations.rb'). Take a look at that code for more examples of how you might write your own custom validations.

Now that’s a good start, but we can do even better. We can take the custom validation and fold in the other validations we do on each zip code.

def self.validates_zip_code(*attr_names)
  configuration = { }
  configuration.update(attr_names.extract_options!)

  validates_presence_of(attr_names, configuration)
  validates_format_of(attr_names, configuration.merge(:with => /^d{5}(-d{4})?$/, :allow_blank => true));

  validates_each(attr_names, configuration) do |record, attr_name, value|
    if record.errors.on(attr_name).nil? && !GeoState.valid_zip_code?(record.send(attr_name))
      record.errors.add(attr_name, :invalid_zip, :default => configuration[:message], :value => value)
    end
  end
end

validates_zip_code :home_zip_code
validates_zip_code :mailing_zip_code
validates_zip_code :previous_zip_code

This example was extracted from the version history of the code for a project I worked on recently. It follows the very steps we took to refactor the validations step by step. I know that Jeff Dean has contributed a cool validator object enhancement that will be appearing in Rails 3, but this approach works out of the box with the current version of Rails, so you don’t have to turn blue holding your breath.

Final note: If you’re wondering why we validate presence and then allow blank on the following validations, it’s so that the user only gets one error message at a time.

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

Standup 9/11/2009: to_param now required in functional tests

Pivotal Labs
Friday, September 11, 2009

Ask for Help

“Do you have to use to_param in functional tests?”

In the past you could simply provide an object in a param list in functional tests and the to_param for the object would be called to get the proper value for the parameter. This is now broken, forcing you to use object.to_param every time.

Perhaps not helpful for existing projects but I recommend you use cucumber, webrat, or even selenium rather than Rails functional tests. Rails functional tests require that you specify parameters and specify them correctly. If you get them wrong your functional tests might continue to pass for the wrong reason. Here’s another “bad params in functional tests” post.

Interesting Things

  • Rubyconf program should be available today on their website
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Standup 9/10/2009: "All Alias, All The Time"

Pivotal Labs
Thursday, September 10, 2009

Interesting Things

  • One team reported that Capistrano can’t deal with host aliases in .ssh/config. Others use host aliases with capistrano without trouble. The problem could be related to multiple host aliases. All this led to the statement:

    If you are working with EY, you should go all alias, all the time.

  • Problems with JsUnit and Firefox 3.5. From Adam:

    If you’re planning to use JsUnit, or you already use JsUnit
    and you plan to upgrade your Firefox to version 3.5, you
    may run into this problem. Apparently the security settings
    in Firefox were updated in 3.5 to restrict file access in a way
    that stymies JsUnit. If you don’t fix this JsUnit will fail to open
    test files, and will simply hang on tests runs, with no error
    output. To fix it:

    • type ‘about:config’ in the Firefox URL bar
    • Filter by ‘security.fileuri.strict_origin_policy’
    • Set this to false
  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (780)
  • 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 (20)
  • cucumber (20)
  • 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)
  • 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. →
  • 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 >