Experimental resource_controller Feature: Custom Action Discovery 1

Posted by james
on Wednesday, May 07

A few weeks ago, Nate Wiger emailed me to ask whether I was interested in a patch for r_c. Evidently, it is possible to determine which controller actions need to be created by examining the routes that are pointing to it. Nate had also noticed that most controller actions follow a pattern:

  1. Load or build an object or a collection (i.e. Post.find(1) or Post.build(params[:post])).
  2. Optionally do something with the object(s) (i.e. @post.save).
  3. Optionally set the flash.
  4. Render a response.

Put those pieces together, and it's possible to create a controller abstraction that is aware of what's routed to it, and sets up a set of sensible defaults, even for custom actions. As you might have guessed, Nate and I have done exactly that.

For the 7 default RESTful actions, things haven't changed much. The only real difference is that you can now change step 2 (refer to list above), by passing a block to action.action.

create.action do
  @post.my_custom_save_method
end

The action block's return value determines whether the success or failure callbacks (i.e. success.flash vs failure.wants) are triggered.

For custom actions, you'll now be able to use the same DSL you're used to for customizing default actions. For example, if you had a custom action called update_collection, you might put something like this in your routes:

map.connect '/posts', :controller => 'posts', :action => 'update_collection', :conditions => {:method => :put}

Based on that route, r_c will automatically be aware of your action, and create a basic skeleton for it, following the pattern of the list above. For a collection update method, you might want to customize your action like this:

update_collection do
  before { @posts.each { |p| p.update_attributes params[:posts] } }
  update_collection.action { @posts.save }
end

That's all you'd have to do. r_c would automatically load the objects (including any parent, respecting polymorphism), and render using its internal helpers.

Note: It is also possible to modify loading by setting a block for the build accessor, which corresponds to step 1.

Highly Experimental

This is highly experimental software. I'm not sure how much I love it, since it's a bit on the magical side for my tastes. However, it is kinda neat to have your controller be aware of which actions to create, based on what is routed to it. I have been having a bit of fun playing around with this.

So, please give it a shot, and let us know how you like it on the mailing list.

Get it from github (checkout the automatic_route_discovery branch). If you aren't using git, you can get a tarball here.

Rails Training w/ James Golick & Other Rails Ninjas 0

Posted by james
on Tuesday, April 29

This past December, a friend of mine, Peter, wanted to improve his rails skills. I had been asked by a few people about teaching some rails training sessions, but wanted to give it a beta test first. When I was in Toronto over the holidays, Peter and I gave it a trial run, which went great. Since then, I've been dying to offer ruby / rails training to a wider audience.

How It'll Work

The sessions will be based on the format that I tried out in my beta session with Peter. We'll start by covering some advanced rails fundamentals, with time for questions, and plenty of time to go off on long tangents about whatever you might want to learn (like, how to write a plugin, or how some of the rails internals work). The remainder of the session will be spent coding.

During the coding portion of the sessions, the instructor(s) will pair program with each of the participants in a rotation. Think of it as an opportunity to work extremely closely with a rails expert. We'll be able to apply what we've learned in the earlier portion of the session. We'll work on your real problems; that way, we'll be teaching a custom course that's custom tailored to you, and you'll walk away from the experience with some code, to boot!

Participants will be asked to bring two projects: one in progress (or finished), and one idea.

  • The project in progress serves as a tool for analysis of where you're at, and what kind of coding practices you have in general. We'll work out some possible refactorings for that project, and talk about how you'd make use of advanced rails techniques to improve its readability, maintainability, etc.
  • Next, we'll work on your project idea. The rest of the session will be spent architecting, and actually coding this project. We'll help you pick the right plugin set, model your data, and anything else.

Logistics

  • I'd like to offer the first session some time in early July, most likely over a weekend.
  • The sessions will be held in Montreal, unless there's a compelling reason to offer them somewhere else, like, a group of participants who all live in the same city.
  • Price is still TBD; I've got some potential sponsors (which would offset the price for participants), but nothing confirmed yet. There would certainly be a discount for groups or companies.

Finally, I intend to keep the instructor to participant ratio extremely low to support plenty of one-on-one time. So, if there is sufficient interest, I'll get some of my fellow Montreal rails ninjas in as additional instructors. I've got some awesome people in mind. Who knows — you might even get scouted by one of the local rails shops!

If you're interested, send me an email (top right) for more info.

Introducing My New Company: Giraffetyp

Posted by james
on Monday, April 28

Everybody loves their iPod, and rightly so. It's one of the greatest technology products of all time. The iPod experience is the sum of many parts: design, development, etc. One of Apple's biggest strengths is their ability to put all of these pieces together effectively. If the iPod was ugly, unusable, had buggy software, or felt lousy in your hands, it wouldn't be the same device; it would be mediocre like all of the other portable mp3 players that came before and after it.

On the web, shops are usually run with a strong focus on one piece of the puzzle. Some firms are development-centric, others are design-centric, others still are business-centric. A shop's core skill usually trumps everything else. Even when they hire consultants to fill the gaps in their skill set, shops tend to keep focused on their core competencies, failing to properly execute the work of the consultants. It seems to happen everywhere — even in the best shops.

