Don't Try This At Home
Mar 07
On the Mac there's a nifty little utility called Growl. It's a system-wide notification service that other applications use to display attractive popup status messages. There are all kinds of programs that utilize it, namely instant messaging apps and other small utilities. Sometimes they're really useful, other times they're fun and occasionally they're a little bit stupid.
I wrote a really stupid one. I mean a really, really stupid one. I added Growl notifications to dashing.com.
So now every time someone visits this site a little window pops up on my desktop announcing it. Is that the stupidest thing ever or what? :-)

Now I confess that it is kind of cool. But the idea of the website sending notifications to my desktop Mac whenever people visit is really a terrible idea. But it was such a neat little hack I had to do it. :-)
So for you Rails programmers who are bored, here's how I did it.
First download Growl and install it if you don't already have it.
Next you'll need Ruby Growl. You can install that via RubyGems.
sudo gem install ruby-growl
Once that's installed add an after_filter to the top of your application.rb controller file.
after_filter :growl
Then add the following code to the bottom of application.rb.
Note: I'm using the Acts As Authenticated style user model code and helpers, so you may need to change this for your app, or remove the username display entirely.
1 private
2 require 'ruby-growl'
3 def growl
4 if logged_in?
5 user = current_user.login
6 else
7 user = request.env['REMOTE_ADDR']
8 end
9 g = Growl.new 'home.server.address', 'Website Visitors',['visit'], ['visit'], 'PASSWORD'
10 g.notify 'visit', 'yoursite.com',"#{user} visited #{request.env['REQUEST_URI']}", 0, false
11 rescue
12 # Something didn't work
13 end
You'll want to replace "home.server.address" with the IP address or hostname of your Mac. Replace "PASSWORD" with your Growl network password and "yoursite.com" with the name of your website.
You'll need to enable incoming connections in the Network section of the Growl control panel, and be sure to set a password. Also check the "Allow remote application registration" button, at least until you've received your first message. Finally, if you have a firewall you'll also need to allow port 9887 access to your Mac over UDP.
That's all there is to it. It's really pretty fun to see all of the popups...at first anyway. Though I can't imagine this would work well with even a moderately popular website. In fact even dashing.com is too busy for this and I'll likely turn it off shortly. But it was a fun hack just the same.
Maybe this will inspire someone to come up with a useful reason to incorporate Growl into a Rails app? If you do, be sure to let me know.
Disclaimer: Yes I'm fully aware the 99.9% of the people who read this site will not only have no idea what I'm talking about, but wouldn't care even if they did. But for the .1% who do, this is really, really cool! :-)
Recently at work I've been writing a Ruby On Rails application for our Network group to monitor and track our environment. The fellow I've been working with has written a bunch of really amazing Perl scripts that scour the network and collect lots of information about all of the devices. I'm writing the Rails front end to work with that data, and in the process teaching him Ruby (and Rails), so once we're done he can take it over and maintain the app. It was while showing him some things in IRB that I discovered an area for improvement that would really make working with a collection of ActiveRecord objects easier and the code far more beautiful.
Here's how it worked:
>> locations = Location.find(:all)
>> just_the_names_and_ids = locations.collect{|loc| [loc.id, loc.name]}
=> [[1, "New York Zone"], [2, "Los Angeles Zone"], [4, "Boston Zone"],
[5, "San Francisco Zone"], [6, "Development Center"]]
That's really groovy, and something that every Rubyist does in his/her sleep. The syntax for collect, being a block can be a bit confusing for a new programmer though. Then I remembered the ActiveRecord finder syntax.
Location.find_by_city_and_function("Los Angeles", "Production")
Why can't we do the same sort of thing with collect? Now you can!
>> locations = Location.find(:all)
>> just_the_names_and_ids = locations.collect_id_and_name
=> [[1, "New York Zone"], [2, "Los Angeles Zone"], [4, "Boston Zone"],
[5, "San Francisco Zone"], [6, "Development Center"]]>> just_the_names = locations.collect_name
=> [["New York Zone"], ["Los Angeles Zone"], ["Boston Zone"],
["San Francisco Zone"], ["Development Center"]]>> names_and_cities = locations.collect_name_and_city
=> [["New York Zone", "New York"], ["Los Angeles Zone", "Los Angeles"],
["Boston Zone", "Boston"], ["San Francisco Zone", "San Francisco"],
["Development Center", "Chicago"]]
I love it! It's so much more readable and "Rails like" that I'm really surprised that it wasn't something built in. But since it's so easy to extend Ruby (and Rails) it takes just a tiny bit of code to add that functionality in. Just paste the code below into your environment.rb and you're good to go.
class Array
def method_missing(method_id, *arguments)
if match = /collect_([_a-zA-Z]\w*)/.match(method_id.to_s)
attributes = match.captures.last.split('_and_')
self.collect{|array| attributes.collect{|attr| array[attr.to_sym]}}
else
super
end
end
end
This could easily be made into a plugin, or stuck into a .rb file in the lib directory and required. It's up to you. Hopefully someone out there will find this handy. I know I have.
Caveats: This won't work with included associations. Example:
Device.find(:all, :include => :location).collect_name_and_city
That will return a collection with the device names, but city will be nil because it was an included association. It would be easy to extend the functionality to support that, but I couldn't think of a clean syntax to allow it. So I figure it's best to keep it as it is.
Finally, this will work on Array's of hashes too, and not just ActiveRecord objects.
>> pets = [{:name => "Browser", :kind => "Dog"},
{:name => "Fluffy", :kind => "Cat"},
{:name => "Noriko", :kind => "Japanese Actress"}]
>> pets.collect_name_and_kind
=> [["Browser", "Dog"], ["Fluffy", "Cat"], ["Noriko", "Japanese Actress"]]
Ever since I started using the fantastic web development framework Ruby On Rails I've come to an interesting realization. Coding websites is really easy, but designing them is really hard. At least for me.
When I was asked to do the redesign for the American Clematis Society's website I went work right away. In no less than two days I had 90% of the functionality working. There was a complete content management system, admin interface and even some of more complicated sections like the groovy clematis flower database, all basically done. I was thinking this was going to be a breeze! "Piece of cake," I thought.
So two months later I'm finally done. As it turns out designing a website that looks good, works well, is modern and standards compliant is a major pain in the ass. The time I spent adjusting colors and doing mockups in Photoshop absolutely dwarfed the amount spent actually programming. The end result is pretty good. It's much better than the previous version of the site anyway. But I'm still not even close to satisfied. You see my standards are higher than my talent and skill can achieve. That annoys me to no end. :-)
Anyhow, I flipped the switch and set the new site live tonight. They'll be issuing a press release in a week or two but I wanted to "beta test" for a bit first to work out any bugs. So if you're bored, and especially if you have no idea what a "clematis" is (no it's not a naked body part) then please head on over to clematis.org and have a look around.
P.S. If you happen to be a good visual designer with strong XHTML and CSS skills and want to work both for free (on fun projects) and for hire (when we're lucky), drop me a line.
