polymorphism & REST

I have a polymorphic model:

class Note < ActiveRecord::Base
  belongs_to :notable, :polymorphic => true
end

And some models that use notes:

class User < ActiveRecord::Base
  has_many :notes, :as => :notable
end

class Blog < ActiveRecord::Base
  has_many :notes, :as => :notable
end

My routes are set up:

map.resources :users :notes
map.resources :blogs :notes
etc

REST says I should GET /users/3/notes or /blogs/243/notes if I want to
see the notes associated with the objects. All fine and good, but how
do I implement the index method of the NotesController?

Thanks,
--Dean

def index
  parent = User.find(params[:user_id]) if params[:user_id]
  parent = Blog.find(params[:blog_id]) if params[:blog_id]

  @notes = parent.notes
end

I'd rather have a more general way to find @notes so I can add more
objects with notes without going back to the notes_controller every
time. Is there a good way to inspect params[] for anything_id?

--Dean

I have a similar setting with audits where all of my resources have an
audit log. As this functionality cuts across the resources, I handle
display of the audit log by a separate controller. I haven't come up
with a way to do this neatly with map.resources, rather, I add the
routes like this

  map.with_options(:controller => 'audits', :action => 'show') do |
audits_map|
    audits_map.audits ':resources/:id/audits.:format'
    audits_map.audits ':resources/:id/audits', :format => 'html'
  end

The AuditsController looks (partially) like this

class AuditsController < ApplicationController
  session :off, :if => proc { |req| req.parameters[:format] != 'html' }

  def show
    respond_to do |format|
      format.html { show_html }
      format.atom { show_atom }
      format.xml { show_xml }
    end
  end

  private

  def show_html
    setup_objects
    render :action => 'show'
  end

  def setup_objects
    audited_class = assert_valid_resource_name(params[:resources])
    auditable_type = audited_class ?
audited_class.singularize.camelize : nil
    auditable_id = params[:id]
    @audits =
Audit.find_all_by_auditable_type_and_auditable_id(auditable_type,
auditable_id)
  end

end

To ensure that audit logs can only be displayed for valid resources,
I've monkey patched resource route registration to collect resource
names and check against these names. Here, too, I'm all in favor of a
more elegant solution.

In ApplicationController

  def assert_valid_resource_name(name)
    raise ArgumentError, "Resource does not exist: #{name}" unless
ActionController::Resources.valid_resource?(name)
    name
  end

loaded on startup from a file in lib

ActionController::Resources.module_eval do
  mattr_accessor :valid_resources
  ActionController::Resources.valid_resources = []

  def self.valid_resource?(name) #:doc:
    ActionController::Resources.valid_resources.include?(name.to_sym)
  end

  private
  
  def map_resource_with_collecting(entities, *args, &block)
    ActionController::Resources.valid_resources |= [ entities ]
    map_resource_without_collecting(entities, *args, &block)
  end
  alias_method_chain :map_resource, :collecting
end

HTH,
Michael

Based on another post here, this is what I'm using for restfull nested
routing. It's a work in progress and I'm only a few weeks into ruby/rails
so YMMV. No error checking yet here. That'll come later.

This routine automatically pulls both the parent/child objects from the DB.
Not sure whether I'll leave that or not.

This gets called as a global before_filter, populating an instance variable
chock full of crap that gets consumed downstream. I have another helper
method that generates proper routes for add/edit/show/etc that I might roll
into this as well. I've also considered making this a class, but for now
I'm still tweaking it.

My design will only (currently) allow for nesting 1 level deep, as I plan to
have redundant routes, ala:

Customers
Customers/PO
PO/
PO/LineItems

I've come up with a way to display nested resources within their parents,
via a (still-in-progress) tabular paging thingymabob.

private
def find_type_and_id
  sections = request.env['REQUEST_URI'].scan(%r{/(\w+)/*(\d*)})
  @page = {} unless defined?(@page)
  
  @page[:isnested] = false
  if sections.length > 0
    @page[:base_class_name] = sections[0][0].singularize
    @page[:base_class] = eval(@page[:base_class_name].camelize)
    @page[:base_id] = sections[0][1].blank? ? nil : sections[0][1]
    @page[:base_object] = @page[:base_class].find(@page[:base_id]) if
!@page[:base_id].blank?
  end
  
  if sections.length > 1
    @page[:sub_class_name] = sections[1][0].singularize
    @page[:sub_class] = eval(@page[:sub_class_name].camelize)
    @page[:base_prefix] = @page[:base_class_name] + "_"
    @page[:base_show_path] = sections[0][0] + '/show'
    @page[:sub_new_post_path] =
eval("#{@page[:base_prefix]}#{@page[:sub_class_name].pluralize}_path(#{@page
[:base_id]})")
    @page[:isnested] = true
    
    if !sections[1][1].blank?
      @page[:sub_id] = sections[1][1]
      @page[:sub_object] = @page[:sub_class].find(@page[:sub_id])
      @page[:sub_edit_post_path] =
eval("#{@page[:base_prefix]}#{@page[:sub_class_name]}_path(#{@page[:base_id]
},#{@page[:sub_id]})")
    end
  end
  logger.debug("Page-> #{dumphash(@page)}")
end