this is totally gonna work… » Ruby

ActiveRecord Fun Thay May Stump Only Me

July 23rd, 2008

I’ve just spent the last two hours pulling my hair out trying to get Single-Table Inheritance (STI) working with associations in ActiveRecord. After essentially walking through all of the possible ActiveRecord options in this setup, I finally stumbled upon a configuration that seems to work. So this post is an attempt to help the next poor bastard who is Googling in earnest for a solution to a similar problem.

So let’s start with the domain model. I’m too spent at this point in the evening to port this to one of the standard examples. Instead I’ll expose you to the domain of my particular problem. The app I’m working on is one that tracks (non-financial) lending transactions between two individuals. The parties involved, the item in question and when it’s due are all tracked in the Transaction model (and transactions table). A Transaction has a number of states it walks through, using the acts_as_state_machine plugin. These state transitions are triggered by opaque-looking URLs that are sent via email to either party. These are one-time use actions that once consumed are no longer available. When an Action instance is created it also has a before_save callback that generates a unique ID (used in the URL) using Digest::SHA1.

So my plan was to have my Transaction class write one or more Action records for each possible action based on my state transitions. Take a look at the state diagram below:

state-transitions.png

I want to encapsulate the actual work to be performed within the Action instance the user invokes by following the link in their email. So my plan is to use STI to have different sub-classes of Action that operate on a transaction and march it forward to its next state polymorphically.

Now STI may appear to be total overkill for this problem, but here are my reasons for going this route:

  • I want to have these opaque IDs written down somewhere to associate a specific action with a URL
  • When the action is complete, I want to remove the record so it can’t be performed again
  • The state for a given Transaction can have more than one possible action. I want a separate for each action.

Whew. Okay, clear so far?So my initial code looked something like this:

require digest/sha1

class Action < ActiveRecord::Base

  belongs_to :transaction
  before_save :create_guid

  def create_guid
    sha1 = Digest::SHA1.new
    sha1.update transaction_id.to_s
    sha1.update type.downcase
    sha1.update DateTime.to_s
    self.guid = sha1.hexdigest
  end
end

class ReturnAction < Action
  def execute
    transaction.return!
  end
end

class AbortAction < Action
  def execute
    transaction.abort!
  end
end

class DisputeAction < Action
  def execute
    transaction.abort!
  end
end

It seemed like a good idea at the time, but the strange thing was that no matter which incantation I tried, I simply couldn’t create a new Action instance and have it write a record to the database. This simply didn’t work:

ReturnAction.create! :transaction_id => 1

There were no errors on the returned object. No exceptions were thrown. No queries to the database and certainly no insert statements executed. Just complete and utter silence. Out of desperation, as much as anything else, I removed the belongs_to declaration from the Action class and instead declared a has_many on the Transaction class. Voila! It worked like a champ.

After a bit of thought, the has_many association makes complete sense to me in the case where we want to create new Action instances for a particular Transaction. However, if you look in the code above, the execute methods of each sub-class are referring to a transaction object/method—which I no longer have. However I don’t necessarily need the full-blown belongs_to association here. I can just fake the bits I want in the parent Action class like so:

class Action < ActiveRecord::Base
  def transaction
    @transaction ||= Transaction.find(self.transaction_id)
  end
end

So none if this is particularly earth-shattering. Sorry folks, no great gems of philosophical wisdom today. Just one man’s small accomplishment blown completely out of proportion.

Posted in Rails, Ruby | 1 Comment »

clip version 0.0.6 has been released!

July 10th, 2008

You like command-line parsing, but you hate all of the bloat. Why
should you have to create a Hash, then create a parser, fill the Hash
out then throw the parser away (unless you want to print out a usage
message) and deal with a Hash? Why, for Pete’s sake, should the parser
and the parsed values be handled by two different objects?

Changes:

0.0.6 / 2008-07-10

* Fixed a bug with getting the ‘remainder’ when only flags are declared.

*

Posted in Ruby | 1 Comment »

gemdoc completion in zsh

June 25th, 2008

This week I stumbled upon Stephen Celis’ awesome bit of shell-fu, gemdoc, which allows you to quickly get to the HTML docs for installed gems via command-line. Unfortunately I abandoned bash years ago for zsh and Stephen’s shell bits needed a little porting. For me, zsh, is a bit like swiss-army knife where about 95% of it is a mystery to me, but the 5% I use I couldn’t live without. So simply switching back to bash is a no-go.

My setup is little-bit complicated, but I believe the following, stripped-down, recipe should work:

GEMDIR=$(gem env gemdir)
OPEN=$(whence xdg-open || whence open)

gemdoc() {
  ${OPEN} $GEMDIR/doc/`$(which ls) $GEMDIR/doc | grep $1 | sort | tail -1`/rdoc/index.html
}

_gemdocomplete() {
  reply=( `$(which ls) $GEMDIR/doc` )
}

compctl -K _gemdocomplete gemdoc

Update 6/25/08-10:30: Updated to work for both Penguins and Macs.

Posted in Ruby | No Comments »

