Search code examples
ruby-on-railsrubyrspecrspec-rails

Routes nested resources and redirect_to on controllers


I have a controller that when creating and editing redirects to the show page. I'm using nested atrribututes and I can not use the redirect to the path of the show. My controller is this:

class SalesmenController < ApplicationController

  def show
    authorize! :read, @salesman
  end

  def create
    @salesman = Salesman.new(params_salesman)
    authorize! :create, @salesman
    if @salesman.save
      redirect_to company_salesman_path(@salesman.id)
      flash[:notice] = "Salesman saved!"
    else
      flash.now[:error] = "Could not create salesman!"
      render :new
    end
  end


  private

  def params_salesman
    params.require(:salesman).permit(:name, :company_id)
  end
end

My routes are:

Rails.application.routes.draw do
  resources :companies do
    resources :salesmen
    resources :goals do
      resources :days
    end
  end
  devise_for :owners, :controllers => { registrations: 'registrations' }
end

I am doing a test with rspec and I get the following error message:

 1) SalesmenController POST #create redirect to new team
     Failure/Error: redirect_to company_salesman_path(@salesman.id)

     ActionController::UrlGenerationError:
       No route matches {:action=>"show", :company_id=>#<Salesman id: 310, name: "Raymond Kihn", company_id: 831, created_at: "2017-10-29 03:20:33", updated_at: "2017-10-29 03:20:33">, :controller=>"salesmen"} missing required keys: [:id]

My test is this:

require 'rails_helper'

RSpec.describe SalesmenController, type: :controller do
   include Devise::Test::ControllerHelpers

   before(:each) do
    @request.env["devise.mapping"] = Devise.mappings[:owner]
    @current_owner = FactoryGirl.create(:owner)
    sign_in @current_owner
    @current_company = FactoryGirl.create(:company, owner: @current_owner)
  end

  describe "POST #create" do
    before(:each) do
      salesman = FactoryGirl.create(:salesman, company: @current_company)
      post :create, params: {:company_id => @current_company.id, salesman: { name: salesman.name, company_id: @current_company.id } }
    end

    it "redirect to new team" do
      expect(response).to have_http_status(:success)
    end

    it "Create team with right attributes" do
      expect(Salesman.last.company).to eql(@current_company)
      expect(Salesman.last.name).to eql(@salesman[:name])
    end
  end
end

Could anyone help?


Solution

  • You need to return the redirect, not the flash message. In Ruby, the last line to execute in the method is what gets returned. You had a flash message and thus what returned fro the create action was not a redirect call.

    Fix it and you should be good!

    class SalesmenController < ApplicationController
      # def index...
    
      def create
        @salesman = Salesman.new(params_salesman)
        authorize! :create, @salesman
        if @salesman.save
          flash[:notice] = "Salesman saved!"
          redirect_to company_salesman_path(current_company, @salesman)  # This has to executed last for the redirect
        else
          flash.now[:error] = "Could not create salesman!"
          render :new
        end
    
        # ....
      end
    end
    

    The specs need to be updated to reflect the redirection. Success is for rendering pages.

    it "redirect to new team" do
      expect(response).to redirect_to(company_salesman_path(company, salesman))
    end
    

    BTW you don't need to create the record in FG. You can do attributes_for(:salesman) which you can directly use in the post.

    Happy hacking!