So, having the best people work on the product isn't enough; to achieve greatness, there has to be tight collaboration between all parts. The developers must respect and understand the designers, and likewise. Most importantly, no one concern can trump another.

Come read the rest of this article at the Giraffetyp blog. Note: I'm going to be doing a lot of my blogging over there for the next little while, so, please subscribe there too.

Introducing Action Messager: Dead simple IM notifications for your app! 3

Posted by james
on Sunday, April 06

Sometimes email is the wrong choice for webapp notifications. Our inboxes are becoming increasingly cluttered, and especially for those of us who carry PDAs, it can be a pain to scroll through twelve facebook notifications just to get to the mail that we actually need to read. What's more, email just isn't that great of a tool for receiving short messages, since you have to open them each individually. At the very least, it's nice to have another choice.

Particularly with shorter notifications, instant messaging can be a great alternative to email. The messages don't clutter up your inbox, or need to be opened individually. Most people already use it on a daily basis. The only problem is implementation.

ActionMessager

ActionMessager is a simple framework for creating IM-based notifiers. It is structured like ActionMailer, so it's got virtually zero learning curve for most rails developers. What's more, because the syntax is the same, it's pretty easy to create a delegate class that acts like your mailer, but sends IMs as well. That means drop-in replacement!

All you have to do start sending IM notifications to your users is subclass ActionMessager::Base. Then, create a method that sets an array of recipients, and returns the message you'd like to send:

class JabberNotifier < ActionMessager::Base
  def friendship_request(friendship_request)
    @recipients = friendship_request.receiver.jabber_contact
    
    "You have received a friendship request from #{friendship_request.sender.name}! Click here to accept or decline: #{friendship_request.url}"
  end
end

Then, wherever you'd like to send the notification:

JabberNotifier.deliver_friendship_request(friendship_request)

That's all there is to it.

Caveats

Currently, only jabber is supported. It is possible to access other IM services over jabber, but I'm not 100% clear how it works, and I don't yet have need for it, so that may or may not come later.

The bigger caveat, though, is speed. Sending a notification takes anywhere from 1-2s (whether you send 1 or 5). It may be possible to improve performance by using a different jabber library, and I'll probably investigate that in the near future. Even with a performance boost, though, you should still take your notifications out of the request cycle, by using something like workling to process them asynchronously.

Get It!

Get action_messager in gem form:

$ sudo gem install action_messager

Or get the source, from github:

$ git clone git://github.com/giraffesoft/action_messager.git

attribute_fu and jQuery shake hands 3

Posted by james
on Wednesday, April 02

This blog has been quiet of late, because I'm working on a couple of exciting, but still top-secret projects. Anyway, I recently moved one of those projects over to jQuery, because of its speed, syntax, and general awesomeness. Tonight, when I went to create a multi-model form with attribute_fu, I was stopped dead in my tracks by its heavy dependency on prototype. A few minutes of hacking later, a_f and jQuery are playing rather nicely together.

Get it from the jquery branch of a_f's git repo. If you aren't using git yet, this might be just the excuse you need to check it out! Or, download a tarball from github. After you install the plugin, you'll have to copy its one javascript dependency (jQuery templates) from the javascripts directory over in to your public/javascripts and require it in your layout.

Plugins I've Known and Loved #3: Ultrasphinx 16

Posted by james
on Monday, March 03

If you've ever implemented searching in your rails app, you probably noticed that it's a major hassle (unless, of course, you already know about Ultrasphinx). Ferret is probably the most commonly used indexing solution, but, it isn't anywhere near production ready. acts_as_solr, another option, is so full of show stopper bugs that it really isn't even worth bothering with. The worst part about both of those solutions is that they usually work great in your development environment. Try to put them in to production, though, and you're in for big problems.

