April 27, 2007 @ 04:42 PM by nap · 0 comments
Dr Nic Williams wrote a nice little app to allow folks to plot out the sessions they'll be attending at Railsconf (or any conference, for that matter) and share them with others. In case you're curious, check out my tentative session list.
Some of those decisions are tough ones! In particular, I'd like to see both Nutter's JRuby talk as well as Dan Webb's javascript-fu presentation. I'd also like to attend both Matthew Bass' homesteading talk as well as Brian Leonard's Tooling/NetBeans session. Sigh, decisions decisions.
Also of note: Dr Nic built MyConfPlan in Hobo, which seems to have come a long way since I last checked it out. Very cool stuff.
April 18, 2007 @ 04:04 PM by nap · 0 comments
After struggling with it probably a little bit more than I should have, I've totally come around to the REST way of looking at things. It seems clean and nice, and I'm 100% behind it. That said, I've found that once you adopt the mentality, things that don't gel with it start to stick out like the guy in the fur coat at the PETA meeting.
Case in point: the ever-popular Rails auto_complete_field helper. To back up my accusation here, let's walk through an example scenario with a REST app, and examine the issues that arise...
Imagine a really simple web application which consists of a collection of Articles. The only user interaction is that guests can comment on articles and tag them with keywords. So in a RESTful world, we have an Articles resource, and a Tags resource. Tags are also nested on Articles such that when you ask for /articles/:article_id/tags you're asking for the subset of tags which have been applied to article_id. We might present these as a tab you can click on when viewing an article resource in a browser.
Now let's say that there's a form somewhere (maybe the #new action on an ArticleCommentsController), which can be used to apply new tags to a given article. There's an auto_complete_field in that form, that will suggest tags to apply to an article, based on the tags that have already been applied. Dumb example, I know, but hear me out.
So, given this scenario, it would make sense to point our autocompletefield helper at article_tags_path(article_id). This is about as RESTful as it gets -- we've become a client of our own API. Nice! Our #index action on ArticleTagsController, in a perfect world, would do a couple things above and beyond just displaying the pretty HTML list of tags on the article:
It would have a respond_to block. if a browser client is asking for XHTML, we probably want to render a nice paginated view of the relevant tags, ordered by weight, with a nice layout. On the other hand, if we're asking for XML or JSON, it just returns no-frills data in the appropriate format.
it would optionally check for a filter query string. perhaps we set params[:filter] = :attribute_name and then make the actual string filter value (the thing we're trying to help auto-complete) available via params[:attribute_name]. in the case of our tags example, we'd end up with a params hash that has filter=name and name=zer (or some other text string). If a filter parameter is found, we'd filter the results by the pattern specified. This could all be wrapped up in a nice little helper.
This proposal raises a couple issues. First, the auto_complete_field helper as it exists today expects us to return some HTML from the AJAX call that we can plug into an appropriately named DIV. This is simple and works well, but if we're really consuming our own web service, we should probably be talking to it via JSON or XML and have a JavaScript handler to process the results, converting them into list format or whatever it is that we desire. But I have to admit that this convenient hack works nicely.
More importantly, if the auto-complete filter is being applied to the collection of resources, that means it maps to the #index action by way of a GET request. So our parameters end up encoded in the URL with a query string like ?filter=name&name=zer. Obviously, GET params are a little more limiting than POST params in terms of length and (perceived) security. But for simple filter strings that take a short sequence of characters, I don't see this as being an issue. And this is always the case when doing an auto-complete, as far as I can figure.
Note that the auto_complete_field JavaScript helper doesn't actually allow you to specify an HTTP verb other than POST, although that's easy enough to change. Surely the core team would accept the one-line fix required to support a :method => :get option if it's the "right thing to do". The real problem is more of a philosophical one: are we, as a community, really going to embrace REST 100% or is it acceptable to add small bits of cruftiness in places where it seems inconvenient?
A perfectly legitimate half-way solution is to define a standard #autocomplete action for any resource you want to use in this manner. The advantage to this is that you can easily use a POST against a custom :collection method on the resource. That way, we continue to work inside the existing box of conventions. But I ask you, is that really RESTful?! What do you think?
April 17, 2007 @ 09:17 AM by nap · 0 comments
Join us tonight at the Portsmouth Public Library for a bunch of lightening talks on Ruby-related subjects. I'll be speaking on REST, Rake, and RMagick (if I can manage to put together a preso and demo app in time). Scott will be presenting a demo of his Rails-based ProgressPuppy task manager. And there will be other stuff, too.
Note that we're still doing the meeting, despite aftereffects of the nasty coastal flooding. Hardcore, eh? As always, more information (including directions) can be found at the NHRuby Wiki.
April 10, 2007 @ 09:28 AM by nap · 0 comments
Just saw this InfoQ article about RailRoad and had to check it out. Gotta say, this is by far the best class visualization tool for RoR I've seen yet. Set your options, generate those DOT files, and then run them through GraphViz to export your image format of choice. Couldn't be easier.
Check out some of the examples on the RailRoad RubyForge site, including the diagrams of the popular Depot example app and the much more complex Typo blog package. The latter is a good illustration of why the brief option is provided, heh. If you're in the UML camp, you might be a little disappointed as the diagrams it produces are closer to BON, but personally I think they're very straightforward and natural.
Whatever your modeling language preference is, I think we can agree that tools like this go a long way towards legitimizing Rails use in large multi-person projects and are, well, just plain helpful. Big thumbs up.
April 10, 2007 @ 08:49 AM by nap · 0 comments
It's no real secret that the default Rails pagination helpers are kind of awful. Sure, you can use them, but I wouldn't recommend it if you expect to scale. Instead, go snag yourself the wonderful paginating_find plugin. And then, if you're going to be using them with your model associations, whip up an association extension like this:
module PaginationExtension
def paginate(current = 1, size = 10, options = {})
options[:page] = {:current => current, :size => size}
find(:all, options)
end
end
Now just extend the has_many association on your City class and you can call city.bars.paginate(2) to get the second 10-element page of bars associated with your city.
class City < ActiveRecord::Base
has_many :bars, :extend => PaginationExtension
end
city.bars.paginate(2)
The good bars are all on the first page though, so consider yourself warned.
April 02, 2007 @ 07:53 PM by nap · 0 comments
I spent the better part of my morning today writing some code to do sample data generation within the framework we're building. I'd originally packaged it as a standalone utility class to be run with the script/runner facility until I realized that it was a perfect candidate for a custom Rake task. And a perfect topic for a blog entry -- two for the win!
If you've never written a Rake task before and are a Ruby pseudo-noob, fear not. If you've written Ant or Make scripts before, and have the requisite passing familiarity with Ruby, it'll be old hat. Of course, the big win with Rake is that you get to write your tasks in Ruby. Sweet, sweet Ruby. No crufty XML here.
What follows is pretty minimalist, but I wanted to record it for posterity. Also, many of the tutorials I looked at didn't deal with handling parameters, so I figured it might be useful to someone else out there...
namespace :foo do
desc "Generate sample data for the app"
task :sample_data => [:environment] do |t|
require 'db/script/sample_data.rb'
num_users = ENV.include?('NUM_USERS') ? ENV['NUM_USERS'] : 5
SampleData.generate(num_users.to_i)
end
end
What we're doing here is defining a task named sample_data within the foo namespace. We declare a dependency on the Rails :environment, which is a little gift from the Rails team allowing you to write code in your tasks that lives within your app's pre-existing environment. The desc line serves as a comment about what the task does, and will appear alongside the task name when you query it with the --tasks parameter.
Within the body of the task we simply require the script we're going to run, in this case my virtually unmodified utility class, and then we check to see if a parameter named NUM_USERS was passed in. If it was, we use that value to determine the number of users to create. If it wasn't, we default to 5.
Because Rails is smart, and talented in the mystical arts, it auto-loads any files in lib/tasks that have a .rake extension. So just plop this file in that directory and you can then check the available Rake tasks from the top level of your project. You should see our new task definition there:
$ rake --tasks | grep sample
rake foo:sample_data # Generate sample data for the app
Rockin. So let's run it. As you can see from the output below, it calls the utility calss, creating 10 sample users (and a bunch of other stuff I've excluded). Couldn't be easier.
rake foo:sample_data NUM_USERS=10
SampleData: generating 10 users, blah blah blah...
I hope this has been helpful, and if you're looking for more information, make sure to check out Martin Fowler's seemingly definitive reference.