clip version 0.0.5 has been released!

June 13th, 2008

You like command-line parsing, but you hate all of the bloat. Why should you have to create a Hash, then create a parser, fill the Hash out then throw the parser away (unless you want to print out a usage message) and deal with a Hash? Why, for Pete’s sake, should the parser and the parsed values be handled by two different objects?

Changes:

0.0.5 / 2008-06-12

  • Removed sample_parser from bin (technomancy)
  • fix a stupid bug causing an infinite loop for empty ARGV (technomancy)
  • http://clip.rubyforge.org
Posted in Ruby | 1 Comment »

clip version 0.0.4 has been released!

June 6th, 2008

You like command-line parsing, but you hate all of the bloat. Why
should you have to create a Hash, then create a parser, fill the Hash
out then throw the parser away (unless you want to print out a usage
message) and deal with a Hash? Why, for Pete’s sake, should the parser
and the parsed values be handled by two different objects?

Changes:

=== 0.0.4 / 2008-06-06

* Fixed typo in error message (thanks francois!)

=== 0.0.3 / 2008-06-05

* Merged technomancy’s patches for simple 1 LOC parsing -> hash

Posted in Ruby | No Comments »

RailsConf ‘08 Wrapup

June 3rd, 2008

RailsConf closed up last Sunday afternoon and after three-hour drive back and day of work to contemplate, here’s what I’ve boiled it down to:

The Good:

I met a ton of people this year. Last year I went with a co-worker and we pretty much stuck together. This year I was on my own and made a concerted effort to just meet folks. By the end of the conference I couldn’t go more than about fifteen minutes without running into someone I had previously met. I ordered a fresh batch of Moo cards before I left and I was hell-bent to hand as many out as I could. Just meeting people turned out to be my favorite part of the conference.

The Kent Beck address was fantastic. I’ve had the fortune earlier in my career to work at a company that had Kent come out and run XP workshops with us. Those experiences left a last impression on me (much like Chad Fowler expressed in the introduction) and so I was eager to hear his talk after seeing him on the roster. While his main content probably dragged for a bit, the Q & A ended with a bang. The answer he gave to the final question expressing a mixture of hope and concern brought the crowd to its feet. Go on, Kent.

DSCN2566

There were a couple of presentations I went to that I though really knocked it out of the park. In some cases the material alone saved the day (in spite of the presenters) and in a few other cases the two came together nicely. I thought some of these presentations were particularly good:

  • “Facebook Development and Performance with Rails” – Mike Mangino
  • “The Launch: Do’s and Don’ts of Real-life Deploys” – Chris Wanstrath
  • “Assembling Pages Last: Edge Caching, ESI & Rails” – Aaron Batalion
  • “Skynet: A Ruby Map/Reduce Framework” – Adam Pisoni
  • “Vertebra” – Ezra Zygmuntowicz
  • “Advanced ActiveRecord Techniques: Best Practice Refactoring” – Chad Pytel

The Bad:

Sadly, a number of the presentations were pretty lacking. Now I think presenting is just plain hard and very few people are good at it. Keynote helps a bit, but really it’s a crutch for people who don’t have good public speaking skills (which I’m not necessarily claiming I have). Really exceptional content can help overcome the stylistic short-comings of a particular speaker, but I think that’s rare. I think it’s pretty easy to lose an audience quickly if your material can’t shine in the way you present it.

Since I have another year of Ruby and Rails experience under my belt, many more of the talks just really didn’t do anything for me. That’s why I’m psyched that the RailsConf team has decided to incorporate Caboose Conf as the “hallway track” in next year’s meeting. I think that’s the track I’ll be taking next year. I did a lot of ingesting this year, next year I’d really like to produce more.

The Style Report:

Apparently the black t-shirt is king in the Rails community. Something like 99% of all attendees had black t-shirts on. Of the free t-shifts to be had, the GitHub tees were the only ones that weren’t black. All of the others were red on black. So apparently the new black is, well, black. I’m hoping that nuclear orange makes a comeback next year. All-black is just a little too Depeche Mode for me.

Portland:

Portland deserves special mention because I just flat-out love that city. Besides Seattle, it’s probably the only other city I would choose to live in. One of the highlights of experiencing the local flair was getting involved in the 1000-person “doughnut march” held by Portland’s beloved Voodoo Doughnuts. Somehow they convinced officials to get a parade permit and police escort as they crossed from their Pearl District digs to their new shop in East PDX. It was a very “Portland” experience with a whole crowd of folks letting their freak-flags fly high.

Voodoo Doughnuts Parade

The big treat at the end was the bacon maple-bar. I’m not kidding folks, this is real and it’s freakin’ brilliant. So hear this Chad Fowler and the rest of the RailsConf committee, please don’t move this to Vegas! Besides, can you imagine putting a bunch of hygienically-challenged nerds in 100-degree heat in a desert? That is simply not a good idea.

Bacon maple bar

Posted in Rails, Ruby | 1 Comment »

clip version 0.0.2 has been released!

May 20th, 2008

