Monday, October 29, 2012

Ruby 1.8.7 vs 1.9.3 performance

There are plenty of Ruby 1.8 to 1.9 benchmark results out there, and this is by no means as thorough as most.  I thought I'd share the results for one of my websites when I upgraded it from Ruby 1.8.7 to Ruby 1.9.3 as demonstrated in my New Relic report for the site. Conveniently, I upgraded the site on a Monday morning, and since that's also when New Relic reports switch over, it provided a fairly clean week-to-week comparison (perhaps 1/3 of Monday 10/22 was still Ruby 1.8.7).

High Level Summary

With no other changes, other than fixes required to allow the application to work with Ruby 1.9.3, simply upgrading Ruby resulted in the overall response time average for the week dropped from 304ms to 72ms (a 76.32% drop in response time).

Unfortunately, the week immediately before the upgrade was a bit of an anomaly.  The week before that had a 153ms average response time, which is more in line with typical weekly results.  However, that's still a 53% reduction in response time from 1.8.7 to 1.9.3.

Here's a daily comparison for the 3 weeks:

Ruby 1.8.7 


Ruby 1.9.3



Keep up the good work, Ruby development team!

attachment_fu as a gem for Rails 3.2

As I've mentioned before, I develop the web site for St. Francis Society Animal Rescue.  As I've also described in that earlier article, it started as a Rails 1.2 app, then Rails 2.1, then 2.3.  Right now it's in Rails 3.1, but I'm about to switch it to Rails 3.2.  Having some history, it was developed to use attachment_fu to upload all the images for the cats and dogs.  While I know many people have abandoned attachment_fu for paperclip, or carrierwave, or dragonfly, etc. attachment_fu has continued to work just fine for me so if it ain't broke, don't fix it.  Plus, there are currently over 13,000 animal images that have been uploaded, and I don't really want to hassle with converting them over to a new attachment system.

Enter Ruby 1.9.x

The last official update to the attachment_fu github repository was on April 25, 2009.  While that update is for Ruby 1.9 compatibility fixes, if you try to use it as is for Ruby 1.9.3, you'll find it won't work.  To that end, I've forked off a new repository that will fix additional Ruby 1.9.3 incompatibilities.  If you want to continue to use attachment_fu as a plugin with Ruby 1.9.x and Rails prior to 3.2, you can use my github repository in your project simply by issuing this command in your project:

git submodule add https://github.com/pothoven/attachment_fu.git vendor/plugins/attachment_fu

Enter Rails 3.2.x

attachment_fu has always functioned as a plugin (vendor/plugins), the problem is that Rails 3.2 will give you this error if you continue to use it as a plugin:

DEPRECATION WARNING: You have Rails 2.3-style plugins in vendor/plugins! Support for these plugins will be removed in Rails 4.0. 
Move them out and bundle them in your Gemfile, or fold them in to your app as lib/myplugin/* and config/initializers/myplugin.rb. 
See the release notes for more on this: http://weblog.rubyonrails.org/2012/1/4/rails-3-2-0-rc2-has-been-released.

To that end, I've also updated my fork of attachment_fu to function as a gem! Simply add this line to your Gemfile
 
gem 'pothoven-attachment_fu'

I need to acknowledge Christophe Porteneuve for doing most of the gem conversion work. I just pulled in his updates and fixed a few problems with it that I encountered. Please feel free to let me know if you have any problems using the gem.

Thursday, October 04, 2012

Self-marking required fields in Rails

David Sulc wrote a nice article entitled, Self-marking required fields in Rails 3 that utilized a key element of Ryan Bates' Railscast on Validations in Rails 3.  In Ryan's original solution, he creates a new helper method named mark_required that uses the Rails validation reflection to determine if the model requires that the field to be present (via validates :presence => true or validates_presence_of).  If the field is required, this helper method adds a '*', otherwise it does nothing. Using this helper method in your code, your code would look like:
<%= f.label :name %><%= mark_required(@user, :name) %>
<%= f.text_field :name %>  

David improves upon this idea by enhancing the ActionView::Helpers::FormBuilder label helper instead of adding a new helper.  This eliminates the need to add the extra call to the mark_required helper for every label, which can be easily forgotten.  If the field is required, the enhanced label helper appends a "*" to the label text to indicate that the field is required.  So now your code is back to the normal form of:
<%= f.label :name %>
<%= f.text_field :name %>  

That's a nice improvement, but I thought it would be an even better design practice to simply give the label a required CSS class, and then allow the designer to be able to easily modify how required fields should be indicated.   My version of config/initializers/form_builder.rb looks like:

class ActionView::Helpers::FormBuilder
  alias :orig_label :label

  # add a 'required' CSS class to the field label if the field is required
  def label(method, content_or_options = nil, options = nil, &block)
    if content_or_options && content_or_options.class == Hash
      options = content_or_options
    else
      content = content_or_options
    end

    if object.class.validators_on(method).map(&:class).include? ActiveModel::Validations::PresenceValidator
      if options.class != Hash
        options = {:class => "required"}
      else
        options[:class] = ((options[:class] || "") + " required").split(" ").uniq.join(" ")
      end
    end

    self.orig_label(method, content, options || {}, &block)
  end
end
With this revised version in place, all labels of required fields are given a required CSS class.  If you want to indicate required fields with an asterisk after the label, you can do that by adding this CSS rule to your stylesheet,

/* add required field asterisk */
label.required:after {
    content: " *";
}
However, by extracting the method of indicating that the field is required from being hard-coded in the helper to instead using a CSS class, anything you can do with CSS is open to you and can be quickly and easily modified without updating the helper code.  You can change colors, fonts, borders, add images before or after, you could even use the dreaded text-decoration:blink.  The options are nearly endless.

Update for Rails 4! 


Thanks to the input from alex_m and Dan in the comments below, the follow revised version should work for Rails 4 (with ActiveAdmin):

class ActionView::Helpers::FormBuilder
  alias :orig_label :label

  # add a 'required' CSS class to the field label if the field is required
  def label(method, content_or_options = nil, options = nil, &block)
    if content_or_options && content_or_options.class == Hash
      options = content_or_options
    else
      content = content_or_options
    end

    if object.class.respond_to?(:validators_on) &&
      object.class.validators_on(method).map(&:class).include?(ActiveRecord::Validations::PresenceValidator)

      if options.class != Hash
        options = {:class => "required"}
      else
        options[:class] = ((options[:class] || "") + " required").split(" ").uniq.join(" ")
      end
    end

    self.orig_label(method, content, options || {}, &block)
  end
end

Thanks again alex_m and Dan!

Thursday, May 10, 2012

Rails Rumble 2010

In my long absence from posting, I see I totally skipped 2010.
For the 2010 Rails Rumble we created Commendable Kids.

Here are the results for our entry CommendableKids.com:
  • Finished 4th place out of 180 teams
  • Winners of the Appearance Category
  • Runners Up from Chargify as Most Potential to Monetize
    Read More
  • #1 team from the U.S.
  • Tech Crunch Top 5
    Read the Review

St. Francis Society Animal Rescue

One of my side-projects is the development of the St. Francis Society Animal Rescue web site.  My friend Brian Burridge and I originally converted the site from a static web site to a Ruby on Rails site several years ago (2008 timeframe).  I believe it was initially a Rails 1.2 project.  Besides the public site, it includes a fairly involved back-end administration component that beyond just allowing content management of the web site, performs all the animal rescue administration functions (detailed animal information with health records, adoption records, etc.).  At the time we were both fairly new to Ruby on Rails and we decided to use ActiveScaffold to build this administration component.

At some point we upgraded to Rails 2.1 and then in 2010, Brian left the project to be able to better focus on his other numerous projects and I further upgraded it to Rails 2.3.x.  These Rails version upgrades were more effort than a typical Rails upgrades may be due to various gem dependencies, most specifically ActiveScaffold.  ActiveScaffold is really a pretty nice framework for admin sites, but it had limitations that required work-arounds and those work-arounds often didn't work when upgrading.

