Bulletproof Web Design

Bulletproof Web DesignImproving flexibility and protecting against worst-case scenarios with XHTML and CSS by Dan Cederholm

Let me start off by saying I’m a programmer, not a designer. Before reading Bulletproof Web Design, I had a basic understanding of just enough CSS structure and concepts to get by. This approach lead to very inefficient markup that was hard to read and maintain. What I was missing was a deeper understanding as to when to use the different constructs and why. I found this and much more in this book.

Dan Cederholm used a brilliant format in Bulletproof Web Design. Each chapter takes a single concept illustrated by an example site that employees a traditional “unbulletproof” approach and explains the pitfalls of the traditional methods. He then deconstructs the page and rebuilds it step-by-step using semantic XHTML and CSS. The books step-by-step approach of modifying only a couple lines of CSS and explaining the results make the book a quick, yet informative read.

The book starts by explaining why and how to design your site for flexible text sizes. He uses this as the driving point for the rest of the book. How to make your navigation, tables, tabs, lists, widget boxes, rounded corners, and layouts flexible. How to design your site to be valuable to users who either can not or choose not to use images and / or style sheets. The book ends with the step-by-step approach of creating a page that ties every concept together.

Reading the book has made me feel much more confident in my CSS usage. I have already seen the payoff as I have used the methodologies in the book to both design new widgets and to refactor existing code. I feel lucky to have stumbled upon it and am looking forward to reading his sequel book, Handcrafted CSS — More Bulletproof Web Design.

ActiveRecord object caching in Ruby on Rails

Yesterday I started playing with basic caching for PriceChirp and tried what I thought would be easy. Boy was I wrong. It turns out what I was attempting to do is not supported by :memory_store in the development environment. Before moving to :mem_cache_store, I was able to find a work around. The work around is outlined below for those who do not have the option of using memcached. However, if you can use memcached, it is by far the better route to take.

My goal was to cut down on the number of database hit by caching the resultant ActiveRecord object in :memory_store.


#models/foo.rb
 def self.cool_widget
 Rails.cache.fetch('cache_object_name') { find_by_name('cool_widget') }
 end

#config/environments/development
 config.action_controller.perform_caching = true
 config.cache_store = :memory_store

#controler/foos_controllers.rb
 awesome_widget = foo.cool_widget
 logger.info awesome_widget.id

