3

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?

Ed Haywood
  • 219
  • 1
  • 3

2 Answers2

3

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
  • 3,806
  • 21
  • 14
  • 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 Sep 06 '10 at 18:53
  • 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 Sep 09 '10 at 21:31
  • 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 Sep 09 '10 at 21:42
  • 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 Sep 09 '10 at 21:56
  • 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 Sep 10 '10 at 12:57
  • 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 Sep 27 '10 at 02:20
  • 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 Sep 27 '10 at 05:08
  • Thanks! I apologize for not digging into the documentation before asking. Shoulda thought of that. – Ed Haywood Sep 28 '10 at 00:26
0

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
  • 12,713
  • 11
  • 52
  • 64