We do a lot of data migrations for Donor Tools – a lot of folks are coming to us from other systems, and they need their data ported over. Depending on the kind of system they were using, this can be anywhere from a quick script to a major data transformation headache.

Recently I needed a quick way to parse a full name string into name parts. Given a name like “Dr. Joe Donor, M.D.”, I wanted to end up with a name object with a prefix of “Dr.”, a suffix of “M.D.”, a first name of “Joe”, and a last name of “Donor”. Complicating matters, it also needed to be able to handle odd permutations like “Dr. and Mrs. Joe and Jane Donor”, etc.

The main problem that I had was with the “and”. If there was an “and”, it should put the preceding and following words together to form a single word. This turned out to be nearly impossible with regular expressions, but pretty easy with a combination of Ruby and regex. Here’s how it looks.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Name < ActiveRecord::Base

  def self.parse(name)
    return false unless name.is_a?(String)
    
    # First, split the name into an array
    parts = name.split
    
    # If any part is "and", then put together the two parts around it
    # For example, "Mr. and Mrs." or "Mickey and Minnie"
    parts.each_with_index do |part, i|
      if ["and", "&"].include?(part) and i > 0
        p3 = parts.delete_at(i+1)
        p2 = parts.at(i)
        p1 = parts.delete_at(i-1)
        parts[i-1] = [p1, p2, p3].join(" ")
      end
    end
    
    # Build a hash of the remaining parts
    {
      :suffix => (s = parts.pop unless parts.last !~ /(\w+\.|[IVXLM]+|[A-Z]+)$/),
      :last_name  => (l = parts.pop),
      :prefix => (p = parts.shift unless parts[0] !~ /^\w+\./),
      :first_name => (f = parts.shift),
      :middle_name => (m = parts.join(" "))
    }
  end

end

Here’s the output:

1
2
3
4
5
6
7
8
9
10
11
Name.parse "Mr. Joe Donor"
=> {:middle_name=>"", :prefix=>"Mr.", :last_name=>"Donor", :suffix=>nil, :first_name=>"Joe"}

Name.parse "Dr. and Mrs. Joe Donor, M.D."
=> {:middle_name=>"", :prefix=>"Dr. and Mrs.", :last_name=>"Donor,", :suffix=>"M.D.", :first_name=>"Joe"}

Name.parse "Joe and Jane Donor"
=> {:middle_name=>"", :prefix=>nil, :last_name=>"Donor", :suffix=>nil, :first_name=>"Joe and Jane"}

Name.parse "Joe and Jane Major-Donor"
=> {:middle_name=>"", :prefix=>nil, :last_name=>"Major-Donor", :suffix=>nil, :first_name=>"Joe and Jane"}

application/octet-stream WTF?

December 17th, 2008

Lately I’ve been having this problem with uploaded files using the Paperclip plugin (though I don’t think it has anything to do with the plugin).