In script/console and in rails production, this works as you would expect. However, in the rails development environment, funny things happen. On the first page view, all looks good. On the second page view, as rails attempts to pull the object out of cache, rails cashes with the following error:

 .../activerecord/lib/active_record/attribute_methods.rb:142:in `create_time_zone_conversion_attribute?'
 

A google search lead me to:

https://rails.lighthouseapp.com/projects/8994/tickets/1290-activerecord-raises-randomly-apparently-a-timezone-issue

Here they talk known problems about caching Active Record objects on development servers. Something about the way the object are torn down between page loads to allow for every hit to get a new instance of the controller.

At the very end of the discussion (over the course of a year), I got the following work around:

http://gist.github.com/251886


# put this in lib/nil_store.rb:
 class NilStore < ActiveSupport::Cache::Store
 def initialize(location='//myloc'); end
 def read(name, options = nil); super; end
 def write(name, value, options = nil); super; end
 def delete(name, options = nil); super; end
 def delete_matched(matcher, options = nil); super; end
 end

# and then in environments/development.rb:

require 'nil_store'
 config.cache_store = NilStore.new

# or you could just turn on development class caching:

config.cache_classes = true

This “nil_store” routine basically prevent caching in the development environment which makes everything work. On production, I did not notice any problems.

Other work arounds to this bug include, not caching the ActiveRecord object or using a different caching routine, like memcached.

DrupalCamp Austin 2009

I had a great time this weekend at DrupalCamp Austin, 2009. DrupalCamp was a “Barcamp” styled meetup with a strong emphases on Drupal. At the camp I was introduced to Drush. Drush is the Drupal Shell and provides tools for managing your Drupal projects with shell scripts. I can see great use for Drush at the office to help quickly create new Drupal sites in a standardized fashion. It can also be used to run maintenance scripts.

Other topics included Panels 3, Drupal SEO, and JQuery UI being added to Drupal 7 core. The improvements to Panels in Panels 3 are significant. The prevailing wisdom at the conference was that Panels 3 will replace blocks for most cases.

During the keynote talk, they performed a basic DISC personality assessment on everyone at the camp. The test performed was way over simplified (two questions) and the results over generalized. It was interesting seeing how people reacted to the test. One result that was curious is while about 75% of the attendees consider themselves to be introverts, the conference had their happy hour at a karaoke bar. That did not seem well thought out. Then again, several of us stayed their until 2 in the morning.

Overall, the camp was fun. I met a lot of good people. And I hope to go to another camp soon.

PriceChirp has improved wishlist support


This week I improved the Amazon wishlist support in PriceChirp. One of the cool features of PriceChirp from the beginning has been how easy it is to import an Amazon wishlist into PriceChirp. The only problem with this feature is it was an all or nothing proposition. Now, we have the ability to view our wishlists in PriceChirp and select which items we wish to import. The old feature of importing everything is still thee, but now we have options.

To see this feature in action, log into your PriceChirp account and do a wishlist search. This is done by searching for the email address associated to your Amazon wishlist. Once your wishlists are displayed, you can select “view wishlist” to get a listing of your items. From this page you can easily select which item to import into your personalized tracker.

Have fun!

PriceChirp tracks prices on Internationial Amazon sites


PriceChirp is growing. This week I added support to allow people to track prices and be alerted of changes for all the international Amazon sites. This includes Amazon US, Canada, France, Germany, Japan, and the UK. Just select the location of Amazon you are interested in searching, and use PriceChirp like normal. It was designed to make it easy to manage products from multiple sites at once. I’m hoping this design decision will pay off if in the future I add more vendors to PriceChirp.

Upgradeing from Drupal 5.20 to 6.14

Today I upgraded from Drupal 5.20 to 6.14. I’ve been dreading the upgrade to the Drupal 6.x series for almost 9 months because I had no idea how hard it would be or what I would lose in the migration.

The instructions on the Drupal site are fine, but they didn’t prepare me for what would be different.

  • Back everything up
  • Deselect all non-core modules
  • Select a core template
  • Move the old Drupal version out of the way
  • Install the new version of Drupal
  • Copy your /files and /sites directory from the old installation to the new installation
  • Copy other misc files like your robots.txt to the new installation
  • Fix the /sites/default/settings.php
  • Run the update script — Pray

If all has gone well, you have an upgraded core. Now for the tedious part. Start upgrading each module one at a time, running your update script between each one.

After upgrading every module that had a Drupal 6.x version, here is what I lost:

  • My site template
  • All my views
  • Most of my custom menu items (Links to anything that did not upgrade cleanly was lost — ie.. links to my views, links to my galleries, etc…)

Differences of note:

  • Drupal 6 is much faster
  • HTML comments started appearing in my stories. I had to install the htmlcomment modules to get my comments to hide again
  • The views configuration menu is much improved
  • Status Report is now part of core
  • Gallery module is much improved
  • The wysiwyg module is more flexible than the old tinymce module

In the end, I am very happy with the upgrade. I still have work as I will need to find / hack a new template sometime so I don’t look like a default Drupal install.

Number to Currency Outside Rails

I created a module I am also using both in rails and for some batch processing outside of rails. In order to ensure my data was formatted the same with both environments, I needed to figure out how to get access to the active_view helper number_to_currency outside of rails. Here is how I did it:

require 'rubygems'
require 'action_view'

def number_to_currency(number, options = {})
  ActionView::Base.new.number_to_currency(number, options)
end

Rescuing from open-uri timeouts

Found an issue with open-uri because today Amazon’s API is wonky. The same issue will occur when accessing any remote data via open-uri (like a RSS feed) if the data source is going to slow. The relevant part of my ruby code looks like:

  begin
    doc = Nokogiri(open(api_url))
  rescue
    print "Connection failed: #{$!}\n"
    next
  end

For some reason, the rescue does not catch a timeout error.

/usr/lib64/ruby/1.8/timeout.rb:54:in `rbuf_fill': execution expired (Timeout::Error)

Explicitly catching the timeout error fixes the code.

  begin
    doc = Nokogiri(open(api_url))
  rescue Timeout::Error
    print "Timeout::Error: #{$!}\n"
    next
  rescue
    print "Connection failed: #{$!}\n"
    next
  end

Good to know, as you never know when your remote data provider will be slow.

Gravatars on Rails


Gravatar is the globally recognized avatar run by the folks who run WordPress.com.

Your Gravatar is an image that follows you from site to site appearing beside your name when you do things like comment or post on a blog. This is done by associating an image to the users email address. Adding Gravatar to PriceChirp seemed like a fun thing to do.

The API is very simple, so adding it to a Ruby on Rails site is easy.

First, add a few lines to your application-helper.rb:

  def gravatar_url_for(email, options = {})
    url_for({
      :gravatar_id => Digest::MD5.hexdigest(email),
      :host => 'www.gravatar.com',
      :protocol => 'http://',
      :only_path => false,
      :skip_relative_url_root => true,
      :controller => '/avatar'
    }.merge(options)
    )
  end

You may also need to add:

require 'digest/md5'

Now you can add the gravatar to your views like this:

  # standard gravatar url
  <%= gravatar_url_for 'user@gmail.com' %>

  # gravatar url with a rating threshold
  <%= gravatar_url_for 'user@gmail.com', { :rating => 'R' } %>

  # show the avatar
  <%= image_tag(gravatar_url_for 'email@gmail.com')%>

  # change the size of the avatar
  <%= image_tag(gravatar_url_for('email@gmail.com', { :size => 30 })) %>

Note: Gravatar is case sensitive on the user’s email address. They will match against an all lower case version of the email address. If you need to allow uppercase characters in your email addresses, you will need to .downcase the email address in the view or helper. I do not have this requirement, so I fix the case in the user.rb model at save time:

  before_save :fix_email_case

  protected

  def fix_email_case
    if !self.email.nil?
      self.email.downcase!
    end
  end