Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 3 - Nested resources and polymorphic paths: OK to two levels, but break at three

I'm trying to do a simple family reunion site with: "posts", "families", "kids", and "pictures". Ideally I'd like the routes/relationships to be structured this way:

  resources :posts do
    resources :pictures
  end

  resources :fams do
     resources :pictures
     resources :kids do
       resources :pictures
     end
  end

In the models I have the necessary "belongs_to" and "has_many" relationships set between fams and kids. Fams, kids, and posts all are defined with "has_many :pictures, :as => :imageable" while pictures are defined as: belongs_to :imageable, :polymorphic => true

When trying to do link_to "Edit" and link_to "Destroy" in the pictures views I run into all sorts of _path problems. polymoric_path works fine at two levels, namely for posts-pictures and fams-pictures but it fails to handle the three level case of fams-kids-pictures. I'm guessing that it was not designed to handle the two levels of "imageable" objects above the picture object. Another issue is that in one instance the pictures controller has to handle a "one level" resource-nesting situation and in another it has to handle a "two levels" situation. Not sure how to approach this.

One thing I did try was to not nest resources more than one deep, per the Ruby Guides directions. I structured them like this:

  resources :posts do
    resources :pictures
  end

  resources :fams do
     resources :pictures
     resources :kids
  end

  resources :kids do
     resources :pictures
  end

This caused another set of problems with paths since the fam to kid relationship was no longer preserved. I also could not get polymorphic_path to function correctly accross all the different picture views.

So here is my main question: Does anyone know of a Rails 3 example/tutorial where nested resources, belongs-to/has_many, and polymorphic relationships are all put together, especially where it is not just the simple, two-level relationship that most examples show? (I'm fairly new to Rails and the Rails 2 examples I've found in these areas are confusing given my lack of Rails historical experience.)

Or can someone tell me how to structure the link_to EDIT and link_to DELETE statements for my picture views, as well as the redirect-to statement for my create, update, and destroy methods in my pictures controller?

Thanks!

like image 527
Don Leatham Avatar asked May 07 '11 00:05

Don Leatham


2 Answers

Your code example that limited your nesting to 2 levels is quite near the answer. To avoid duplicate routes for fams->kids and kids, you can use the :only option with a blank array so that the 1st-level kids will not generate routes except in the context of kids->pictures, like so:

resources :posts do
  resources :pictures
end

resources :fams do
  resources :pictures
  resources :kids
end

resources :kids, only: [] do # this will not generate kids routes
   resources :pictures
end

For the above code, you can use the following to construct your polymorphic edit url:

polymorphic_url([fam, picture], action: :edit) # using Ruby 1.9 hash syntax
polymorphic_url([kid, picture], action: :edit)
like image 62
mikong Avatar answered Nov 05 '22 23:11

mikong


Have been having this exact same problem for a while. I have it working now, but it isn't beautiful :S

From a nested monster like:

http://localhost:3000/destinations/3/accommodations/3/accommodation_facilities/52

Your params object ends up looking like this:

action: show
id: "52"
destination_id: "3"
accommodation_id: "3"
controller: accommodation_facilities

where "id" represents the current model id (last on the chain) and the other ones have model_name_id

To correctly render another nested link on this page, you need to pass in an array of objects that make up the full path, eg to link to a fictional FacilityType object you'd have to do:

 <%= link_to "New", new_polymorphic_path([@destination, @accommodation, @accommodation_facility, :accommodation_facility_type]) %>

To generate this array from the params object, I use this code in application_helper.rb

def find_parent_models(current_model = nil)
    parents = Array.new

    params.each do |name, value|
      if name =~ /(.+)_id$/
        parents.push $1.classify.constantize.find(value)
      end
    end

    parents.push current_model

    parents
end

Then to automatically make the same link, you can merrily do:

 <%= link_to "New", new_polymorphic_path(find_parent_models(@accommodation_facility).push(:accommodation_facility_type)) %>

Any pointers on making this solution less sketchy are very welcome :]

like image 42
dangerous.beans Avatar answered Nov 05 '22 23:11

dangerous.beans