3

Okay, so here's an example scenario. There is a student resource resources :students, and students has and belongs to many collections: resources :clubs, resources :majors, etc.

So we can set up our routes easily enough...

resources :clubs do
  resources :students
end
resources :majors do
  resources :students
end
resources :students do
  resources :clubs
  resources :majors
end

which generates us a bunch of standard RESTful routes

  • /clubs
  • /clubs/:id
  • /clubs/:club_id/students
  • /clubs/:club_id/students/:id
  • /majors
  • /majors/:id
  • /majors/:major_id/students
  • /majors/:major_id/students/:id
  • /students
  • /students/:id
  • /students/:student_id/clubs
  • /students/:student_id/clubs/:id
  • /students/:student_id/majors
  • /students/:student_id/majors/:id

So here's my question. With REST semantics, how would one delete a major for a student? Browsing students under a major /majors/:major_id/students/:id would show that student in the specific major's 'collection'. But the route for deleting an :id, points to StudentsController#destroy, which would delete the student completely. Whoops! So maybe we go the other way, and perform DELETE on the resource at /students/:student_id/majors/:id and well now UnderwaterBasketweaving is now no longer offered at this school...Whoops!

Now granted, we could set the destroy method of ClubsController, MajorsController, or StudentsController to look for club_id, or major_id, or student_id, but lets say we also down the road want to add Fraternities and GraduatingClasses, etc. Each class would start to be comprised of huge switch conditionals looking to see what param was present... and then find the collection of either the top resource and remove the bottom resource, or vise versa. The Models themselves should decide whether they delete themselves if they have no more association records... 'Destroy' on that resource has become really a misnomer...

Is there an easier way to do this? Even popular restful rails plugins like make_resourceful or resource_controller would blow away UnderwaterBasketweaving when removing it from Joe's Majors or completely delete JohnDoe when removing him from the major UnderwaterBasketweaving. It would seem there's a potential for looking at the association to understand the desired effects of the semantics and what 'destroy' should do.

Then again, am I looking at this all wrong? Is it not UnderwaterBasketweaving -> Joe but UnderwaterBasketweaving+Joe as a single resource and what we're deleting is truly not Joe, nor UnderwaterBasketweaving, but the resource representing the combination? However, that's not easy when the controllers are Students and Majors, that in effect represent resources of the same name (MVC has really become RV...in the 'convention' approach and not developing Controllers that may have no bearing on a Model name, or the path to reach it) So you'd be deleting a Major or a Student; pick your poison...

How can I avoid managing conditionals across an infinite graph of associated resources where delete really isn't the intent when the delete is desired to be in context of a collection and not pertaining to its singularity...?

...major.student.delete... Is there a way for 'student' ActiveRecord object to know it was sent a 'delete' message in a method chain beginning with the 'major' AR object ?

Andy
  • 75
  • 5

1 Answers1

2

Well the standard RESTful aproach is to use has_many :through and generate a cotroller for the association resource. Naming association resources is always difficult, but I'll attempt for this example.

resources :majors do
  resources :studies
  resources :students
end
resources :students do
  resources :studies
  resources :majors
end

The models would be of course:

class Major < ActiverRecord::Base
  has_many :studies
  has_many :students, :through => :studies
end

etc. (comment if you want me to elaborate)

Then for a student you wouldn't DELETE it's associated @student.major but his @student.studies.where :major => @major.

Jakub Hampl
  • 39,863
  • 10
  • 77
  • 106
  • I think I like this approach, except for the sheer volume it could cause of excess code. Outside of my contrived example, I'd have over 100 association resources in my actual project. It seems that we're breaking the `C over C` paradigm here by configuring a lot of extra code where the ideas are already there in the original association code (not using `:through`); though the reason some of the restful plugins like `make_resourceful` are nice is that they move the relationship semantics into the controller space so that this would be more feasible to logically process into meta-generated code – Andy Nov 07 '10 at 23:12
  • Also, you wouldnt have `resources :majors having many :students` since its not a nested resource under your paradigm (as I'm following...). The nested resource to each is the association resource, which definitely solves the problem of deleting the wrong object. The association resource could then have nested resources of what it relates to which are the true domain models. Again, I love it, though worry about the excessive association resources to manage. _Its interesting that weve gone from hierarchical taxonomy into promoting multi-faceted ones, yet (RESTful) URL is still very hierarchical_ – Andy Nov 07 '10 at 23:18
  • I'm not sure I follow your second comment. Generally spoken `has_many :through` behaves exactly like `has_and_belongs_to_many` except that it gives you control over the association. So you will need to generate the models for those associations. However you can restfully manage their creation and deletion in other controllers. See `accepts_nested_attributes_for`. Controller plugins are nice (I personally use InheritedResources, but for many cases you'll have to write the code yourself (or write a library that solves your particular use case). – Jakub Hampl Nov 08 '10 at 03:36
  • (It'd be nice if comments on here were threaded (reason blog swapped in solutions like IntenseDebate|Discus)) I just meant that while the Models are nested, the Resources aren't particularly. Your answer works because it breaks my prior mindset of 1:1 of models<->resources. The 'controller' really is the Resource, which can implement an abstraction (e.g. 'studies'), which I guess then wouldn't even need to be a `has_many :through` (usually a `habtm` where you need to have domain logic on the association; in this case we just need to represent that association, but it has no logic per se) – Andy Nov 08 '10 at 11:20