Enter Ultrasphinx, by Evan Weaver. Sphinx is a super high performance search daemon, designed to suck information out of a mysql or pgsql database (though, it isn't limited to that). Normally, one would configure Sphinx with SQL queries that it uses to fetch the data. Apparently, the configuration file can be a major hassle to work with — I wouldn't know. Ultrasphinx provides you with a declarative API that it ultimately uses to generate the Sphinx configuration file for you, making the process quick and painless. A few lines of code, a couple of rake commands, and you're searching.

The Sphinx approach has several advantages over ferret, and acts_as_solr. First of all, Sphinx is extremely stable. They're nearing a 1.0 release, and its stability certainly merits that. You don't even really need to worry about monitoring the search daemon, because it just isn't going to crash. Also, if you're running any rails apps in production, you know that long running ActiveRecord callbacks can lead to your app performing very poorly. The ferret and solr solutions both rely on active record callbacks to inform the indexer of new data. Moreover, if the search daemon goes down for any period of time, or, say, the index becomes corrupted (ferret, I'm looking at you), your entire app is going to be down for the count. You set the Sphinx indexer to run every half hour on a cron job, and since it works with the db directly, its performance and stability characteristics have absolutely zero impact on your app.

Trying it Out

So, let's implement a search for our blog. First, we'll want to edit the paths to the index and logs in the default config file. Note that Ultrasphinx uses two types of config files: one that the programmer edits, and one that it generates; both are necessary on all machines that are accessing the index (seriously, not having the generated config on a slave machine caused me some trouble). Then, we'll want to declare our post model as indexed (note that you must declare the fields as strings, not symbols):

class Post < ActiveRecord::Base
  is_indexed :fields => ['title', 'body']
end

Then, we'll need to ask Ultrasphinx to generate a configuration file. You've got to re-run this rake task any time you make changes to the definition of your models' is_indexed declaration. Make sure to .gitignore (svn:ignore for people still stuck in svn land) the generated file in development. Evan recommends checking the production file in to version control, but I have it set to generate automatically on deploy with a cap recipe. That way, if I make changes to the indexing, I won't forget to re-generate the config file. All you have to do is run:

$ rake ultrasphinx:configure

Then, since it's our first time, we've got to run the indexer:

$ rake ultrasphinx:index

Finally, we'll need to start the search daemon:

$ rake ultrasphinx:daemon:start

Now, we can start searching.

@search = Ultrasphinx::Search.new(:query => params[:query])
@search.run
@search.results

So, it's really easy to build a basic search engine using Ultrasphinx. There are some gotchas, though.

Gotchas & Notes

  • More complex indexing with Ultrasphinx can be slightly more verbose, and SQL-focused than it would be with a solution that relies on AR callbacks to do its indexing.
  • Transforming data with Ruby is impossible; the data you index must be in your database (unless you use a stored procedure, which can actually be written in ruby if you're using pgsql, but I digress).
  • Ultrasphinx preloads your indexed models when it is initialized. So, if your models depend on any monkey patches that live in your app's lib directory, they must be loaded before the Ultrasphinx (in my experience, this has meant pluginizing my monkey patches). Because of the way that exceptions are caught in the preloading routine, you won't see the actual error that is stopping your model from loading. Instead, you'll just get a constant loading error, or a name error, or something. If you see something like that, and your models load fine without Ultrasphinx installed, look for dependency issues.
  • Ultrasphinx attempts to preload your indexed models using a regex that doesn't respect commenting (at the time of writing). If you're struggling with issues mentioned in the last gotcha, you'll probably try commenting out the is_indexed call to see whether that's what's causing the problem. That won't work. You can either delete the is_indexed call entirely when debugging, or pull from my git repo, where I've modified the regex to respect #-style commenting (but not the =begin/=end style).
  • If you see DivideByZeroErrors in production, it's probably because you're missing the generated configuration file on one or more of your app server machines.

Check it Out

You'll need to grab Sphinx. Then, the plugin...

Get it from svn:

$ svn export svn://rubyforge.org/var/svn/fauna/ultrasphinx/trunk vendor/plugins/ultrasphinx

Or pull from my git repo (for the change described in the gotchas section):

git clone git://github.com/giraffesoft/ultrasphinx.git

To Sum Up

Ultrasphinx is by far the most effective rails searching solution I've come across. Unlike most of the other options, the search daemon is incredibly stable, and the index never seems to become corrupted (I'm running it in a relatively high load production environment with absolutely zero trouble so far). Also, since Ultrasphinx doesn't rely on AR callbacks for indexing, your application isn't quite as coupled to your search daemon; if it dies, search functionality will break, but the rest of your app will still function. It's not without problems, and complex indexing can be trickier, but Ultrasphinx's stability and performance make the choice a no-brainer.

Plugins I've Known and Loved #2: has_finder 10

Posted by james
on Monday, February 25

Plugins I've Known and Loved started out as a would-be series of presentations at Montreal on Rails. I've been bad about getting to the second presentation, so I thought I'd continue PLIKaL here. No, I'm not going to talk about any of my plugins. Instead, over the next week or two, I'd like to tell you about some wonderful plugins that I only recently discovered (though, none of them are particularly new). Today — a plugin that belongs in everybody's toolbox: has_finder.

The basic idea of this school of plugins is creating reusable scopes for your models. For instance, if you had a blogging application with a post model, you might want to query for published posts. To that end, you might write something like this in your controller:

Post.find(:all, :conditions => {:published => true})

Hopefully, though, you're familiar with the skinny controller, fat model best practice, so you'd write it like this:

class Post < ActiveRecord::Base
  def self.published
    find(:all, :conditions => {:published => true})
  end
end

That's a lot better, but far from perfect. If you were to write several such methods, for example, you would not be able to combine them easily. So, looking for all published posts written by James would require writing a second method; not very DRY. Of course, ideally, you'd use an association proxy to accomplish that goal anyhow. That would work with our hand-written finder, but we'd lose all the benefits of the association_proxy like nested finds (...published.find(:all, :order => 'created_at DESC')), and we certainly wouldn't be able to chain two of them together (...published.order_by_recent). has_finder solves those problems, and more.

I Can Has Finder?

In order to reproduce the finder we wrote earlier, you'd write the following:

has_finder :published, :conditions => {:published => true}

Should you want to find all published blog posts by James, you could then make use of association proxy:

User.find_by_name('James').published.find(:all, :order => 'created_at DESC')

I have come across a good number of associations used for similar purposes, but defined on the associated model. For instance (omitting irrelevant details):

has_many :published_posts, :conditions => {:published => true}

Having discovered the wonders of has_finder, I now consider this to be an anti-pattern. The has_finder method is definitely DRYer. Far more importantly, however, the definition of what makes a post published (in our case, :published => true) belongs in the post model. The same rule applies to ordering. Following this best practice ensures there's only one point of change for refactoring, and that your models and their tests tell a more complete story about the data they represent.

Finally, has_finder supports parameterization of finders. You can wrap your conditions hash in a lambda that accepts an arbitrary number of arguments. Continuing with our blog post example, you might wish to query your posts table for all posts this week, this month, or this year, depending on the situation. To keep DRY, you could define your finder as follows:

has_finder :since, lambda { |date| {:conditions => ['created_at > ?', date]} }

Using it like this:

Post.since(1.week.ago)

To put it all together, let's query for all James's published posts in the last week:

User.find_by_name('James').posts.published.since(1.week.ago)

It reads just like a sentence!

Oh yeah, and all of this is compatible out of the box with will_paginate. I heard a rumor that it's going to be sucked down in to rails, too. Really, how could you not check it out (get it here)?

The Crunch Mode Paradox: Turning Superstars Average 11

Posted by james
on Monday, February 18

We've all been there — a product near (or not so near) ready to launch. Bosses breathing down your neck. Deadline soon approaching. People start to work evenings, then weekends. It starts to feel, perhaps rightly so, like nobody is doing anything other than working. Crunch mode is the time when everybody buckles down, and focuses on the product, and only the product. Forget about everything else.

Crunch mode is a result of the belief that more hours spent coding means more software gets written. The obvious problem with management's formula is that a tired programmer can do more harm than good. A particularly nasty bug, for instance, can cost a team orders of magnitude longer to fix than it took to write. So, while it may be possible to increase the total number of lines of code written, it only seems reasonable to measure shippable lines of code as a metric for productivity. Nasty bugs that get written by tired, over-worked developers should be counted against total output, rather than for it. In crunch mode, managers tend to overlook the bugs, and see productivity as increasing, while it's really decreasing. That's the crunch mode paradox.

Why Does it Happen?

You've assembled a team of all-star programmers. They're going to practice test-driven development, continuous refactoring, and other agile processes. They are code artists. Indeed, your team of developers is obsessed with 'beautiful code', and whether it's artistry, or OCD, they're not happy until code is perfect. They write shippable code.

The key to understanding why your team of carefully selected programmers breaks down in crunch mode is understanding what makes them tick in normal mode. Writing shippable code means being in a constant state of careful thought. Each change to the code is a calculated maneuver, the programmer's brain always working hard to discover new patterns of duplication that may have emerged in the code base, and promptly abstract them. The process of continuously searching for a better way — for better abstraction — is a major key to writing high quality code. As with anything else, abstraction can become a pitfall, but used properly, it means writing less code, easier testing, and perhaps most importantly, fewer places for bugs to come from. Of course, continuous refactoring, and code reuse aren't the only pieces of the puzzle, but it's important to note the thought process of the developer who is crafting his code this way.

In normal mode, your superstar developer cares about writing beautiful code; that's all. That's why he makes sure to write a comprehensive test suite, to continuously refactor, and makes sure his code is readable. That is probably the biggest difference between your great developer, and the average developer you went to so much trouble to avoid hiring; your superstar's priority is the code, whereas the average programmer's priority is going home at the end of the day. When you flip that crunch mode switch, though, priorities change.

When you tell your team that there's a deadline for a certain feature set (especially when it's a tight one), the focus is no longer on the code. When everybody is racing to accomplish a set of tasks for a certain date, coming in to work every day is about trying to get as many of those features out the door as possible in as short a time. The pressure is on. People start cutting corners. Processes break down. People write fewer tests, refactor less frequently. You have effectively turned your superstar team in to a group of average programmers.

In factory terms, a worker's production rate decreases over time. A worker who is creating 10 widgets/hour at the beginning of a shift may be producing only 6/hour at the end of the shift, having peaked at 12/hour a couple of hours in. Over time, the worker works more slowly, and makes more mistakes. This combination of slowdown and errors eventually reaches a point of zero productivity, where it takes a very long time to produce each widget, and every last one is somehow spoiled. Assembly-line managers figured out long ago that when this level of fatigue is reached, the stage is set for spectacular failure-events leading to large and costly losses – an expensive machine is damaged, inventory is destroyed, or a worker is seriously injured. (Why Crunch Mode Doesn't Work)

It Doesn't Have to Be This Way

There are two problems with crunch mode: management, and developer. Being a developer, I'm going to go after the pointy haired among us first.

Managers

Crunch mode doesn't work. Sending your team on a death march quickly leads to programmer fatigue, which nearly always leads to bad code. Moreover, developers are likely to become demoralized, burnt out, lose interest in the product, and perhaps worst of all for you, they'll become resentful of management. Moreover, in many cases, due to nasty bugs, and later hits in maintainability and consequent necessary rewrites, the crunch mode developer is outputting less in total. Finally, this sort of crunch mode is nearly always symptomatic of a team that has slipped in to waterfall development.

The great thing about true agile development is that after the first couple of weeks, the code is always in roughly shippable state. So, if you are working on a product that has to launch on a particular date, for whatever reason (be it PR, or anything else), agile is a highly effective way to ensure that you're going to have a shippable product ready. The reality is that trying to shove a specific featureset down the throats of your development team with a hard deadline simply does not work. The software will be alpha-quality if you're lucky.

John Nack, Adobe Photoshop product manager says of their transition to agile:

The team's ability to deliver a public beta--a first for a major Adobe application*--is probably the single greatest tribute to the new method. In the old days, we could have delivered a feature-rich beta, but the quality would have been shaky at best. This time, as Russell says, "The public beta was basically just 'whatever build is ready on date X,'" because we knew it would be stable. (Agile development comes to Photoshop)

