mardi 25 août 2015

Multiple devise_for routes for the same rails resource

I am working on an existing web application that uses Rails 3 and Devise. My task at the moment is to add a JSON API to the application. I have created a Mobile::SessionsController (< Devise::SessionsController) that implements '#create' with a data-only response.

The issue is in the routing. Right now, the relevant parts of routes.rb are as follows:

 # For the web app:
  devise_for :users, :controllers => {:sessions => 'sessions',
                                      :registrations => 'registrations'}
  as :user do
    get "/login" => "devise/sessions#new"
    get "/logout" => "devise/sessions#destroy", :as => :logout
    post '/signup' => 'registrations#signup', :as => :signup
  end
  # And for the API, later on:
  namespace :api do
    namespace :user do
      devise_for :users, :controllers => {:sessions => 'mobile/sessions'},
                 skip: :all
      devise_scope :user do
        post 'login' => 'mobile/sessions#create'
      end
...

The data endpoint is at /api/user/login. rake routes lists the following:

api_user_login POST /api/user/login(.:format) api/user/mobile/sessions#create

In my spec, there's the following:

describe Mobile::SessionsController do
  render_views
  describe '#create' do
    context 'for some condition' do
      let(:params) do
        {
          # basic params
        }
      end
      before do
        post :create, params.merge(format: :json)
      end

      it 'should render a json response' do
        expect(response.headers['Content-Type']).to eq 'application/json; charset=utf-8'
      end
...

When I run the spec, it gives the following error:

AbstractController::ActionNotFound: Could not find devise mapping for path "/mobile/sessions/create.json?params=blah.
This may happen for two reasons:

1) You forgot to wrap your route inside the scope block. For example:

  devise_scope :user do
    get "/some/route" => "some_devise_controller"
  end

2) You are testing a Devise controller bypassing the router.
   If so, you can explicitly tell Devise which mapping to use:

   @request.env["devise.mapping"] = Devise.mappings[:user]

I've tried many different configurations within the second devise_for block (modules, whether or not to use skip: :all, removing the second devise_for entirely, putting it at different levels of the namespace) and I can't shake this error. My guess is that the second option there (the devise.mapping bit) is not the solution as it seems like it's not supposed to be bypassing the router at all. And since I have the route in precisely the sort of block it suggests for the first option, I'm befuddled as to how to fix this thing. How can I get this thing working?

Appreciate the help!

Aucun commentaire:

Enregistrer un commentaire