Article moved to http://www.pivotalblabs.com/articles/2007/10/15/dot-rake
Monthly Archives: September 2007
Framework Wars: "Now all restaurants are Taco Bell."
Software development is full of framework wars: passionate debates about technology choices that unfortunately degenerate into winning and losing, rather than understanding benefits, risks, and suitability for contexts.
I have unfortunately found myself embroiled in more than one such battle and haven’t always been a balanced contributor. What amazes me though is the common drive towards absolute resolution, the quest to find the “right answer.”
There is a scene in the movie Demolition Man that I think offers a good perspective on these issues. At one point, Sylvester Stallone’s character is confused by an invitation to dine at Taco Bell. Sandra Bullock’s character notes that there were restaurant wars, and “Now all restaurants are Taco Bell.”
Nothing against Taco Bell specifically, but do you want to live in a world where the only restaurant option is Taco Bell?
A common corollary argument in such framework debates is that the industry, or some relevant subset community, is embracing and standardizing on some particular platform, and as such, this is offered as justification for following suit.
While this line of thought sometimes has merits, its important to consider the Taco Bell scenario: if all restaurants are Taco Bell, sometimes its preferable to make dinner at home.
Treetop 1.0.1
I’ve been working on a parsing framework for Ruby since January called Treetop. I just released version 1.0.1 and recorded this screencast demonstrating its use.
String#split and regex lookarounds
Ruby supports regular expression quite well, so it’s not surprising that one can use a lookaround to match a specific position. That also works really well with String#split, so if one needs to extract the key/value out of a string such as this:
s = “TYPE=Exterior RED=119 GREEN=105 BLUE=88 TABLEREQ=PNTTBL-01 TABLE=Primary w/XL GENERICCLR=Beige GENERICCLRCODE=31″
this will do nicely:
s.split(/s(?=[A-Z]*=)/)
Just splitting on “space” won’t work because there are spaces inside the values, too (”Primary w/XML”).
Simple and useful…
Ruby quiz du jour answer
Josh got it right.
1.0/0
indeed resolves to “Infinity”. Pretty cool!
-Felix
pong

ping is awesome, but it’s a little simple-minded. I often find myself running several pings at a time, especially when debugging network configurations. And when you’re watching ping run in several windows, as routers go up and down and packets spurt and congeal like blood in a trauma center, extra features spring naturally to mind…
Enter pong. It’s a Ruby app I wrote that runs ping in the background and decorates the results, tracking statistics in realtime, refreshing the screen every 5 seconds. Here, I’ll show you:
% pong localhost 192.168.1.240 192.168.1.1 google.com yahoo.com pivotalblabs.com
Last 10 seconds:
Mean Missing Host
0.073 msec 0 (0.00%) localhost (127.0.0.1)
2.884 msec 0 (0.00%) 192.168.1.240 (192.168.1.240)
25.745 msec 0 (0.00%) 192.168.1.1 (192.168.1.1)
98.905 msec 0 (0.00%) google.com (72.14.207.99)
38.761 msec 0 (0.00%) yahoo.com (66.94.234.13)
113.418 msec 0 (0.00%) pivotalblabs.com (72.9.100.34)
Last 60 seconds:
Mean Missing Host
0.092 msec 0 (0.00%) localhost (127.0.0.1)
5.757 msec 0 (0.00%) 192.168.1.240 (192.168.1.240)
24.198 msec 0 (0.00%) 192.168.1.1 (192.168.1.1)
98.840 msec 1 (1.67%) google.com (72.14.207.99)
38.504 msec 0 (0.00%) yahoo.com (66.94.234.13)
110.687 msec 0 (0.00%) pivotalblabs.com (72.9.100.34)
Entire run (97 sec):
Mean Missing Host
0.088 msec 0 (0.00%) localhost (127.0.0.1)
5.716 msec 0 (0.00%) 192.168.1.240 (192.168.1.240)
23.713 msec 1 (1.03%) 192.168.1.1 (192.168.1.1)
99.261 msec 1 (1.03%) google.com (72.14.207.99)
35.403 msec 0 (0.00%) yahoo.com (66.94.234.13)
105.349 msec 4 (4.12%) pivotalblabs.com (72.9.100.34)
I’ve packaged it up as a gem and put the source on RubyForge. Install it with
sudo gem install pong
and let me know what you think. Bug reports, feature requests, and (naturally) patches welcome. Currently it’s only been tested on OS X and might work on other Unixes.
(Image of Edmond Lau’s two-player analog oscilloscope Pong.)
Ruby quiz du jour
Question:
In Ruby, what does the following represent?
-1.0/0…0
Post your guesses as comments. Answer tomorrow. (No fair using irb.)
- Felix and Alex and Koung
HasFinder — It's Now Easier than ever to create complex, re-usable SQL queries
HasFinder is an extension to ActiveRecord that makes it easier than ever to create custom find and count queries.
Let’s start with an example. Suppose you have an Article model; some articles are published, some are popular. Let’s declare finders for each of these notions. You may be tempted to write something like the following:
class Article < ActiveRecord::Base
def self.published
find(:all, :conditions => {:published => true})
end
def self.popular
...
end
...
end
But there are serious limitations to this approach. How do you find articles that are BOTH popular and published? How can you easily paginate published articles?
HasFinder Features
Let’s define the equivalent finders using my new plugin, has_finder:
class Article < ActiveRecord::Base
has_finder :published, :conditions => {:published => true}
has_finder :popular, :conditions => ...
end
Query Composition
Now, you can elegantly compose queries:
Article.published.popular
This will return all articles that are both popular and published.
Calculations and Nested Finds
You can also easily paginate or call nested finders or do calculations:
Article.published.paginate(:page => 1)
Article.published.popular.count
Article.popular.find(:first)
Article.popular.find(:all, :conditions => {...})
Works with ActiveRecord has_many and has_many :through Associations
Furthermore, without any additional work, these finders will work with ActiveRecord associations.
class User
has_many :articles
end
user.articles.popular.find(:first)
user.articles.published.popular.average(:view_count)
Finders are extendable just like ActiveRecord Associations
class Article
has_finder :unpublished :conditions => {:published => false} do
def published_all
find(:all).map(&:publish)
end
end
end
Alternatively, you can use the :extend options:
class Article
has_finder :unpublished, :conditions => ..., :extend => MyExtensionModule
end
Finders behave just like ActiveRecord Associations
For example, you can call #reload:
Article.published.popular.reload
Finders can take parameters
class Car
has_finder :colored, lambda {|color| { :conditions => {:color => color} } }
end
Car.colored('red').paginate(:page => 1)
What makes HasFinder better than alternatives like scope_out and scoped_proxy?
There are already two plugins similar to HasFinder: scope_out and scoped_proxy. Both of them are excellent. In fact, Scoped Proxy was the model for HasFinder. Unfortunately, neither plugin provided all of the features I desired. Scope-Out lacks on-the-fly composition, a nice way to call a nested find, or the ability to do arbitrary calculations. Scoped Proxy is great, but it doesn’t work with regular ActiveRecord Associations, it is not extendable like ActiveRecord associations, and it doesn’t behave exactly like a regular ActiveRecord Association. Neither of them work out of the box with will_paginate. For all of these reasons and more, I rolled my own. It’s now available as a gem.
Installation
% gem install has_finder
Usage
In environment.rb:
gem 'has_finder'
require 'has_finder'
See the examples above for usage.