During the end of 2011 to the beginning of 2012, I did a more drastic upgrade.  I migrated to Rails 3.1.x.  However, this wasn't just a simple migration, I decided to re-write the entire application.  The most involved part of this re-write is what most people will never see, the administration area.  I decided to totally abandon ActiveScaffold.  Brian told me about ActiveAdmin that he was using on some of his other projects, but after fighting with ActiveScaffold for long, I opted to stay clear of such a major framework dependency and just wrote the entire admin from scratch with straight Rails.

Comparisons of the old and new administration pages.

Here's a comparison of the listing of the cats.  I reduced the amount of information displayed on the list to reduce some clutter, and I've made some of the frequently changed values available to be changed directly on the list (save instantly via Ajax) to eliminate the need to open the edit form just to change a status.


Filtering the list required an extension to ActiveScaffold that was problematic to upgrade, and it resulted in a very large area added to the top of the list.  Now it uses a jQuery UI dialog.


ActiveScaffold constructed its forms very vertically which didn't utilize the space well.  I now have full control of the layout allowing me to organize things better.



Responsive Web Design

Last summer at the 2011 front-end design conference I had the privilege of having Ethan Marcotte introduce me to the idea of responsive web design.  So, I took that to heart and designed both the public site and the admin area to be responsive.  So, if we look at the cat listing page again and compare it between a browser and an iPhone, you can see that the navigation menu has collapsed and the table has dropped several columns.

There are also intermediate changes for tablets, but it's time I wrap this up.

I'm only touching on a very few of the page layouts and features.  Beyond a new look and feel, the re-write of the application also brought performance improvements.  Here is a New Relic report on the week I switched it over from the old to the new.

Note: Thursday was the transition day, so it should be ignored.  The jump in CPU percentage is due to also switching from a shared hosting environment on DreamHost to a Linode slice since DreamHost doesn't support Rails 3.1 at this time -- at least not on my server)




I encourage you to take a look at the St. Francis Society Animal Rescue public site, particularly if you're in the Tampa Bay area and are interested in a new pet.  Be sure to try it out in different sizes to see how the responsive layout works, and please let me know what you think of it.

Adding and removing multiple associated sub-models dynamically on a single form in Rails

The last couple blog posts have been about PeepNote ... its beginning and its ending.  Now I thought I'd share a snippet of code that I developed for it the form of a simple contact manager.

One of the things Brian pointed out in his concluding remarks about PeepNote that I referred to in my last post was,
As I began interacting more with potential customers I realized that my target audience was not what I thought it was. It wasn’t people like me who were heavily using Twitter for career networking and wanted to keep track of how I met people and what I knew about them. Instead, the only people that would pay for the service were companies. Companies that wanted to use it to track potential customers; a CRM.
Part of the CRM functionality that we had incorporated into PeepNote was to add contact information about your peeps.  The idea of a contact manager is well understood by most audiences and are thus commonly used as a demonstration application.  So, what's special about this one and why I'm I writing about it?

This particular contact manager demonstrates (and improves upon) Ryan Bates “Handle Multiple Models in One Form” recipe (#13) from the Advanced Rails Recipes book.  In Ryan’s original recipe, he added discrete field values (individual tasks on a to-do list) to the form. Any tasks that did not have an identifier associated with it was a new task, and any missing task identifiers from those currently in the database were assumed to be deleted.  This is great when you're essentially adding single values to a list, but what about adding multiple pieces of information that need to stay together as a single entity (like the parts of a contact information - street address, city, state, phone, etc)?

The solution is to use a JavaScript variable to assign temporary unique identifiers to newly added nested models (addresses, phone numbers, urls, etc). These newly added models are assigned a negative value in order for the contact controller to distinguish between new and existing records.  So, new records with a negative id are added, existing records are updated as necessary, and any records with ids that are no longer in the submitted list are removed.

This is one of the problems I solved for PeepNote and have extracted into a simple stand-alone Rails application.   Rather than provide a lengthy description here, I placed the source code on GitHub and have a running demo on Heroku.

PeepNote conclusion

I realize it's been a very long time since my last post, but I am still around.

In regards to that last post, I thought I'd share a blog entry by my RailsRumble teammate discussing the status of PeepNote:

PeepNote: The Rumble, the Startup, and now…the Conclusion