My customers are trying to upload CSV files to their Donor Tools account. Normally, when you upload a .csv file, the content type is set to “text/csv” or some variation of “text/*”.

Here’s the problem: when uploading .csv files in Windows using any version of IE or Firefox, the content type is set to “application/octet-stream”.

What the flip?

This only occurs with IE or Firefox. Safari for Windows uploads the exact same file with the content type set correctly to “text/csv”.

To get around the problem I’ve temporarily turned off the file type validation, which I don’t like because I really don’t want to be accepting uploaded binary files.

Has anyone seen this problem before? How did you deal with it?


Here’s a quick and easy way to track exactly where your signups are coming from, and who referred any given customer, without having to cross-reference your analytics program.

1
2
3
4
5
before_filter: set_referer

def set_referer
  cookies[:referer] = request.env['HTTP_REFERER'] if cookies[:referer].nil?
end

The set_referer method sets up a session variable that keeps the original referrer with your customer, no matter how many pages they click through before signing up.

You can email this variable to yourself when they sign up, or store it in the database to keep track of who came from where.


Ryan Bates has a helpful screencast about the using the excellent Thinking Sphinx Rails plugin for the Sphinx search engine.

There seems to be a bit of confusion over how to enable wildcard (star) searches with Thinking Sphinx. Wildcard searches would let you search for partial words, so “thin*” would match “Thinking Sphinx” and “Thinner”. This is pretty useful, and for Donor Tools it is essential.

Turns out it’s really easy to turn on wildcard search. There is no need to make any changes to your Sphinx setup or add a config/sphinx.yml file. In your define_index block, simply add enable_star and min_prefix_len like so:

1
2
3
4
5
define_index do
  ...
  set_property :enable_star => true
  set_property :min_prefix_len => 3
end

enable_star simply turns on wildcard searching. However, this won’t do much good unless you also enable prefix indexing using min_prefix_len.

min_prefix_len sets the minimum number of letters that Sphinx will index. From the Sphinx docs:

...indexing a keyword “example” with min_prefix_len=3 will result in indexing “exa”, “exam”, “examp”, “exampl” prefixes along with the word itself.

You can also set min_infix_len, which does the same thing as min_prefix_len, except it does it on the middle of the word.

Infix indexing allows to implement wildcard searching by ‘start*’, ’*end’, and ’*middle*’ wildcards.

Caution: Infix indexing can cause your index to grow, and may slow down searching.

Now, re-run rake thinking_sphinx:configure, re-index (rake ts:in), and restart the Sphinx daemon (rake ts:run), and it should work.


PassiveRecord

October 2nd, 2008

Ever need to use structured data that doesn’t need its own database model? Ryan Bates shows you how in a recent Railscast: Non Active Record Model.

If you have more than one non ActiveRecord model, you’ll find yourself starting to duplicate a lot of code. I did recently, so I extracted a bunch of functionality into a plugin called PassiveRecord.

There's more! Read the rest of this entry



State Machine Simulator

April 1st, 2008

If you need to create a wizard, with forward and back buttons for several different steps, a great way to do it is to use the acts_as_state_machine plugin.

But for just two or three steps, loading a whole plugin seems like overkill. Instead of loading a whole plugin, you can simulate the same functionality by dropping next! and previous! methods into your model like so:


  # State Machine 
  # Guilt-free stateful modeling! 
  # These two simple methods simulate all the goodness of 
  #   acts_as_state_machine without the added fat of an included plugin. 
  def next!
    next_step = case self.state
    when "step1" 
      "step2" 
    when "step2" 
      "step3" 
    when "step3" 
      "step3" 
    end
    self.update_attribute(:state, next_step)
  end

  def previous!
    previous_step = case self.state
    when "step1" 
      "step1" 
    when "step2" 
      "step1" 
    when "step3" 
      "step2" 
    end
    self.update_attribute(:state, previous_step)
  end

  def current_step
    @current_step ||= self.state
  end

attachment_fu on Edge Rails

February 26th, 2008

Speaking of Edge Rails… attachment_fu will break because Edge has extracted callbacks out into a separate module.

To prevent unnecessary pain and suffering, see http://blog.methodmissing.com/2008/1/19/edge-callback-refactorings-attachment_fu/

Just paste that whole blob at the bottom of your attachment_fu.rb file and you’re good to go.


check_box broken in Rails 2.0.2?

February 26th, 2008

I was having an awful time submitting a form with a check_box. No matter what I did, it would only return “0” or “false”, never “1” or “true”, no matter how hard I mashed the mouse button on the check box.


  <%= check_box :fund, :tax_deductible %> <%= f.label :tax_deductible %>

I finally tried moving to edge Rails (from Rails 2.0.2), and the problem went away. Anyone else experience this?


Introducing Donor Tools

December 14th, 2007

With a little bit of fanfare, we recently announced Donor Tools, a new donor management app for non-profits.

I used to work at a small non-profit, and I was never satisfied with the software that was available to us. It all seemed either too hard to use, too clunky, too slow, or 20 years old. Ever since then I dreamed of making a killer app for non-profits – one that’s easy to use, has just a few features that every non-profit needs (and not a lot of extra features that only a few need), helps non-profits get to know their donors better, and is affordable.

We’ve been writing code for Donor Tools since July, and it’s exciting to watch it take shape. There’s still a lot to be done. If you’d like to keep an eye on the development progress, be sure to subscribe to the news feed.

Pretty soon we’ll need some beta testers. If you know of any nonprofit organizations that might be interested in helping us beta test, please invite them to subscribe to the mailing list on www.donortools.com


Radiant Factory

November 29th, 2007

Say hello to Radiant Factory, our new theming service for Radiant CMS.

Basically, Radiant Factory is a theming service for Radiant CMS. We take your design and content and turn it into a series of layouts, snippets, and pages that you (or your client) can manage with the fabulous Radiant content management system.

I’ve fallen in love with Radiant’s easy, straightforward website administration. Honestly, I always hated content management and website update tasks until Radiant came along. The idea behind Radiant Factory is simple: share the love. Radiant Factory is my way of helping web designers provide an even better product to their clients.

So check it out and let me know what you think. Your feedback is appreciated!


Sitepoint Rails Book Giveaway

November 27th, 2007

Only five days left to get a free copy of Sitepoint’s Build Your Own Ruby on Rails Web Applications

It’s a pretty good book for beginners – very complete; over 400 pages.


WebAppers

June 26th, 2007

WebAppers – Only the Best and Free Resources for Web Application Developers

Where has this been all my life?


has_one :through

June 20th, 2007

Via: http://idm.s9.xrea.com/ratio/2006/08/04/000496.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class ActiveRecord::Associations::HasOneThroughAssociation < ActiveRecord::Associations::HasOneAssociation
  private
    def construct_sql
      @finder_sql = "#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.primary_key_name} = #{@owner.quoted_id}"
      @finder_sql << " AND (#{conditions})" if conditions
    end

    def find_target
      @reflection.klass.find(:first,
        :select => @reflection.options[:select] || "#{@reflection.table_name}.*",
        :conditions => @finder_sql,
        :order => @reflection.options[:order],
        :include => @reflection.options[:include],
        :joins => "LEFT OUTER JOIN #{@reflection.through_reflection.table_name} " +
                  " ON #{@reflection.through_reflection.table_name}.#{@reflection.source_reflection.primary_key_name} " +
                "    = #{@reflection.table_name}.#{@reflection.klass.primary_key}"
      )
    end
end

module ActiveRecord::Associations::ClassMethods
  def has_one_with_through(association_id, options = {})
    if options[:through]
      reflection = create_has_one_through_reflection(association_id, options)
      association_accessor_methods(reflection, ActiveRecord::Associations::HasOneThroughAssociation)
    else
      has_one_without_through(association_id, options)
    end
  end

  alias_method :has_one_without_through, :has_one
  alias_method :has_one, :has_one_with_through

  private
    def create_has_one_through_reflection(association_id, options)
      options.assert_valid_keys(
        :class_name, :foreign_key, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through
      )
      create_reflection(:has_one, association_id, options, self)
    end
end

CSSEdit with Rails

June 19th, 2007

I love CSSEdit. It has to be one of the prettiest OS X programs out there, and it makes editing CSS a breeze.

The one thing that has annoyed me though has been using the override feature when developing with Rails. Rails likes to append an asset id on every CSS, JavaScript, and image file that it serves. If I have screen.css, Rails would embed it like screen.css?123456789. So every time I would reload the page in CSSEdit, the style sheet override would be broken, because the asset id would be different.

There is a fast and wonderfully simple solution. Just add this line in config/environments/development.rb:


ENV["RAILS_ASSET_ID"] = ""

Voila! No more asset id in development mode. No more broken overrides.

Credit: MacRabbit Blog


[UPDATE] This article accidentally got deleted, along with all the comments. Shame on my trigger-happy mouse finger, which clicked “OK” without reading the contents of the delete confirmation. So, thanks to Jeff Whitmire who found the text of the article in his RSS feed. Sorry to those who commented – there were some good thoughtful ones.

In my Rails projects, I sometimes have trouble remembering all my complex routes. Not only that, it’s a bummer to have to type out that long line every time I need to generate a URL.

So instead, I let the models tell me their URLs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Article < ActiveRecord::Base
belongs_to :category

  include ActionController::UrlWriter
  default_url_options[:host] = "www.example.com"

  def url
    category_article_url(self.category, self)
  end

  def path
    category_article_path(self.category, self)
  end

end

Now I can say @article.url, which will generate


http://www.example.com/categories/123/articles/456-my-article

or @article.path, which will give me just the path:


/categories/123/articles/456-my-article