resources_controller: things

April 27th, 2008

RC's got a github home now

The subversion repo will still continue to be maintained for the foreseeable future.

RC's also reported as being one of the things under the hood at naked.

Lastly, I've been cooking my CI with garlic.

So I've been curled up in a ball, riding the git avalanche, trying to sort out my rails plugins - making sure they're getting tested against the latest and greatest.

Inspired by this ticket for rspec, git's coolness, some menthol snuff, and a lot of coffee, I came up with garlic.

It's an extremely lightweight set of rake tasks that let you test your plugins or app against various version of rails, and other dependencies.

If you want to see it in action (on one of my plugins), do this:

  git clone git://github.com/ianwhite/inherit_views
  cd inherit_views
  rake cruise

Sit back, watch it download all the dependencies, then create rails apps for each set, and run the rcov task for the plugin in each one... (the download only happens the first time you do it).

You configure it using a little dsl, like this:

garlic do
  # default paths are 'garlic/work', and 'garlic/repos'
  work_path "tmp/work"
  repo_path "tmp/repos"
  
  # repo, give a url, specify :local to use a local repo (faster
  # and will still update from the origin url)
  repo 'rails', :url => 'git://github.com/rails/rails' #, :local => "~/dev/vendor/rails"
  repo 'rspec', :url => 'git://github.com/dchelimsky/rspec'
  repo 'rspec-rails', :url => 'git://github.com/ianwhite/rspec-rails'
  repo 'inherit_views', :url => '.'
  
  # for target, default repo is 'rails', default branch is 'master'
  target 'edge'
  target '2.0-stable', :branch => 'origin/2-0-stable'
  target '2.0.2', :tag => 'v2.0.2'
  
  all_targets do
    prepare do
      plugin 'rspec'
      plugin 'rspec-rails', :branch => 'origin/aliased-render-partial' do
        sh "script/generate rspec -f"
      end
      plugin 'inherit_views'
    end
    
    run do
      cd "vendor/plugins/inherit_views" do
        sh "rake spec:rcov:verify"
      end
    end
  end
end

Notice that I'm using my fork of rpsec-rails, and the plugin specifies that it should use a particular branch 'aliased-render-partial'. The reason for this is that I have some outstanding tickets on rspec, which haven't been resolved. In the meantime, I can just use my patched version. If the patch gets accepted, I can just change the url, and garlic will inform me that I need to remove and run rake garlic:install_repos to get the new one. This is just making use of the awesomely cool coolness of git.

Also notice the block passed to the 'rspec-rails' plugin. This will be executed inside the rails target after the plugin has been installed. Finally the run block says what should happen for the actual CI. In this case cding into the plugin and running an rcov task.

It's new stuff

So it probably has bugs and stuff.

resources_controller new trunk

February 26th, 2008

There's going to be some new features coming in resources_controller. Stay tuned...

Meanwhile, rc has got a new repository location (the old one will still work for a while, but will remain at the current version):

trunk: http://svn.ardes.com/resources_controller/trunk/resources_controller

tag 0.5: http://svn.ardes.com/resources_controller/tags/0.5/resources_controller

For those interested in providing patches, you should checkout http://svn.ardes.com/resources_controller/trunk and run rake pre_commit in that directory to check your changes. Then send a diff along, or post it on the google group.

Why I love Safari Tidy

November 23rd, 2007

Recently I mentioned that I use Safari Tidy—a plugin for Apple’s Safari browser. I thought I’d shed a little light on why I think it’s so great.

For years now individuals—Jeffrey Zeldman most prominent among them—have been evangelising the use of standards-based (X)HTML. There are vague reasons for supporting standards, like getting the warm & fuzzies for doing so; there are abstract reasons, such as preparing work properly to be handed over to a successor; but a very real & immediate reason is that you have a very firm basis upon which to work. It’s all too easy to make mistakes in the semantic properties of HTML, which can yield disastrous rendering results in browsers. And it’s all too easy to not see the problem until late in your design process.

