Reprocessed, by Matt Patterson

Something approaching a weblog

Shared templates, context-dependent url_for, and RSpec

I have this Rails app which has a set of shared templates which are used by two actions in different controllers. Each action renders one of three shared templates, dependent on the context. This is all fine, except that I rely on the magic of url_for to generate URLs based on the current controller; and I rely on RSpec's view specs to keep things working and tidy.

Because RSpec's view specs infer the controller and action name from the path to the template being specced a template like shared/month_archive.html.haml will generate a controller name of shared and an action name of month_archive. The problem was that in use the controller name would be archives and the action by_date. url_for was having kittens as a result.

The solution is to override RSpec's choice for controller and action with something which reflects the views' real use. It's really easy:

describe "/shared/context_sensitive_view"
  def derived_controller_name(*args)
    "sensible_controller_name"
  end

  def derived_action_name(*args)
    "sensible_action_name"
  end

  it "should do something"
    ...
    render
  end
end

Then, when you call url_for it gets :controller => 'sensible_controller_name', :action => 'sensible_action_name' instead of :controller => 'shared', :action => 'context_sensitive_view' as part of the params, and it makes URLs instead of having Kittens.

If you need finer control of the params, perhaps because of url_for stuff, you can also do one of the following:

describe "/shared/context_sensitive_view"
  # No need to override .derived_controller_name or .derived_action_name
  before(:each) do
    # With Mocha
    template.stubs(:params).returns(:controller => 'sensible', :action => 'also_sensible', :other_param => 'needed value')
    # Or, with RSpec's own mocking framework'
    template.stub!(:params).returns(:controller => 'sensible', :action => 'also_sensible', :other_param => 'needed value')
  end
end

Of course, stubbing the params can often bite you -- It is, after all, a HashWithIndifferentAccess so the stringiness or otherwise of your hash keys can cause hard to trace problems. YMMV.

Not forgetting:

This page is: