Close and Go BackBack to Viget

Tips and Tricks

Simple jQuery Solution to Provide Default Values for Page Elements

Tony Pitale
Tony Pitale, Web Developer, November 26, 2008 8

When outputting sets of data that may or may not be present it is valuable to have some default to display in the cases when that data does not exist.  As this set grows to the point where detection becomes unwieldy, perhaps as new fields are added, having complex conditionals in your view code is typically not the best solution. In this example, Dining & Kitchen and Appliances have no information.

Listing Price Information
  • Original Price: $239,999.00
  • Low Price: $139,000.00
Dining & Kitchen
Appliances
Bedroom Information
  • Main Floor Beds: 2

Enter jQuery: with a simple statement we can easily detect which elements on our page are empty, and fill them with some default value to notify a user of that case.

$('ul').each(function() {
  if(jQuery.trim($(this).html()) == "") {
    $(this).html("<li>No information available</li>");
  }
});

The above code checks every unordered list element on the page to see if the html within is empty. If so, it adds a simple list item with a default value to inform the user that the section on the page has no information, instead of simply being empty. The end result is below.

Listing Price Information
  • Original Price: $239,999.00
  • Low Price: $139,000.00
Dining & Kitchen
  • No information available
Appliances
  • No information available
Bedroom Information
  • Main Floor Beds: 2

This solution has the benefit of speed, and a fairly accessible global reach, while keeping view code and helpers free of potentially gargantuan if statements.

Protip: TimeWithZone, All The Time

David Eisinger
David Eisinger, Web Developer, September 10, 2008 0

If you’ve ever tried to retrieve a list of ActiveRecord objects based on their timestamps, you’ve probably been bitten by the quirky time support in Rails:

>> Goal.create(:description => "Run a mile")
=> #<Goal id: 1, description: "Run a mile",
     created_at: "2008-09-09 19:32:57",
     updated_at: "2008-09-09 19:32:57">
>> Goal.find(:all, :conditions => ['created_at < ?',
     Time.now])
=> []

Huh? Checking the logs, we see that the two commands above correspond to the following queries:

INSERT INTO "goals" ("updated_at", "description",
  "created_at") VALUES('2008-09-09 19:32:57',
  'Run a mile', '2008-09-09 19:32:57')
SELECT * FROM "goals" WHERE created_at < '2008-09-09 15:33:17'

Rails stores created_at relative to Coordinated Universal Time, while Time.now is based on the system clock, running four hours behind. The solution? ActiveSupport’s TimeWithZone:

>> Goal.find(:all, :conditions => ['created_at < ?',
     Time.zone.now])
=> [#<Goal id: 1, description: "Run a mile",
     created_at: "2008-09-09 19:32:57",
     updated_at: "2008-09-09 19:32:57">]

Rule of thumb: always use TimeWithZone in your Rails projects. Date, Time and DateTime simply don’t play well with ActiveRecord. Instantiate it with Time.zone.now and Time.zone.local. To discard the time element, use beginning_of_day.

BONUS TIP

Since it’s a subclass of Time, interpolating a range of TimeWithZone objects fills in every second between the two times — not so useful if you need a date for every day in a month:

>> t = Time.zone.now
=> Tue, 09 Sep 2008 14:26:45 EDT -04:00
>> (t..(t + 1.month)).to_a.size
[9 minutes later]
=> 2592001

Fortunately, the desired behavior is just a monkeypatch away:

class ActiveSupport::TimeWithZone
  def succ
    self + 1.day
  end
end

>> (t..(t + 1.month)).to_a.size
=> 31

For more information about time zones in Rails, Geoff Buesing and Ryan Daigle have good, up-to-date posts.

Unpublished Keyboard Shortcuts on Mac OS X

Tony Pitale
Tony Pitale, Web Developer, September 04, 2008 9

This is just the quickest of tips for everyone using Mac OS X.

Keyboard shortcuts are one of my favorite features on Mac OS X and something that I think few operating systems get right. Not only are there a staggering number of predefined shortcuts (some aren’t even advertised, but we’ll get to that in a moment) but also, for any menu item, a custom shortcut can be defined within System Preferences -> Keyboard & Mouse -> Keyboard Shortcuts (the right-most tab).