Safari Tidy is unobtrusive & always on: you’ll see that an error has arisen immediately, so you can go fix it. Other implementations of Tidy aren’t so automatic—you have to physically check your markup’s validity, which often means that you don’t check—this is what makes Safari Tidy so fantastic.

I’ll illustrate with a real-world example (one upon which I’m currently working, in fact):

Many out there will be aware of the generating capabilities of Ruby on Rails. A good explanation of this is beyond the scope of this article, but suffice to say you can build the general building-blocks of a web application very quickly, such as setting up a new user in a system. Here’s a rudimentary example of a ready-built form (linked to the users controller). It’s to be rendered in XHTML:

  <% form_for(@user) do |f| %>

    <p>
      Login:<br />
      <%= f.text_field :login %>
    </p>

    <p>
      Email:<br />
      <%= f.text_field :email %>
    </p>

    <p>
      Password:<br />
      <%= f.text_field :password %>
    </p>

    <p>
      <%= submit_tag %>
    </p>

  <% end %>
  

The result of all that <%= f.text_field :foo %> is to create form tags, such as input. Here’s what the above gets with the Login example:

  <p>
    Login:<br />
    <input id="user_login" name="user[login]" size="30" />
  </p>

So far, so good. But what if the user hasn’t filled out a field & you have to display associated errors? Simple in Ruby on Rails. Assuming validation has been set up in your users controller, just add the following bit in front of your form:

  <%= error_messages_for :users %>

With this in place, if your user doesn’t enter a required field, or enters a required field incorrectly, then Rails will automatically inform the user of what was wrong (in the location of the <%= error_messages_for :users %>). Moreover, it’ll wrap the offending field to highlight the user’s attention to it. It will look like this:

  <p>
    Login:<br />
    <div class="fieldWithErrors">
      <input id="user_login" name="user[login]" size="30" />
    </div>
  </p>

And now we have a problem. A div can contain a p, but cannot be contained within it. This is where Safari Tidy comes in really handy. When you test your error message (as above) by viewing it in Safari, Safari Tidy will inform you of an error in your markup. The error it makes is somewhat cryptic:

  <p>
    Login:<br /> 
    <div class="fieldWithErrors">
      <input id="user_login" name="user[login]" size="30" type="text" value="" />
    </div>
  </p>
  -----
  Line 85  Warning: inserting implicit <p>
  Line 89  Warning: trimming empty  <p>

What’s going on here? Since a p can’t contain a div, Tidy thinks you’ve not closed your p tag before you started your div. In addition, it thinks you haven’t opened your p after you’ve closed your div tag. Certainly, knowing your XHTML helps in this situation, since the error it throws is otherwise cryptic; but Tidy gives the clue where to find the error. On a form with a lots of fields, this is really helpful.

Safari Tidy helped me note there was a problem before I even realised that there was a problem—and there really was, the error screwed up my pretty design horribly.

Update: What a wally—I got so wrapped up in Safari Tidy that I didn’t explain how to fix the error! There’s a simple solution: convert your p tags to divs throughout. I don’t know why Rails screws this up in the first place—Rails seems to have been designed pretty well & this is a glaring oversight. It’s really bothersome to have to fix every automatically-generated form.

response_for_resources_controller

November 20th, 2007

If you're using response_for, then you might want to grab response_for_resources_controller

It simply replaces the default action modules with ones that use the response_for idiom.

This means that you can override what an action does without overriding how it responds. For example, I want a standard REST controller, that sets a protected attribute, user, on create.

class PostsController < ApplicationController
  resources_controller_for :posts

  def create
    self.resource = new_resource
    resource.user = current_user
    save_resource
  end # the standard response_for :create is used
end

If you don't know what response_for does, it lets you declaratively set the respond_to of an action. For example: if you want to override some aspect of the response, here I just want to change the flash message on successful create:

