Just A Summary

Piers Cawley Practices Punditry

Initial release of acts_as_resource 18

Posted by Piers Cawley Thu, 25 Jan 2007 18:56:00 GMT

Right. I’ve bundled acts_as_resource up and stuck it on the typosphere SVN server. You can grab it from http://svn.typosphere.org/typo/plugins/acts_as_resource if you’re interested.

It’s currently in what I’d call an all convention, no configuration state – if your resources don’t look pretty similar to the kind of things you get from the resource scaffolding, you’ll probably have some pain, but I expect to rectify that with coming releases. One thing I want/need to do for instance is to allow for ‘relative’ ids in your resource url. For instance, if you’re looking at /albums/10/tracks/982, it’s not the most readable of permalinks… next trick is to allow you to have urls like /albums/because-its-there/tracks/1, ie: the first track on the album ‘Because it’s There’. I’m sort of expecting that you’d do that by doing:

class Album
  has_many :tracks
  acts_as_resource :uri_field => :name_dasherized
end
class Track
  belongs_to :album
  acts_as_list
  acts_as_resource :uri_field => :position, :parent => :album
end

However, my first priority is to add some tests (or, more likely,Rspec specifications) so I’ve got some confidence that I’m not breaking things as I go.

Anyhow, go grab the plugin, have a play, let me know what you think.

'acts_as_resource' progress

Posted by Piers Cawley Thu, 25 Jan 2007 15:30:00 GMT

I’m very nearly ready to release acts_as_resource, I just have to pull up and tidy code that’s currently in my working directory’s ApplicationController and we’re laughing. However, I thought you’d like to see what my nested controller looks like.

class ChildrenController < ApplicationController before_filter :fetch_resources

def index end end

def show
end
def edit
end
def create
  @child = @children.build(params[:child])
  if @child.save
    flash[:notice] = 'Child was successfully created.'
    redirect_to child_path(@child)
  end
end
def update
  if @child.update_attributes(params[:child])
    redirect_to child_path(@child)
  else
    render :action => 'edit'
  end
end
def destroy
  @child.destroy
  redirect_to children_path
end

I’ve removed the XML responses to save vertical space, but they work pretty straightforwardly.

Note that fetch_resources is absolutely generic. It is unscaffolded, uses the resourceful conventions and extra information provided by each model’s acts_as_resource declations to work out what instance variables should be called and sets them appropriately.

With a little work to set up a generically named variable as well as the conventionally named ones (say, resource_chain) and a wee bit of simply_helpful pixie dust (polymorphic paths), it should be possible to write a ResourceController class that does all the work for you but allows you to alter behaviour without having to rewrite an entire action method. But that’s definitely for the next release of the plugin.

Tips for data smugglers

Posted by Piers Cawley Thu, 25 Jan 2007 08:25:00 GMT

While I was working on the acts_as_resource plugin trying to fix things up so that the resource finding side of things works neatly, I realised that I needed some way to get at the ordered list of parameter keys that were matched by the routing system.

One way to do it would have been to parse the path again, but that smacked a little too much of repetition, after all, the routing system knows this stuff already, but how to get at it?

What I needed was the route object that matches the current path.

I ended up modifying ActionController::Routing::Route::parameter_shell. In case you aren’t familiar with the innards of Rails’ routing system, parameter_shell is the ‘skeleton’ params hash that the route populates with information from the current url. So, if you have a route like:

map.connect 'some/paths/are/from/:planet',  \
  :controller => 'planets', :action => 'index'

then the generated route’s parameter shell would look like:

{:controller => 'planets', :action => 'index'}

I needed to stuff the route into that hash, but what key should I use? My first thought was to use :matched_route, but what would happen if some future rails application decided that it needed the :matched_route for something it wanted to do?

So, then I thought of adding a ‘ghost’ key to the hash:

matched_route = self
class << @parameter_shell
  def matched_route
    matched_route
  end
end

That would have worked by hanging a method off the side of the parameter hash which, through the magic of closures would return the matching route. At least, I think so (I’m still not entirely sure when Ruby closes over things). Before I started to test it, I remembered that the first thing the recognize method does when it has a successful match is dup the parameter shell, and singleton class magic doesn’t survive dup.

Then, I remembered something lovely about the way Ruby’s hashes work; keys can be arbitrary objects! All I needed to do was to use a key that was neither a string nor a symbol to smuggle the data through, and there would be no way that it could clash with some future application. So, my current implementation of parameter_shell_with_matched_route looks something like:

def parameter_shell_with_matched_route
  @parameter_shell || 
    returning(parameter_shell_without_matched_route) do |params|
      params[ [:matched_route] ] = self
    end
end
alias_method_chain :parameter_shell, :matched_route

It turns out that various of the Hash support methods in Rails don’t play that well with guarded hash keys like that, so I’ve adapted ActionController::AbstractRequest to pull the matched route out of the parameter hash at the earliest opportunity and stash it in a new method. Here’s the code that does that:

attr_accessor :matched_route
def path_parameters_with_matched_route=(parameters)
  if route = parameters.delete([:matched_route])
    self.matched_route = route
  end
  self.path_parameters_without_matched_route = parameters
end
alias_method_chain :path_parameters=, :matched_route


Just A Summary