You like command-line parsing, but you hate all of the bloat. Why
should you have to create a Hash, then create a parser, fill the Hash
out then throw the parser away (unless you want to print out a usage
message) and deal with a Hash? Why, for Pete’s sake, should the parser
and the parsed values be handled by two different objects?

Changes:

=== 0.0.2 / 2008-05-20

* Cleaned up README
* Added support for late-binding option processing with blocks

Posted in Ruby | No Comments »

clip version 0.0.1 has been released!

April 11th, 2008

You like command-line parsing, but you hate all of the bloat. Why should you have to create a Hash, then create a parser, fill the Hash out then throw the parser away (unless you want to print out a usage message) and deal with a Hash? Why, for Pete’s sake, should the parser and the parsed values be handled by two different objects?

Introducing Clip…

Checkout the details at http://clip.rubyforge.org, or just dive right in and install that sucker as a gem.

Changes:

0.0.1 / 2008-04-10

  • Initial release for y’all to throw rotten veggies at.

Posted in Ruby | No Comments »

PCI4R Update

March 23rd, 2008

We finally made some progress this week on the languishing pci4r project. First, congrats to Sandro Paganotti for the first commit to pci4r–the prize is in the mail. This morning, after a bit of git-fiddling, I managed to get the second commit for the project in. It’s code for document classification, which is the topic of Chapter 6 of Toby Segaran’s “Programming Collective Intelligence”. I deviated quite a bit from Toby’s original code. In some cases this was simply a side-effect of porting from Python to idiomatic Ruby. In other cases though changes were made for simple aesthetic reasons.

In short, here’s what you can do:

  c = Filtering::NaiveBayes.new

  c.train("Nobody owns the water", :good)
  c.train("the quick rabbit jumps fences", :good)
  c.train("buy pharmaceuticals now", :bad)
  c.train("make quick money at the online casino", :bad)
  c.train("the quick brown fox jumps", :good)

  c.prob("quick rabbit", :good)  #=> ~ 0.156
  c.prob("quick rabbit", :bad)   #=> ~ 0.050

Here we create a new NaiveBayes classifier, train it with some text and then query it with other text. Nifty eh? There is another classifier included in the package called Fisher, which has a slightly more clever classification algorithm.

Both of these default to in-memory storage of classification data. You can override it by using the built-in ActiveRecord persistence adapter like so:

  ar_config = Filtering::Persistence::ActiveRecordAdapter(
    :adapter => "sqlite3",
    :database => "mydb.sqlite3"
    :timeout => 5000
  )
  c = Filtering::NaiveBayes(ar_config)

Finally, there’s an executable in the ‘bin’ directory where you can interactively classify RSS feeds using either of the classifiers or persistence mechanisms provided. This relies on the
feed_tools gem.

So there it is. The rest of the pci4r team should be spooling up soon and hopefully we’ll make some more progress. Stay tuned…

Posted in Ruby | No Comments »

Yet More RSpec Fun With TextMate!

December 15th, 2007

Why is it that I just can’t leave well enough alone? Here, after several weeks for forehead-impact conditioning, I finally get a working setup with Ruby on Leopard with RSpec and TextMate. Life is good, I have my pretty spec runner window back, I’m a BDD’ing fool. But oh no, I have to keep fiddling with stuff.

So I’m finally putting some foundation work into a nifty little webapp that a (childhood) buddy of mine and I are working on. “Hey”, I think to myself, “why not make it a Rails 2.0 app? Better yet, why not get yer RSpec on too?” Brilliant. Why not? What could possibly go wrong? Well after installing the trunk versions of the RSpec plugin for Rails (of course) my beloved RSpec bundle stopped working. :-(

After a little digging around (including the OS X equivalent of printf debugging, Growl) I realized that I’m probably facing incompatibility issues with the TM bundle and the version of RSpec. Now keep in mind that I’ve got two different RSpec-based projects that I’m using the bundle with. One is a straight-up lib-and-spec directory app (the Programming Collective Intelligence port), and the other is this new Rails app. The former was using version 1.0.8 of the gem, while the latter is using the plugin code that I installed.

So I go back to the RSpec bundle that the RSpec folks put out and lo and behold it works like a champ for my Rails app…but not so much for the standalone project. Oh dear. Now what? Like Doug Flutie vs. Miami I chuck a Hail Mary and run sudo gem update rspec

Updating installed gems...
Attempting remote update of rspec
Successfully installed rspec-1.1.0
1 gem installed
Installing ri documentation for rspec-1.1.0...
Installing RDoc documentation for rspec-1.1.0...
Gems: [rspec] updated

Yayyy! A new version! Now the RSpec bundle for TextMate works in both projects. So, the final recipe goes a little something like this:

  1. Install the latest bleeding-edge RSpec plugin for Rails
  2. Install the RSpec version of the TextMate bundle
  3. Make sure the latest gem (1.1.0) is installed.

OK, now has anyone seen my productivity around here? I’d swear I saw it just before Halloween…

Posted in Mac, Ruby | No Comments »