class LoginsController < ApplicationController
  resources_controller_for :logins

  response_for :create do |format|
    format.html { flash[:notice = "You have logged in" } if resource_saved?
  end
end

miscellany

I added save_resource and resource_saved? to rc, as a way of sharing the result of the save outside the scope of the action. Previously I've done this with valid?, but realised that this might hit the database unnecessarily in some circumstances. You can use either idiom.

If you've ever subclassed a controller to deal with a particular MIME type, say FBML, you will have had the painful experience of having to copy and paste all of your contoller's actions, adding, or replacing the fbml blocks into the code.

This doesn't feel right - and it isn't. If you have to change some controller logic - you have to remember to change it in all these subclassed controllers.

Enter response_for (rdoc here).

response_for lets you declaratively specify how your actions should respond to particular Mime types. This helps particularly when your action's logic is not simple (like a CRUD action - but it still helps there). Here's an example I just made up:

class ReportController < ApplicationController
  # this puppy gets some models to collaborate to form a report
  def report
    @report = {}
    @report[:invoiced] = Customer.find_invoiced
    @report[:outstanding] = @report[:invoiced].select {|c| c.latest_invoice.outstanding? }
      
    respond_to do |format|
      format.html 
      format.xml { # some funky xml stuff here }
    end
  end
end

Let's say I wanted to make FBML responding version of this controller, normally, I'd have to copy/paste this action and add the fbml responses. With response_for, I do this:

  class FacebookReportController < ReportController
  response_for :report do |format|
    format.fbml { # my funky fbml stuff here, accessing @report}
  end
end

So, down the line, if I change the way my @report object is generated, I don't have to change my subclassed controllers.

Another example: create

You can choose to replace the respond_to block of the action as well. This means that common actions can be written, and the response can be adjusted according to your needs (resources_controller comes to mind).

Here's a standard create action:

class ForumsController < ApplicationController
  def create
    @forum = Forum.new(params[:forum])
    @forum.save
      
    respond_to do |format|
      if @forum.valid?
        flash[:notice] = 'Forum was successfully created.'
        format.html { redirect_to(@forum) }
        format.xml  { render :xml => @forum, :status => :created, :location => @forum }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @forum.errors, :status => :unprocessable_entity }
      end
    end
  end
end

Let's say you want to create an FBML only version of this:

class FacebookForumsController < ForumsController
  response_for :create, :replace => true do |format|
    if @forum.valid?
      format.fbml { # funky fbml stuff for valid record }
    else
      format.fbml { # and for an invalid record}
    end
  end
end

One last example

For many actions, you just want to tell your action to respond to particular MIME type and a template does the rest:

class FacebookUsersController < UsersController
  response_for :index, :show, :types => [:fbml]
  end

You don't need to have a respond_to block defined in your actions - the parent controller could look like:

class UsersController < ApplicationController
  def index
    @users = User.find :all
  end
  
  def show
    @user = User.find params[:id]
  end
end

resources_controller + response_for

For those users of resources_controller, response_for gives you an easy way of adding responses on an ad-hoc basis (like if only one or two of your actions need to be RSS feeds or summat)

This is the first release...

and I welcome any bug reports

Jason Lee of Big First Name just posted his experiences of using resources_controller on the google group.

He writes:

I've just converted one my existing apps ( http://big.first.name ) to using the resources_controller plugin and thought I'd share some results with the group...
before RC, the output of "rake stats"...
+---------------+-------+-------+---------+---------
| Name          | Lines | LOC   | Classes | Methods 
| Controllers   |  1785 |  1324 |      20 |     112 

after RC conversion (with only minor refactoring)...
+---------------+-------+-------+---------+---------
| Name          | Lines | LOC   | Classes | Methods 
| Controllers   |   877 |   655 |      20 |      45

That's 50% less code and 60% less methods. I've still got some chunky controller code lying around but overall I'm very happy with the result.

That's cool!

resources_controller at LRUG

October 10th, 2007

I gave a talk about resources_controller at LRUG which was great fun.

Thanks to everyone for listening and for the feedback. Skills Matter are apparently going to post a video of the presentation at some point. In the meantime, the slides are here.

I've been chatting to a few people at RailsConfEuropoe about resources_controller, so I thought I'd say a few words about waht it's key features are, and about RC at RailsConfEuropoe in general.

Key features

There's a few plugins out there that try and solve the same sort of problem - DRY up RESTful controllers. I believe that RC's standout features can be seen when considering how to write controller for a polymorhpic has_many relationship.

Polymorphic Tags

So you want to tag a bunch of models, and so you sure the :polymorphic has_many assoc, and make a Tag model with belongs_to :taggable, :polymorphic => true.

You want tags to be nested under a bunch of different resources like this:

  map.resources :users do |user|
   user.resources :tags, :controller => 'user_tags'
  end

  map.resources :posts do |post|
   post.resources :tags, :controller => 'post_tags'
  end

Standardly, you'd then have to write two controllers: UserTagsController, and PostTagsController, and map the above two nested routes to those different controllers. These would be essentially the same functionality except:

  • they would load different models in before filters: @user in one and @post in the other,
  • they get the post from different collections (@tag = @user.tags.find(params[:tags_id]) vs @tag = @post.tags.find(params[:tags_id])),
  • they redirect to different routes on completion of certain actions user_tags_path and post_tags_path in the other.

To do this, even with plugins to dry everything up, you still need to create two (or three, or four) controllers for tags - all doing essentially the same thing.

it gets worse. You'll need a bunch of different views - because they all need to link to urls relative to the enclosing resource. Suddenly you've got a lot of really similar code - or some really ungly hacks in your views.

(and all of this gets much worse if you have deeply nested routes)

Polymorphic tags with resources_controller

resources_controller (used in the default way) inspects the route that was used to invoke the controller. From this, it:

  • loads all of the enclosing resources,
  • uses the immediately enclosing resource as the resource service (the object that we send find and new to to - in the case of /posts it would be the Post class, in the case of /post/1/tags it would be the @post.tags association),
  • does some method missing magic so that you can refer to all named routes relative to the current resource

All this means you just need to write one controller, and one set of views for Tags

Here's some sample code

  class TagsController < ApplicationCntroller
    resources_controller_for :tags
  end

in show.html.erb:

  <%= link_to 'tags', resources_path %>

The above will be user_tags_path(@user) in one case and post_tags_path(@post) in another.

It gets better, you can refer to the enclosing resource as well:

  <%= link_to "back to #{enclosing_resource_name.humanize}", enclosing_resource_path %>

And if you have routes like /users/1/posts/2/tags, and /posts/1/tags, the you can use the same view for posts:

  <%= link_to 'tags', resource_tags_path %> # in /users/1/posts/2/tags will be:
                                            # user_post_tags_path(@user, @post)
                                            
  <%= link_to 'tags', resource_tags_path %> # in /posts/2/tags will be:
                                            # post_tags_path(@post)

That's just some of the features, I'd love to get feedback, patches, bug reports, etc. There are links to RC via svn, and rdoc on our plugins page

BoF and RejectConf

Man, I've got a lot to learn about presentations...

I gave a BoF at RailsConfEurope07 session on resources_controller - I was expecting about 10 people and a round table discussion on taking the pain out of RESTful controllers. About 50 or 60 people turned up so we ended up all crowded round a couple of laptops. But everyone was friendly and there was no heckling. Paolo, who gave the incredibly entertaining talk on widgets, took some photos

There was, however, plenty of heckling at the RejectConf talk. The format was 5 minutes of slides, 20 seconds automatic countdown for each one. I wrote the presentation during the day, and ran through it with my wife before hand. It sucked - way too much info. So I cut it down.

However, I was first up, and gave the audience the choice of the insane, or sane, talk, and they chose insane. I tried to get across about 1/2 an hour's worth of stuff in 5 minutes, and the audience looked as though they were in a wind tunnel. It was a great icebreaker for the other speakers though. I'll post the slides here in due course.

RejectConf was simply awesome by the way. The berlin ruby user group rocks.

resources_controller - update

September 5th, 2007

It's been a while, but some major improvements to resources_controller have just been checked in.

The test coverage has slipped just a bit - that's my next task. However, it's still pretty good. Thanks to the RC group (in particluar Chris Hapgood) and the Guys at Greenvoice for nudging me towards singular resources, and away from lots of inherited controllers.

Major headlines

  • Singular resources are fully supported
  • RC loads enclosing resources by default, which means you pretty much just have to write one line to have all your routes taken care of (more on this below).
  • Cleaner code

Go get get it from the ArDes plugins page.

What follows is from the rdoc

With resources_controller (http://svn.ardes.com/rails_plugins/resources_controller) you can quickly add an ActiveResource compliant controller for your your RESTful models.

Examples

Here are some examples - for more on how to use RC go to the Usage section at the bottom, for syntax head to resources_controller_for

Example 1: Super simple usage

Here's a simple example of how it works with a Forums has many Posts model:

  class ForumsController < ApplicationController
    resources_controller_for :forums
  end

Your controller will get the standard CRUD actions, @forum will be set in member actions, @forums in index.

Example 2: Specifying enclosing resources

  class PostsController < ApplicationController
    resources_controller_for :posts, :in => :forum
  end
As above, but the controller will load @forum on every action, and use @forum to find and create @posts

Wildcard enclosing resources

All of the above examples will work for any routes that match what it specified

  
              PATH                     RESOURCES CONTROLLER WILL DO:

  Example 1  /forums                   @forums = Forum.find(:all)

             /users/2/forums           @user = User.find(2)
                                       @forums = @user.forums.find(:all)

  Example 2  /posts                    @posts = Post.find(:all)

             /forums/2/posts           @forum = Forum.find(2)
                                       @posts = @forum.posts.find(:all)

             /sites/4/forums/3/posts   @site = Site.find(4)
                                       @forum = @site.forums.find(3)
                                       @posts = @forum.posts.find(:all)

             /users/2/posts/1          This won't work as the controller specified
                                       that :posts are :in => :forum

It is up to you which routes to open to the controller (in config/routes.rb). When you do, RC will use the route segments to drill down to the specified resource. This means that if User 3 does not have Post 5, then /users/3/posts/5 will raise a RecordNotFound Error. You dont' have to write any extra code to do this oft repeated controller pattern.

With RC, your route specification flows through to the controller - no need to repeat yourself.

If you don't want to have RC match wildcard resources just pass :load_enclosing => false

  resources_controller_for :posts, :in => :forum, :load_enclosing => 'false'

Example 3: Singleton resource

Here's an example of a singleton, the account pattern that is so common.

  class AccountController < ApplicationController
    resources_controller_for :account, :class => User, :singleton => true do
      @current_user
    end
  end

Your controller will use the block to find the resource. The @account will be assigned to @current_user

Example 4: Allowing PostsController to be used all over

First thing to do is remove :in => :forum

  class PostsController < ApplicationController
    resources_controller_for :posts
  end

This will now work for /users/2/posts.

Example 4 and a bit: Mapping non standard resources

How about /account/posts? The account is found in a non standard way - RC won't be able to figure out how tofind it if it appears in the route. So we give it some help.

(in PostsController)

  map_resource :account, :singleton => true, :class => User, :find => :current_user

Now, if :account apears in any part of a route (for PostsController) it will be mapped to (in this case) the current_user method of teh PostsController.

To make the :account mapping available to all, just chuck it in ApplicationController

This will work for any resource which can't be inferred from its route segment name

  map_resource :peeps, :source => :users
  map_resource :posts, :class => BadlyNamedPostClass

Example 5: Singleton association

Here's another singleton example - one where it corresponds to a has_one or belongs_to association

  class ImageController < ApplicationController
    resources_controller_for :image, :singleton => true
  end

When invoked with /users/3/image RC will find @user, and use @user.image to find the resource, and @user.build_image, to create a new resource.

Putting it all together

An exmaple app

config/routes.rb:

 map.resource :account do |account|
   account.resource :image
   account.resources :posts
 end

 map.resources :users do |user|
   user.resource :image
   user.resources :posts
 end

 map.resources :forums do |forum|
   forum.resources :posts
   forum.resource :image
 end

app/controllers:

 class ApplicationController < ActionController::Base
   map_resource :account, :singleton => true, :find => :current_user

   def current_user # get it from session or whatnot
 end

 class ForumsController < AplicationController
   resources_controller_for :forums
 end
   
 class PostsController < AplicationController
   resources_controller_for :posts
 end

 class UsersController < AplicationController
   resources_controller_for :users
 end

 class ImageController < AplicationController
   resources_controller_for :image, :singleton => true
 end

 class AccountController < ApplicationController
   resources_controller_for :account, :singleton => true, :find => :current_user
 end

This is how the app will handle the following routes:

 PATH                   CONTROLLER    WHICH WILL DO:
 
 /forums                forums        @forums = Forum.find(:all)
 
 /forums/2/posts        posts         @forum = Forum.find(2)
                                      @posts = @forum.forums.find(:all)

 /forums/2/image        image         @forum = Forum.find(2)
                                      @image = @forum.image   
 
 /image                       no route

 /posts                       no route

 /users/2/posts/3       posts         @user = User.find(2)
                                      @post = @user.posts.find(3)
 
 /users/2/image POST    image         @user = User.find(2)
                                      @image = @user.build_image(params[:image])

 /account               account       @account = self.current_user

 /account/image         image         @account = self.current_user
                                      @image = @account.image

 /account/posts/3 PUT   posts         @account = self.current_user
                                      @post = @account.posts.find(3)
                                      @post.update_attributes(params[:post])

Views

Ok - so how do I write the views?

For most cases, just in exactly the way you would expect to. RC sets the instance variables to what they should be.

But, in some cases, you are going to have different variables set - for example

  /users/1/posts    =>  @user, @posts
  /forums/2/posts   =>  @forum, @posts

Here are some options (all are appropriate for different circumstances):

  • test for the existence of @user or @forum in the view, and display it differently
  • have two different controllers UserPostsController and ForumPostsController, with different views (and direct the routes to them in routes.rb)
  • use enclosing_resource - which always refers to the... immediately enclosing resource.

Using the last technique, you might write your posts index as follows (here assuming that both Forum and User have .name)

  <h1>Posts for <%= link_to enclosing_resource_path, "#{enclosing_resource_name.humanize}: #{enclosing_resource.name}" %></h1>

  <%= render :partial => 'post', :collection => @posts %>

Notice enclosing_resource_name - this will be something like 'user', or 'post'. Also enclosing_resource_path - in RC you get all of the named route helpers relativised to the current resource and enclosing_resource. See NamedRouteHelper for more details.

This can useful when writing the _post partial:

  <p>
    <%= post.name %>
    <%= link_to 'edit', edit_resource_path(tag) %>
    <%= link_to 'destroy', resource_path(tag), :method => :delete %>
  </p>

when viewed at /users/1/posts it will show

 <p>
   Cool post
   <a href="/users/1/posts/1/edit">edit</a>
   <a href="js nightmare with /users/1/posts/1">delete</a>
 </p>
 ...

when viewd at /forums/1/posts it will show

 <p>
   Cool post
   <a href="/forums/1/posts/3/edit">edit</a>
   <a href="js nightmare with /forums/1/posts/3">delete</a>
 </p>
 ...

This is like polymorphic urls, except that RC will just use whatever enclosing resources are currently loaded to generate the urls/paths.

General Usage

To use RC, there are just three class methods on controller to learn.

  resources_controller_for name, options, &block
 
  nested_in name, options, &block

  map_resource name, options, &block

Customising finding and creating

If you want to implement something like query params you can override find_resources. If you want to change the way your new resources are created you can override new_resource.

  class PostsController < ApplicationController
    resources_controller_for :posts

    def find_resources
      resource_service.find :all, :order => params[:sort_by]
    end

    def new_resource
      returning resource_service.new(params[resource_name]) do |post|
        post.ip_address = request.remote_ip
      end
    end
  end

In the same way, you can override find_resource.

Writing controller actions

You can make use of RC internals to simplify your actions.

Here's an example where you want to re-order an acts_as_list model. You define a class method on the model (say *order_by_ids* which takes and array of ids). You can then make use of *resource_service* (which makes use of awesome rails magic) to send correctly scoped messages to your models.

Here's how to write an order action

  def order
    resource_service.order_by_ids["things_order"]
  end

the route

  map.resources :things, :collection => {:order => :put}

and the view can conatin a scriptaculous drag and drop with param name 'things_order'

When this controller is invoked of /things the :order_by_ids message will be sent to the Thing class, when it's invoked by /foos/1/things, then :order_by_ids message will be send to Foo.find(1).things association

ArDes Rails Plugins Page

June 7th, 2007

Check out our rails plugins page, which contains links to rdoc, svn and other plugin related stuff.

You may also be interested in the latest build report which shows what plugins are tested against what versions of rails.

Announcing inherit_views

February 23rd, 2007

Edge rails recently added support for multiple view paths. Very cool. This means you can ship a plugin complete with views, and have your controllers use those views (without copying them to app/views), and you get overriding for free.

inherit_views (svn and rdoc) is a plugin that allows you to do something similar, but within a controller hierarchy. (This is not a replacement, or duplication of 'multiple view paths')

Example

Here's an example of the syntax

class PostsController < ApplicationController
  inherit_views
end

class UserPostsController < PostsController
end

UserPostsController will look for a particular view in app/views/user_posts, if it doesn't find it, then it looks in app/views/posts.

For the example above, you may wish to change some aspects of just index.rhtml, and leave the other views alone. Previously you would have to set up a bunch of erb (or haml, or whatever) files that either duplicate, or render the unchanged files. No longer.

render_parent

The inheritance model is familiar, so why not have the familiar 'super' construct in views?. You can now:

posts/_resource.rhtml:
  <%= post.title %>
  <%= link_to 'comments', comments_path(post) %>
  
user_posts/_resource.rhtml:
  <%= render_parent :locals => {:post => post} %>
  <%= link_to 'my comments', user_comments_path(@user, post) %>

Why?

Two main reasons:

  1. Making life for STI users (I'm a heavy user) easier. This is a classic case where inheriting views makes life easier. It's also easier if you use something like resources_controller to decouple the model name form the view (instead of @my_model - which then becomes @my_model_subclass, use a local, or in the case of resources_controller resource)
  2. Providing the basic tools to build auto-scaffold type systems. I think the idea of these systems is great - but they never work quite right for me. For me, they cover too much ground, make too many assumptions. So, I'd like to have a set of tools that help me easily build one of these auto-scaffold things for use in just one, or a few projects. inherit_views and resources_controller are two such tools.

resources_controller part II

February 14th, 2007

Update: check out resource s controller update

Update: point 3 below (broken in rails < edge) is now solved. (non-edge) Rails <= 1.2.2 users should also install this plugin.

(this is a followup to this entry)

There were some problems with resources_controller as released a couple of weeks ago, including:

  • No support for name_prefix, which crippled the url_helpers
  • Limited support for polymorphic resources
  • Broken in rails < Edge, due to the fact that scoped creates for associations are new.
  • Lack of test support to show me the above

The 1st, 2nd and 4th points have been thoroughly addressed in the latest release, and if you run the tests you'll find out if your version of rails is susceptible to the 3rd point. The solution to the 3rd point should be pretty trivial and will appear soon.

(Thanks to Igor Alexeiuk for pointing me in the right direction on the above, and for contributing some code for name_prefix stuff)

Here's an example of what you can do:

class TagsController < ApplicationController
  resources_controller_for :tags
  nested_in :taggable, :polymorphic => true, :load_enclosing => true
end

The above controller can be used to service all of your models that have tags, wherever they're located in the resource schema. The controller will load the enclosing resources (raising RecordNotFound if the relationships don't match) and the named routes will also work.

Take a look at the test app to see what the new syntax is like, and what the plugin is capable of here

Announcing resources_controller

February 2nd, 2007

Update: check out resource s controller update

Update: check out this entry

Update: for Rails <= 1.2.2 users, also install this plugin


So you want to write a quick and dirty REST server:

  • Models, ActiveRecord mmmm nice and DRY tick
  • Routes, super easy - even nesting routes! tick
  • Controllers - oh crap, we're talking scaffoling, then changing like 8 lines of code in each one of the nested controllers - there's major duplication - there must be a better way...

The above inspired me to come up with the resources_controller plugin (svn, and rdoc).

(If you're interested in how this works, and you don't want to read the rdoc, read my post at rails weenie)

Here's an example, your usual forums/posts/comments one. See the docs for more examples.

  class ForumsController < ApplicationController
    resources_controller_for :forums
  end
  
  class PostsController < ApplicationController
    resources_controller_for :posts, :in => :forum
  end
  
  class CommentsController < ApplicationController
    resources_controller_for :comments, :in => [:forum, :post]
  end

Couple of caveats: you need rspec to run the tests, and there's no resource_controller (singleton resource controller) plugin as yet.

Please let me know how to make it better.

Update

2/Feb: Much better docs, and a few improvements

I'm testing some concurrency issues with a Singleton Active Record class. The Singleton ActiveRecord in question should create only one row when the table is empty, and all subsequent instances should use that row.

Note: I'll rewrite this post soon with a more common rails example - updating a record which uses the very latest results from a select.

I want to test the case where a number of concurrent processes do this at once. What's the best way to do this in rails?

(To solve the concurrency issues I'm using pessimistic locking that recently made it into rails - here I'm focussing on testing my solution. I suggest reading this scrap from Ryan for illumination on Rails and locking). Oh yeah, I'm also using RSpec awesomeness in the tests.

First attempt: fork (doesn't work)

The following test fires up 30 processes all asking for a singleton row, and makes sure only one was created.

MySingleton.delete_all

pids = (1..30).to_a.collect do
  fork { MySingleton.instance }
end
pids.each {|pid| Process.waitpid pid} # wait for them all to finish

MySingleton.count.should == 1

The above code doesn't work - the database connection kept dropping out. Why is that? Because a database connection is for one single threaded process - if you simultaneously bombard a single connection with a bunch of stuff, it's going to get confused.

Second attempt: threads (not quite what we want)

ActiveRecord allows you to specify that you'll be using the database concurrently:

ActiveRecord::Base.allow_concurrency = true

This allows the database connection to be handled properly when spawning new threads. (The difference between a forked process and a thread is that forked process runs a new process (a copy of the the forking process) with it's own memory space, whereas a thread shares the memory space, but may run asynchronously).

After adding ActiveRecord::Base.allow_concurrency = true and ActiveRecord::Base.allow_concurrency = false to setup and teardown we change the test to:

MySingleton.delete_all

threads = (1..30).to_a.collect do
  Thread.new { MySingleton.instance }
end
threads.each {|thread| thread.join } # wait for the threads to complete

MySingleton.count.should == 1

Which passes! However, this is not be the case I'm trying to test. Allowing concurrency, and using threads, is a case where one process manages the single database connection intelligently for a concurrent application.

I want to test the case where a number of different processes, all with their own connection, are accessing the database. And I want to be sure there's no intelligent handling of the connection within a class. This is the usual real-world case - lots of distinct rails processes all garnering the database's attention.

(I have still kept this test, as it shows my Singleton class works in a multi-threaded application)

Third attempt: fork mk II (success!)

So I want to establish a new connection in each forked process. Here's a method that does that, taking a db config (we'll see how to get that in a second).

def fork_with_new_connection(config, klass = ActiveRecord::Base)
  fork do
    begin
      klass.establish_connection(config)
      yield
    ensure
      klass.remove_connection
    end
  end
end

To get the config, we remove the current connection, the return value of that method is the config. We can pass this to the new fork method, and also use it to re-establish the parent connection when everything's done. The test now looks like this:

MySingleton.delete_all

config = ActiveRecord::Base.remove_connection
pids = (1..30).to_a.collect do
  fork_with_new_connection(config) { MySingleton.instance }
end
ActiveRecord::Base.establish_connection(config)

pids.each {|pid| Process.waitpid pid}

MySingleton.count.should == 1

Roundup

So now I've got two techniques for concurrency testing:

  • when one ActiveRecord connection is being used concurrently, and
  • when multiple ActiveRecord connections are accessing the database

The Singleton example is just one case of a model with concurrency issues, another would an update which absolutely must be based on the very latest result of a select.

A word of warning: if you're running something like rcov with your tests, each forked process will also be running rcov, along with all of the associated overhead.