agile-ajax

What’s In Your Junk Drawer?

Continuing my recent theme of utilities and the like that I can't do without...

For as long as I've been programming, I've built up a junk drawer of useful functions that I carry from project to project. The Ruby iteration of this is currently implemented as a Rails plugin that is one of the first things I add to each new project.

Here are a few of the utensils that I keep jammed in the back of the drawer.


One of the best pieces of advice I can give about keeping code clean within a method is to normalize the data first, then get on with the business of working with the data. The advantage is that it keeps the main line of your method from having all kinds of special case complications. I sometimes use this monkey-patch special to help within models.

class Integer
  def to_active_record(ar_class)
    ar_class.find(self)
  end
 
  def to_active_record_id
    self
  end
end
 
module ActiveRecord
  class Base
    def to_active_record(ar_class)
      self
    end
 
    def to_active_record_id
      self.id
    end
  end
end

This makes ActiveRecord::Base and Integer duck-type identical for to_active_record_id and
to_active_record (though I admit that requiring the class for to_active_record is a little bit ugly.

The main use case for this is in a model method that might get called from a controller with a parameter (which, come to think of it, might argue for a string version) or from elsewhere in the model with a full record. Typical use is like this (I realize there are other ways of doing this...):

def has_subscription_for(sub_or_id)
  sub_id = sub_or_id.to_active_record_id
  subscriptions.map(&:id).includes?(sub_id)
end

Moving on... I do a lot of date processing, and use the excellent Chronic gem for a lot of it (makes for a nice client demo, when you type in "last Thursday" into a form and it works...). I use the following methods added to Date to cleanly integrate Chronic into Date parsing.

class Date
  def self.parse_or_nil(string, comp = false)
    parse(string, comp)
  rescue ArgumentError
    nil
  end
 
  def self.smart_parse(string, default = nil, comp = false)
    default ||= Date.today
    return default if string.blank?
    date = Chronic.parse(string, :context => :past) ||
           Chronic.parse(string.gsub(",", " "), :context => :past) ||
           Date.parse_or_nil(string, comp) ||
           default
    return default if date.blank?
    date.to_date
  end
end
 
class String
 
  def smart_to_date
    Date.smart_parse(self)
  end
 
end

The smart_parse method takes a default to return if the string doesn't parse to a date. It calls Chronic first (the dual call to Chronic is a workaround for a bug in Chronic), then tries regular Date parsing. The string method is there for a slight readability boost.

Here's a quickie that I wind up using a lot:

class Object
  def in?(*enumerable)
    enumerable.flatten.include?(self)
  end
 
  def not_in?(*enumerable)
    !in?(*enumerable)
  end
end

Usage looks like foo.in?("a", "b", "c"). The flatten makes it flexible, but does adds some weirdness of the object is itself an array, which I'd consider an unusual case.

This one gets used from time to time:

class String
  def is_true?
    %w(true t 1 y yes).include?(self.to_s.downcase)
  end
end

I think I originally wrote this to convert from a legacy database which was giving me boolean true as "1".

Finally, a test helper I wind up using a lot, this goes in the test_helper.rb file inside TestCase

  def assert_methods(actual, expected = {})
    expected.each do |key, value|
      assert_equal(actual.send(key), value,
          "Expected <#{value}> for #{key}, got <#{actual.send(key)}>")
    end
  end

It's a good way to cover a lot of behavior at once, again, testing a data migration is a common use case, or when creating an entire object in the code -- basically, it's a flexible assert_equals. Usage is like this:

assert_entity(actual, :product => product, :date => product.date,
        :description => product.description, :price => product.price)

What's in your junk drawer?

Topics:

Comments: 1 so far

  1. in has_subscription_for I would prefer to use
    self.subscription_ids.include?(sub_or_id.to_active_record_id)
    Works only if self object has many subscription asociation

    Comment by Seban, Saturday, August 30, 2008 @ 4:52 am

Leave a comment

Powered by WP Hashcash

Who is Pathfinder?

Topics

Search

WordPress

Comments about this site: info@pathf.com