I find this to be immensely useful, especially in overriding the shortcut behavior of a variety of third party applications that have the same function. For example, Firefox, Textmate, and almost every other application that has tabbed windows provides a shortcut on the keyboard to switch between tabs (left and right). For Apple’s application, the shortcut is likely to be Command-Option-Arrow Key. By default many other applications use Command-Option-Brace (curly brace, or square bracket?). A quick pair of keyboard shortcuts overrides these behaviors to be standard to your shortcut preference.

If you can’t recall a keyboard shortcut, users of the latest version 10.5 (Leopard), can make instant use of the help menu which, like Spotlight for files, will search for text amongst the seemingly endless sea of menu options. This feature has saved me time, and again, in applications such as any of the Adobe suite of products.

Lastly, I’d like to touch on three very useful but, unadvertised, keyboard shortcuts.
Use Command-Option-Eject Key to set your computer into a blissful sleep state,
Command-Control-Eject Key to reset your Mac immediately, or
combine those two and get Command-Option-Control-Eject Key to shut it down until you need it again.

Overall, the mouse is unbelievably slow for pre-defined activities, in comparison to a keyboard shortcut. Thanks to Apple’s latest OS X, you don’t even have to remember them all.

Protip: Use Your Factories in Development

Clinton R. Nixon
Clinton R. Nixon, Senior Developer, August 17, 2008 2

I’ve been training a new Rails developer recently, and I’ve found it rewarding. His questions are great: without already-built habits, he thinks of ways to do things I never would.

Last week, he said, “I need to test this view with some real data to make sure it looks OK. We have all these factory methods to use in our unit tests. Can we use those in development?” I almost said no, but then I realized it was a pretty great idea.

We’re using FixtureReplacement on this project, but this technique will work with any factories. We opened script/console and typed the following:

require 'fixture_replacement'
include FixtureReplacement
require RAILS_ROOT + '/db/example_data'

With that, I could use, for example, our create_completed_order method to make a purchased cart with multiple items in it so we could see how the admin interface was looking. It’s no replacement for good testing, but for quick view checks, it’s a pretty cool trick.

Named Scope Caching

Brian Landau
Brian Landau, Web Developer, August 07, 2008 3

When working on high-traffic Rails sites, it often becomes necessary to find ways to improve performance with caching. One place we’ve found this is most convenient and easy-to-do is by caching an ActiveRecord result set for models that change rarely or not at all. An easy example of this is a Category model.

Often times, you have a categorization hierarchy that will never or rarely change over the life of an application. Ideally you would fetch the results once from the database and never have to again. So how do we go about caching this? First let’s look at our model and create a named_scope for it:

class Category < ActiveRecord::Base
  acts_as_tree
  named_scope :find_top_level, :conditions => 'categories.parent_id IS NULL',
                              :order      => 'categories.name'
end

Next, we need to create create a method that fetches the results for our new scope and caches it in a class variable. It should also only do caching if in production environment (alternatively or additionally, we could use the ActionController.perform_caching config value), as this can cause problems in tests.

def self.top_level
  unless ('production' == RAILS_ENV) && ActionController.perform_caching
    @@top_level_cache = self.find_top_level
  else
    @@top_level_cache ||= self.find_top_level
  end
end

Finally, we need to create a method to invalidate our cache when records are saved or deleted. Since we know this isn’t happening often (if at all), this should rarely be performed but is a good safeguard so we know our cache is current.

after_save :reset_cached_finder
after_destroy :reset_cached_finder

def reset_cached_finder
  @@top_level_cache = nil
end

This is something that we could easily see doing in a number of models for a number of finders. Since this involves a lot of similar code, it would be great if we could create some meta code that would allow us to define these caches with a simple one liner.

Continue reading "Named Scope Caching"

We're the Developers

at Viget Labs. We write about web development trends, tips, best practices, industry events, and our projects — all with an emphasis on Ruby on Rails.

Recent Comments

Tony,

I understand and agree that the back-end shouldn’t output code (html code), and only content. The templates (aka views) should do the trick, but instead of having lot’s of if/else conditionals inside the view, you may just output the following content.

No information available

The template would loop in an array and put all the <li>’s inside the <ul>.
I don’t see anything wrong, nor...