views:

78

answers:

2

Say I have a child model with two parent models:

Event has_many tickets

Person has_many tickets

Ticket belongs_to Event
Ticket belongs_to Person

Routes are mapped so Ticket always nests within Event or Person:

resource :people do
  resources :tickets
end

resources :events do
  resources :tickets
end

How do I scope my ticket_Controller CRUD actions by the parent resource?

Right now I'm testing for params and using conditional statements:

class TicketController

  before_filter :get_person
  before_filter :get_event

  def index
    if @person do
      ...
    elsif @event do
      ...
    end
    respond_to
      ...
    end
  end

That seems a bit tedious to do for every action. Is there a more rails-y DRY way to do it?

A: 

You could do this:

class TicketController

  before_filter :get_object

  def index

  end

  private

  def get_object
    type = params['event_id'] ? 'event' : 'person'
    value = type.classify.constantize.find(params[:"#{type}_id"])
    name = '@' + type
    instance_variable_set(name , value)
  end

end

There are many ways of improving the code above.

You could also write the routes as follows:

resources :people, :has_many => :tickets

resources :events, :has_many => :tickets
vise
A: 

The most DRY would be to use inherited_resources:

class TicketsController < InheritedResources::Base
  belongs_to :event, :person, :polymorphic => true
end

Boom...done. If you can't use inherited_resources for whatever reason, though, rather than get_person or get_event you could set up a filter to get_parent like so:

class TicketsController < ActionController::Base
  before_filter :get_parent

  def get_parent
    if params[:person_id]
      @parent = Person.find(params[:person_id])
      @template_prefix = 'people/tickets/'
    elsif params[:event_id]
      @parent = Event.find(params[:event_id])
      @template_prefix = 'events/tickets/'
    else
      # handle this case however is appropriate to your application...
    end
  end

  # Then you can set up your index to be more generic
  def index
    @tickets = @parent.tickets
    render :template => @template_prefix + 'index'
  end
end

Edit: I added the @template_prefix above to address the template issue you mentioned in your comment.

Dave Pirotte
thanks, I'll take a look at inherited_resources.
Ed Haywood
On the second method, say my templates are significantly different for the two types of parents. Any clever way to do that, or just put response statements to two templates inside a conditional?
Ed Haywood
I just edited the above to handle the different templates.
Dave Pirotte
Thanks, now I get it. I could also get rid of the if statement by using in get_parent by using something like this: @prefix = @parent.class.
Ed Haywood
One last question: I don't understand the template prefixes in your code, specifically the presence of 'people/' and 'events/'. The index template is located in '/views/ticket/'. Does Rails interpret nested resource paths in such a way to still find 'ticket/index', when 'people/' or 'events/' is prepended in the render command?
Ed Haywood
To clarify, my question is not about the paths to a nested resource, which I understand, but about the file name for the view template. Would 'events/tickets/index' and 'people/tickets/index' both render the 'tickets/index' template?
Ed Haywood
No, they would not render the same template. I put that in because you said that your templates were significantly different based on whether you were rendering tickets for an Event or tickets for a Person. If you were comfortable using the same template in either case, then yeah you could just use 'tickets/index'. But, if you wanted http://yourapp/events/1/tickets and http://yourapp/people/1/tickets to use different templates, you'd want to control that with a variable (i.e. render 'events/tickets/index'). Does that make sense?
Dave Pirotte
Yes, it does, thanks. I used two templates, event_index and person_index, both in the views/tickets folder. Could I have done that more cleverly? If I used "event/tickets/index" and "person/tickets/index" for the templates, in which folder would they be located?
Ed Haywood
They would be located in "app/views/event/tickets/index" and "app/views/person/tickets/index" respectively. If you pass a relative path to `render` it uses "app/views" as a base. See the documentation here: http://guides.rubyonrails.org/layouts_and_rendering.html#using-render
Dave Pirotte
Thanks! I apologize for not digging into the documentation before asking. Shoulda thought of that.
Ed Haywood