0
votes

There are some other Qs (1 2) but unfortunately they don't help.

I have an event resource, and a namespaced event_participant. The routes, actions, etc look good, but when I attempt to click a link_to to my custom, namespaced action the route is mapped to the events#update controller. No idea what's going wrong!

config/routes.rb

Rails.application.routes.draw do
  resources :events
  namespace :events do
      put "/:id/add_or_rm_role" => "participants#add_or_rm_role"
  end
end

rake routes

          Prefix Verb   URI Pattern                          Controller#Action
          events GET    /events(.:format)                    events#index
                 POST   /events(.:format)                    events#create
       new_event GET    /events/new(.:format)                events#new
      edit_event GET    /events/:id/edit(.:format)           events#edit
           event GET    /events/:id(.:format)                events#show
                 PATCH  /events/:id(.:format)                events#update
                 PUT    /events/:id(.:format)                events#update
                 DELETE /events/:id(.:format)                events#destroy
                 PUT    /events/:id/add_or_rm_role(.:format) events/participants#add_or_rm_role #This one looks good!

participants_controller.rb

class ParticipantsController::EventsController < ApplicationController
  load_and_authorize_resource
  def add_or_rm_role
    event = Event.find(event_participants_params.event_id)
    #add user role to event...
    redirect_to event, notice: 'Changes have been saved!'
  end

  private
    def event_participants_params
      params.permit(:role, :is_add, :event_id, :user_id)
    end
end

The events_controller.rb is the autogenerated one.

The link_to on events/show is

    <%= link_to "Sign Up For Event", 
        event_path(role: Event::Participant.event_role_types[:participant], is_add: false, id: @event.id),
        action: :add_or_rm_role,
        method: :put, remote: true %>

When I click the link, the following PUT request is sent. You can see it is processing the wrong link as the wrong format. The link_to is a little button that adds the current_user as a participant to a given event.

Started PUT "/events/_ceEY?is_add=false&role=3" for 127.0.0.1 at 2017-07-31 20:05:10 -0400
Processing by EventsController#update as JS
  Parameters: {"is_add"=>"false", "role"=>"3", "id"=>"_ceEY"}
#......

Any idea on what wires are getting cross here?

UPDATE

The following answers suggest using a nested route instead of a namespaced one, but I'd like to keep trying to get namespaced routes to work to improve my organization/file structure in my controller. Switching to nested will be the nuclear option, but I'd like to see where this takes me first. Utilizing the current feedback I have made the following changes:

config/routes.rb

#...
  namespace :events do
      put :add_or_rm_role, to: 'participants#add_or_rm_role'
  end

rake routes

              Prefix Verb   URI Pattern                      Controller#Action
   #...
   events_add_or_rm_role PUT    /events/add_or_rm_role(.:format) events/participants#add_or_rm_role

link_to

    <%= link_to "Sign Up For Event", 
        events_add_or_rm_role_path(role: Event::Participant.event_role_types[:participant], is_add: false, event_id: @event.id),
        method: :put, remote: true %>

events/participants_controller.rb

class Events::ParticipantsController < ApplicationController #Renamed
  #...
  def event_participants_params
    params.permit(:role, :is_add, :event_id, :user_id)
  end
end

Despite this the controller#action is still wrong (and thus seems to be adding in the id param)

Started PUT "/events/add_or_rm_role?event_id=_ceEY&is_add=false&role=3" for 127.0.0.1 at 2017-07-31 21:37:45 -0400
Processing by EventsController#update as JS
  Parameters: {"event_id"=>"_ceEY", "is_add"=>"false", "role"=>"3", "id"=>"add_or_rm_role"}

Where did I go wrong (server was restarted)?

UPDATE

If I change the order of my Rails routing (giving add_or_rm_role higher precedence), the correct controller is matched. Is this because my Event IDs are string?

  namespace :events do
      put :add_or_rm_role, to: 'participants#add_or_rm_role'
  end
  resources :events

Now the correct action is called but an error is thrown before it can finish: NameError (uninitialized constant Participant):

UPDATE

NameError (uninitialized constant Participant): was being thrown because I was attempting to authorize (via CanCanCan) a non-resource at the top of Events::ParticipantsController with load_and_authorize_resource. Since the model is namespaced I need to specify the resource model manually: load_and_authorize_resource class: "Event::Participant"

UPDATE

After fixing all of the above, I recieved the error NoMethodError (undefined method 'event_id' for #<ActionController::Parameters:0x007f791dcbe240>):, which I thought has something to do with the strong params, but ended up being caused by using dot notation: event_participants_params.event_id is broken but event_participants_params[:event_id] works.

1

1 Answers

1
votes

Look at your routes

PUT    /events/:id/add_or_rm_role(.:format) events/participants#add_or_rm_role #This one looks good!

events/participants#add_or_rm_role translates to an action called add_or_rm_role on a namespaced controller called Events::ParticipantsController, but this controller doesn't exist.

You do have a ParticipantsController::EventsController, but I don't think that's what you're intending either. That translates to an EventsController class nested inside of a ParticipantsController module. I would rename that simply to ParticipantsController.

It also looks like you're trying to send an id: @event.id to the controller, but the controller is expecting a param called event_id.

I think you're looking to nest this path, not namespace it. I think this change to your routing will get you on the right track

Rails.application.routes.draw do
  resources :events do
    put '/add_or_rm_role', as: :add_or_rm_role, to: 'participants#add_or_rm_role'
  end
end

It will generate this route

event_add_or_rm_role PUT    /events/:event_id/add_or_rm_role(.:format) participants#add_or_rm_role

You'll now have a route helper method event_add_or_rm_role with an event_id param in the URL, sending the put request to ParticipantsController#add_or_rm_role. Hope this helps