Developers

The key to beating crunch mode, in my experience, is forgetting about the pressure being put on you (not easy, I know); you have to work as normal. What happens to us during crunch mode is extremely counter-productive. For some reason, when we want to work faster, we stop doing all of the things that save us time. Testing, refactoring, and general caring for code is what separates ultra-productive developers from average ones. But, best practices the first thing to go when the pressure's on.

Once you've pulled yourself together, and started working normally again, the next step is to start saying no. Obviously, this piece of advice is going to be controversial, but I think it's a discussion worth continuing. As Raganwald says:

Try this: Employ an Engineer. Ask her to slap together a bridge. The answer will be no. You cannot badger her with talk of how since You Hold The Gold, You Make The Rules. You cannot cajole her with talk of how the department needs to make its numbers, and that means getting construction done by the end of the quarter. (What I admire about professions like Engineering and Medicine)

Obviously, nobody is going to die on account of (most) bad software (as they might with a poorly built bridge). But, when you know that you, and others on your team are doing bad work, it's time to speak up. If you don't, you're doing a disservice to yourself, and your company. You're the one who is going to have to maintain this nightmare when crunch mode is over (and we know that there's never really time for a cleanup). Your company is going to have to deal with low quality software, and likely a botched release. Everybody loses.

The Crunch Mode Paradox

Crunch mode is a paradox. Managers think they're fostering increased productivity, but instead, they're damaging morale, the product, and often causing a net decrease in output. Developers try to increase their productivity by cutting all of the corners that make them effective in the first place. For those reasons, the death march has the opposite of its intended effect. Agile lifecycle management seems to be the most effective tool for guaranteeing that shippable software will be ready on a particular date. So, for companies trying to develop software, next time, try agile instead of crunch mode. Everybody will be happier.

resource_controller 0.2: maintenance release - no more edge_compatible branch 2

Posted by james
on Friday, February 15

This is mainly a maintenance release.

  • The helpers have been broken out in to four separate files internally, to help with managing the deep nesting branch.
  • There have also been a few little refactorings in preparation for some new features to come shortly.
  • The biggest thing to note for users is that there is no longer an edge_compatible branch. Since rails 1.2.6 generates the same style of named routes as 2.0.2 (edit_tag_photo_path instead of tag_edit_photo_path), there is no longer a need to continue two separate streams of development (yay)!.
  • The generator has been updated to spit out the right filenames for templates (rhtml/haml vs html.erb/html.haml), and old-style migrations (t.column instead of t.string) for any users still stuck on 1.2.6, so the transition shouldn't be a problem.

Get It!

You can get the new version by exporting from svn:

svn export http://svn.jamesgolick.com/resource_controller/tags/stable vendor/plugins/resource_controller

Or, if you're using piston, you may need to switch to the new url if you were previously on edge compatible (this is untested, so it may be slightly wrong).

  piston switch http://svn.jamesgolick.com/resource_controller/tags/stable
  piston update

As always, everybody is encouraged to come join the discussion on the increasingly lively mailing list.

An Introduction to Association Proxy

Posted by james
on Sunday, February 10
@post.comments << Comment.new(:title => 'something')
Comment.find(:all,  :conditions => ['post_id = ? AND title like ?', @post.id, 'something'])
Comment.count(:all, :conditions => ['post_id = ?', @post.id])

There is an easier way to do all of that. So, if you're still manipulating ActiveRecord associations by hand, stop; there is a better way.

AssociationProxy

When you request a collection of associated models, ActiveRecord extends that array with several helpful methods. You can treat that array like a model class, except all of the methods are automatically scoped to the association's parent. It responds to methods like find, create, new, count, etc, and, it's called AssociationProxy.

So, what if we wanted to create a comment, scoped to our post model?

@post.comments.create(params[:comment])

Making use of AssociationProxy, the code reads better, and requires fewer keystrokes. We can also use AssociationProxy to find elements in our collection.

@post.comments.find(:all, :conditions => {:title => 'title we are looking for'})

We can even use dynamic, attribute-based finders through AssociationProxy.

@post.comments.find_by_title 'title we are looking for'

It Gets Better

Since assoc proxy responds to many of the same methods as a model class, the two can be interchanged for many operations. In resource_controller, for example, nested controllers depend heavily on association proxies. Let me show you what I mean.

If we had, for example, a typical show action.

def show
  @comment = Comment.find(params[:id])
end

With very little effort, we can allow comments to be shown nested, or unnested.

def show
  @comment = model.find(params[:id])
end

private
  def model
    params[:post_id] ? Post.find(params[:post_id]).comments : Comment
  end

Since we know that AssociationProxy responds to other model class methods, we can base our create method on this technique, too.

def show
  @comment = model.find(params[:id])
end

def create
  @comment = model.new(params[:comment])
  if @comment.save
    redirect_to @comment
  else
    render :action => 'new'
  end
end

private
  def model
    params[:post_id] ? Post.find(params[:post_id]).comments : Comment
  end

In fact, this is nearly exactly how resource_controller is built.

So, association proxy is useful in simple and complex situations alike. It can save you a lot of code, and increase readability. It's a win-win really.

Look out Ryan Bates - There's a New Screencast in Town 17

Posted by james
on Sunday, January 27

So, I'm sitting there on Friday evening working on a screencast introduction to attribute_fu, when Fabio Akita sends me an email to tell me he just created one for resource_controller! It was like he'd read my mind.

Fabio was nice enough to agree to let me put some title screens on his r_c screencast, but I couldn't seem to get it to export while still looking decent. So, here's the first episode of GiraffeCasts (for those of you who don't know, my company is called GiraffeSoft), and Fabio's awesome resource_controller introduction.

Episode 1: attribute_fu

Episode one is a quick walk through the basics of attribute_fu. Get it here.

As promised, here is the code from the screencast:

## _task.html.erb
<p class="task">
  <b>Title:</b><br/>
  <%= f.text_field :title %>
  <%= f.remove_link('Remove') %>
</p>
## _form.html.erb
<div id="tasks">
  <%= f.render_associated_form(@project.tasks, :new => 3) %>
</div>
<%= f.add_associated_link('Add Task', @project.tasks.build) %>
class Project < ActiveRecord::Base
  has_many :tasks, :attributes => true
  
  private
    before_save :remove_blank_tasks
    def remove_blank_tasks
      tasks.delete tasks.select { |task| task.title.blank? }
    end
end

To get attribute_fu:

$ piston import http://svn.jamesgolick.com/attribute_fu/tags/stable vendor/plugins/attribute_fu

Fabio's resource_controller Screencast

Fabio Akita gives an excellent tour of resource_controller, in screencast form. Get it here.

See Ya Next Time

That's all for today. Check back for more GiraffeCasts!

Ruby Reddit 0

Posted by james
on Friday, January 25

When reddit announced that they were going to be offering users the ability to create their own reddits, I sent them an email asking for a beta account. Guess what? They gave me one!

So, rubyists everywhere, here it is: our very own ruby reddit!

Why Distributed Version Control Matters to You, Today 11

Posted by james
on Sunday, January 20

If Linus's talk about git made you feel like a moron, rest assured you're not alone. Distributed version control is one of the most poorly explained topics in software, today. There are plenty of people saying that you should use it, but nobody has done a great job of explaining why. Here's my take.

WTF is Linus talking about?

First, let's talk about exactly what distributed version control is. The most common approach to describing a decentralized system is to present the reader with an image like this one, taken from the mercurial page called UnderstandingMercurial:

If that image makes any sense to you (and you're new to distributed version control), congratulations. The rest of us need a clearer description.

The best place to start is with the differences between centralized, and decentralized version control. With a centralized system like Subversion or CVS, there is a single copy of the repository; it typically resides on a server somewhere. When a developer works with the code, they receive something called a "working copy" from the central repository. The working copy contains enough information to interact with the central server, but does not contain the revision history for the project, nor the branches or tags (though, the branches and tags can often be explicitly requested).

With a decentralized system, the opposite is true. Instead of checking out a working copy, the developer works with the entire repository, including the entire revision history of the project and all the branches and tags. The copy that the developer receives is identical to the repository they fetched it from. Commits, branches, and tags occur locally, on the copy of the repository that the developer made. Changes can then be pushed and pulled to or from a public repository somewhere, another developer's repository, or anywhere else that a repository exists.

So?

Usually, when you want to get a build or source distribution of open source code, you head the project's main website. The project's repository is closely guarded, with commit rights only awarded to a select few. Anybody wishing to make changes to the source must first check out a working copy from version control, submit a patch, and hope that it is accepted.

When every developer has a full copy of the repository on their machine, the hierarchy of open source projects is all but eliminated. Any developer who wishes to work on the source code can clone the repository, commit as much code as they want to it, receive the changes from any other developer's cloned repository, and publish their work for other developers to use, or pull back into their own repositories. In a decentralized system, it doesn't matter who has the "...keys to the source repository..." (it actually says that on the rails core team page — take a look for yourself). If the original author continues to maintain the best version of the code, great; if not, users of that code can begin to pull from whoever does have the best version.

Really!

Entirely theoretical software articles are lousy — so, I always try to provide examples out of real software; this article is no exception.

Many (maybe most) rails plugins are inactive. They were created to scratch an itch, published, kept up to date for a few months, and then left with no maintainer. Since rails plugins largely reside in subversion repositories, nobody can continue development without losing the entire revision history of the project, and going to the trouble of setting up a public svn server.

Markaby is no exception to that rule. When I tried to use it in a recent project, I found that it was incompatible with rails 2.0.2. According to markaby's subversion logs, the last change was November 24th, 2007, a few weeks before that version of rails was released. Luckily, I was able to find a ticket in rails trac with instructions on how to hack a fix in to the plugin — a solution that worked great for me, but wouldn't work for a user who didn't know their way around plugins. Without commit access, normally I wouldn't be able to offer my fix publicly.

Not so with a distributed system! I was able to use git-svn to pull Markaby into a git repository. I've published my changes, and now anybody can grab a working version of Markaby by typing the following in to a command prompt:

  $ git clone git@github.com:giraffesoft/markaby.git

Even cooler than that, somebody with commit access can grab the changes, and push them back into subversion, including commit messages, and everything! Anybody who clones the git repository can still pull changes from _why's subversion repository if it ever becomes active again. If not, development can continue anywhere, and be done by anybody. That's the beauty of decentralization.

Taking Style Tips from Natural Language 8

Posted by james
on Sunday, January 13

When one speaks of the readability of a computer program, they refer to the ease with which the source code is read by a human. More recently, with languages like Ruby and Python, one often hears praise for sections of code that seem to read like natural language. That sort of code is easier read, but seems to be more challenging to write. If english-like is what we're after, perhaps style manuals can teach us something about writing code.

Having always been interested in writing English, I've read several books on style. The text that I continue to return to is Strunk & White's The Elements of Style. It has served me well as a reference for writing English; let's see how it does for Ruby.

Lesson One: Put statements in positive form.

The rule from Strunk & White:

Make definite assertions. Avoid tame, colorless, hesitating, noncommittal language. Use the word not as a means of denial or in antithesis, never as a means of evasion.

Elements of Style provides a short explanation of each rule, followed by several illustrative examples. Each example contains a violating passage, and a correction. Here's an example from this rule...

The violating example:

He was not very often on time.

...is corrected by:

He usually came late.

Yes, Strunk and White provide refactorings; I'll do the same.

if !@post.save
  render :action => 'create'
else
  redirect_to post_url(@post)
end

That example is rather simple. It says: If the post doesn't save, render the create action, otherwise, redirect to the post's url. I think we can agree that in most cases, it would be better to express that as follows:

if @post.save
  redirect_to post_url(@post)
else
  render :action => 'create'
end

By putting our statements in positive form, we have refactored that code to read: If the post saves, redirect to its url, otherwise render the create action. Because we have eliminated the negation, the code reads more smoothly and easily. While that is an obvious example, it shows that there is something to this. Let's look at another rule.

Lesson Two: Use the active voice.

The rule from Strunk & White:

The active voice is usually more direct and vigorous than the passive.

The violating example:

It was not very long before she was very sorry that she had said what she had.

The correction:

She soon repented her words.

This rule doesn't apply as directly, but I have always loved it as a style guideline for code. Take this rather common example:

[1, 2, 3].each do |number|
  if number.even?
    # do something with an even number
  end
end

That example says: With each element in this array, if that element is even, do something with the even element. Collecting the desired elements that way reads passively. Making use of some more of our tools from ruby's Enumerable module, we can refactor it to use the active voice.

[1, 2, 3].select(&:even?).each do |number|
  # do something with an even number
end

The refactoring says: With all of the even numbers in this array, do something. It reads shorter, and expresses the intent of the coder more directly.

From the Real World

The next example we're going to look at comes from ActionController::Base (a central class in rails). I have selected a few illustrative branches from a conditional that spans some sixty lines, in a method that spans nearly one hundred. It is the rendering logic:

if file = options[:file]
  render_for_file(file, options[:status], options[:use_full_path], options[:locals] || {})

elsif template = options[:template]
  render_for_file(template, options[:status], true)

elsif inline = options[:inline]
  add_variables_to_assigns
  render_for_text(@template.render_template(options[:type], inline, nil, options[:locals] || {}), options[:status])

elsif action_name = options[:action]
  template = default_template_name(action_name.to_s)
  if options[:layout] && !template_exempt_from_layout?(template)
    render_with_a_layout(:file => template, :status => options[:status], :use_full_path => true, :layout => true)              
  else
    render_with_no_layout(:file => template, :status => options[:status], :use_full_path => true)
  end
end

It says (bear with me): If the file option is present, render the file, or if the template option is present, render the template, or if the inline options is present, pass the instance variables to the template, and render some inline text from the controller, or if the action option is present, render that action. Note: that transliteration has been shortened somewhat.

The passiveness of a multi-branch conditional damages clarity; it creates a meandering experience for the reader. The problem is that separate concerns are being forced together. In the case of the render method, the conditional determines which type of rendering to perform, and contains the actual code to perform that type of rendering.

Long conditionals also seem to resemble run-on sentences. Since one must remember all of the context as they read through the branches of this sort of structure, it quickly becomes difficult to follow. It would be better to separate the decision from the specific rendering logic; that will make it easier to be more direct.

def render
  render_types   = [:file, :template, :inline, :action]
  type_to_render = render_types.detect { |render_type| options[render_type] }
  send(:"render_#{type_to_render}")
end

def render_action
  template = default_template_name(action_name.to_s)
  if options[:layout] && !template_exempt_from_layout?(template)
    render_with_a_layout(:file => template, :status => options[:status], :use_full_path => true, :layout => true)              
  else
    render_with_no_layout(:file => template, :status => options[:status], :use_full_path => true)
  end
end

def render_inline
  add_variables_to_assigns
  render_for_text(@template.render_template(options[:type], inline, nil, options[:locals] || {}), options[:status])
end

def render_template
  render_for_file(template, options[:status], true)
end

def render_file
  render_for_file(file, options[:status], options[:use_full_path], options[:locals] || {})
end

Now, it reads: The render types are file, template, inline, or action. The type to render is the one that is present in the options hash. Call the method named: render underscore the type to render. Refactored, each of the ways of rendering gets its own method, and those methods have a name. That way, it is clear what each of those blocks of code does, and what we're doing when we call it; we needn't decipher a conditional to find our way to the pertinent logic. Separating code in to small chunks housed by well named methods allows us to express our intentions directly.

As an aside, writing methods this way is far more testable. In its original form, the render method does many different things, depending on the parameters it receives. Testing it would be an exercise in managing side effects. You'd also be testing two different things at once: does it reach the correct branch of the conditional, and does it execute the rendering properly. With the simple refactoring above, you can test the rendering logic separately from the selection of which type of rendering to perform. Dividing methods in to smaller pieces nearly always allows you to isolate functionality more effectively.

And There's More...

I've only scratched the surface in this article. There is a lot more to be learned from natural language writing style. I highly recommend picking up a copy of The Elements of Style. Worst comes to worst, you'll greatly improve your writing of English; best case scenario, you'll pick up a lot of insightful tips on writing clearer and more readable code.

A Response to Dreamhost 1

Posted by james
on Saturday, January 12

Dreamhost sure stirred things up with their article rant about rails deployment and performance issues. Their complaint is roughly that they are having to work too hard to support rails; it needs performance and deployment improvements. Dreamhost voiced all of these complaints on the company's blog.

As they put it...

What I do have personal knowledge of is how difficult it can be to get a Rails application up and running and to keep it running. DreamHost has over 10 years of experience running applications in most of the most popular web programming frameworks and Rails has and continues to be one of the most frustrating.
...the solution from the Rails community itself was quite honestly, stupid.
That suggestion shows either a complete lack of understanding of how web hosting works, or an utter disregard for the real world.
Ruby on Rails needs to be a helluva lot faster.
Ruby on Rails needs to more or less work in ANY environment.

Forgive me for chopping up their arguments. If you have the time, please read the article. These quotes don't quite do it justice; they serve merely to provide the tone of the piece.

Frankly, I think DHH responded well: rails core team is there to serve their own purposes. Ruby on Rails doesn't need to do anything, despite what a Dreamhost blogger might suggest. The people hacking on rails core don't target platforms like Dreamhost's. So, in fact, rails is not designed to run on shared hosting.

It's not just shared hosting, either. Dreamhost is trying to support rails on massively oversold servers. Configuring rails in a shared environment (particularly under apache) is difficult; configuring, and maintaining their servers to support rails probably costs Dreamhost a lot of money. Rails doesn't perform well in that environment, which probably costs them even more money in support calls from frustrated customers. So, their profit margins on rails service are likely narrowing.

Why is that the rails community's problem to solve? Dreamhost has a business challenge. Rails deployment isn't perfect — it has more than its fair share of problems; but, there are plenty of rails apps running just fine in production. This blog, a rails app, runs on a 20$ per month plan from slicehost. The identical app barely ran at Dreamhost. It's anecdotal evidence, sure, but the point is that rails deployment (at least on this scale) is far from impossible. Dreamhost just can't seem to squeeze it in to their oversold offerings. And, they want somebody else to fix the problem for them.

There are plenty of shared hosting offerings available that support rails nicely. Those companies don't seem to be having any trouble creating an environment that works. None of them have published angry articles pointing the finger at "the rails community". But, I digress.

If it were an individual complaining — a noobie who was having a tough time deploying his rails app on a server he could afford — then perhaps there would be a reason to look at this differently. But, Dreamhost is a corporation; they are simply "...looking to capitalize on a framework that's driving a lot of demand...", to borrow some words from DHH.

If this had been a PC manufacturer, complaining loudly (and rudely) that linux doesn't run on cheap enough hardware for them to sell PCs for 50$/each... If Linksys had posted a rant on their company blog, complaining that linux needed to be faster, because their routers weren't performing well enough... If IBM had posted a rant on their company blog, complaining that the linux community showed "...either a complete lack of understanding of how web hosting works, or an utter disregard for the real world."... Everybody would have said the same thing: so, fix it.

Plenty of businesses are capitalizing on open source technologies. But, part of the deal in the real world is that when something doesn't work right, you might have to fix it yourself; but you can, and that's part of what's so great about open source. Engine Yard just hired the entire rubinius team (and some people to hack merb, so I hear). Linux kernel developments are facilitated in large part by corporate sponsorship, bounties, etc. The system works, because companies can solve their own problems, while leveraging the work of countless others. That's how it works in the real world.