使用匿名控制器

使用 controller 方法来定义一个匿名控制器,它将继承自描述的类。这对于指定像全局错误处理这样的行为很有用。

要指定不同的基类,可以将类显式地传递给 controller 方法

  controller(BaseController)

您也可以禁用基类型推断,在这种情况下,匿名控制器默认将继承自 ApplicationController 而不是描述的类。

  RSpec.configure do |c|
    c.infer_base_class_for_anonymous_controllers = false
  end

  RSpec.describe BaseController, type: :controller do
    controller do
      def index; end

      # this normally creates an anonymous `BaseController` subclass,
      # however since `infer_base_class_for_anonymous_controllers` is
      # disabled, it creates a subclass of `ApplicationController`
    end
  end

使用重定向在 ApplicationController 中指定错误处理

假设有一个名为“spec/controllers/application_controller_spec.rb”的文件,内容如下:

require "rails_helper"

class ApplicationController < ActionController::Base
  class AccessDenied < StandardError; end

  rescue_from AccessDenied, :with => :access_denied

private

  def access_denied
    redirect_to "/401.html"
  end
end

RSpec.describe ApplicationController, type: :controller do
  controller do
    def index
      raise ApplicationController::AccessDenied
    end
  end

  describe "handling AccessDenied exceptions" do
    it "redirects to the /401.html page" do
      get :index
      expect(response).to redirect_to("/401.html")
    end
  end
end

我运行 rspec spec

那么这些示例应该全部通过。

使用渲染在 ApplicationController 中指定错误处理

假设有一个名为“spec/controllers/application_controller_spec.rb”的文件,内容如下:

require "rails_helper"

class ApplicationController < ActionController::Base
  class AccessDenied < StandardError; end

  rescue_from AccessDenied, :with => :access_denied

private

  def access_denied
    render "errors/401"
  end
end

RSpec.describe ApplicationController, type: :controller do
  controller do
    def index
      raise ApplicationController::AccessDenied
    end
  end

  describe "handling AccessDenied exceptions" do
    it "renders the errors/401 template" do
      get :index
      expect(response).to render_template("errors/401")
    end
  end
end

我运行 rspec spec

那么这些示例应该全部通过。

在子类中指定错误处理

假设有一个名为“spec/controllers/application_controller_subclass_spec.rb”的文件,内容如下:

require "rails_helper"

class ApplicationController < ActionController::Base
  class AccessDenied < StandardError; end
end

class FoosController < ApplicationController

  rescue_from ApplicationController::AccessDenied,
              :with => :access_denied

private

  def access_denied
    redirect_to "/401.html"
  end
end

RSpec.describe FoosController, type: :controller do
  controller(FoosController) do
    def index
      raise ApplicationController::AccessDenied
    end
  end

  describe "handling AccessDenied exceptions" do
    it "redirects to the /401.html page" do
      get :index
      expect(response).to redirect_to("/401.html")
    end
  end
end

我运行 rspec spec

那么这些示例应该全部通过。

从描述的类中推断基类

假设有一个名为“spec/controllers/base_class_can_be_inferred_spec.rb”的文件,内容如下:

require "rails_helper"

class ApplicationController < ActionController::Base; end

class FoosController < ApplicationController; end

RSpec.describe FoosController, type: :controller do
  controller do
    def index
      render :plain => "Hello World"
    end
  end

  it "creates anonymous controller derived from FoosController" do
    expect(controller).to be_a_kind_of(FoosController)
  end
end

我运行 rspec spec

那么这些示例应该全部通过。

从描述的类中使用 namecontroller_name

假设有一个名为“spec/controllers/get_name_and_controller_name_from_described_class_spec.rb”的文件,内容如下:

require "rails_helper"

class ApplicationController < ActionController::Base; end
class FoosController < ApplicationController; end

RSpec.describe "Access controller names", type: :controller do
  controller FoosController do
    def index
      @name = self.class.name
      @controller_name = controller_name
      render :plain => "Hello World"
    end
  end

  before do
    get :index
  end

  it "gets the class name as described" do
    expect(assigns[:name]).to eq('FoosController')
  end

  it "gets the controller_name as described" do
    expect(assigns[:controller_name]).to eq('foos')
  end
end

我运行 rspec spec

那么这些示例应该全部通过。

在基类中调用 around_filteraround_action

假设有一个名为“spec/controllers/application_controller_around_filter_spec.rb”的文件,内容如下:

require "rails_helper"

class ApplicationController < ActionController::Base
  around_action :an_around_filter

  def an_around_filter
    @callback_invoked = true
    yield
  end
end

RSpec.describe ApplicationController, type: :controller do
  controller do
    def index
      render :plain => ""
    end
  end

  it "invokes the callback" do
    get :index

    expect(assigns[:callback_invoked]).to be_truthy
  end
end

我运行 rspec spec

那么这些示例应该全部通过。

匿名控制器只创建资源路由

假设有一个名为“spec/controllers/application_controller_spec.rb”的文件,内容如下:

require "rails_helper"

if defined?(ActionController::UrlGenerationError)
  ExpectedRoutingError = ActionController::UrlGenerationError
else
  ExpectedRoutingError = ActionController::RoutingError
end

RSpec.describe ApplicationController, type: :controller do
  controller do
    def index
      render :plain => "index called"
    end

    def create
      render :plain => "create called"
    end

    def new
      render :plain => "new called"
    end

    def show
      render :plain => "show called"
    end

    def edit
      render :plain => "edit called"
    end

    def update
      render :plain => "update called"
    end

    def destroy
      render :plain => "destroy called"
    end

    def willerror
      render :plain => "will not render"
    end
  end

  describe "#index" do
    it "responds to GET" do
      get :index
      expect(response.body).to eq "index called"
    end

    it "also responds to POST" do
      post :index
      expect(response.body).to eq "index called"
    end

    it "also responds to PUT" do
      put :index
      expect(response.body).to eq "index called"
    end

    it "also responds to DELETE" do
      delete :index
      expect(response.body).to eq "index called"
    end
  end

  describe "#create" do
    it "responds to POST" do
      post :create
      expect(response.body).to eq "create called"
    end

    # And the rest...
    %w{get post put delete}.each do |calltype|
      it "responds to #{calltype}" do
        send(calltype, :create)
        expect(response.body).to eq "create called"
      end
    end
  end

  describe "#new" do
    it "responds to GET" do
      get :new
      expect(response.body).to eq "new called"
    end

    # And the rest...
    %w{get post put delete}.each do |calltype|
      it "responds to #{calltype}" do
        send(calltype, :new)
        expect(response.body).to eq "new called"
      end
    end
  end

  describe "#edit" do
    it "responds to GET" do
      get :edit, :params => { :id => "anyid" }
      expect(response.body).to eq "edit called"
    end

    it "requires the :id parameter" do
      expect { get :edit }.to raise_error(ExpectedRoutingError)
    end

    # And the rest...
    %w{get post put delete}.each do |calltype|
      it "responds to #{calltype}" do
        send(calltype, :edit, :params => {:id => "anyid"})
        expect(response.body).to eq "edit called"
      end
    end
  end

  describe "#show" do
    it "responds to GET" do
      get :show, :params => { :id => "anyid" }
      expect(response.body).to eq "show called"
    end

    it "requires the :id parameter" do
      expect { get :show }.to raise_error(ExpectedRoutingError)
    end

    # And the rest...
    %w{get post put delete}.each do |calltype|
      it "responds to #{calltype}" do
        send(calltype, :show, :params => {:id => "anyid"})
        expect(response.body).to eq "show called"
      end
    end
  end

  describe "#update" do
    it "responds to PUT" do
      put :update, :params => { :id => "anyid" }
      expect(response.body).to eq "update called"
    end

    it "requires the :id parameter" do
      expect { put :update }.to raise_error(ExpectedRoutingError)
    end

    # And the rest...
    %w{get post put delete}.each do |calltype|
      it "responds to #{calltype}" do
        send(calltype, :update, :params =>  {:id => "anyid"})
        expect(response.body).to eq "update called"
      end
    end
  end

  describe "#destroy" do
    it "responds to DELETE" do
      delete :destroy, :params => { :id => "anyid" }
      expect(response.body).to eq "destroy called"
    end

    it "requires the :id parameter" do
      expect { delete :destroy }.to raise_error(ExpectedRoutingError)
    end

    # And the rest...
    %w{get post put delete}.each do |calltype|
      it "responds to #{calltype}" do
        send(calltype, :destroy, :params => {:id => "anyid"})
        expect(response.body).to eq "destroy called"
      end
    end
  end

  describe "#willerror" do
    it "cannot be called" do
      expect { get :willerror }.to raise_error(ExpectedRoutingError)
    end
  end
end

我运行 rspec spec

那么这些示例应该全部通过。

为匿名控制器绘制自定义路由

假设有一个名为“spec/controllers/application_controller_spec.rb”的文件,内容如下:

require "rails_helper"

RSpec.describe ApplicationController, type: :controller do
  controller do
    def custom
      render :plain => "custom called"
    end
  end

  specify "manually draw the route to request a custom action" do
    routes.draw { get "custom" => "anonymous#custom" }

    get :custom
    expect(response.body).to eq "custom called"
  end
end

我运行 rspec spec

那么这些示例应该全部通过。

为不继承自应用程序控制器的匿名控制器绘制自定义路由

假设有一个名为“spec/controllers/other_controller_spec.rb”的文件,内容如下:

require "rails_helper"
class OtherController < ActionController::Base
end

RSpec.describe OtherController, type: :controller do
  controller do
    def custom
      render :plain => "custom called"
    end
  end

  specify "manually draw the route to request a custom action" do
    routes.draw { get "custom" => "other#custom" }

    get :custom
    expect(response.body).to eq "custom called"
  end
end

我运行 rspec spec

那么这些示例应该全部通过。

为定义的控制器绘制自定义路由

假设有一个名为“spec/controllers/application_controller_spec.rb”的文件,内容如下:

require "rails_helper"

class FoosController < ApplicationController; end

RSpec.describe ApplicationController, type: :controller do
  controller FoosController do
    def custom
      render :plain => "custom called"
    end
  end

  specify "manually draw the route to request a custom action" do
    routes.draw { get "custom" => "foos#custom" }

    get :custom
    expect(response.body).to eq "custom called"
  end
end

我运行 rspec spec

那么这些示例应该全部通过。

与命名空间控制器一起工作

假设有一个名为“spec/controllers/namespaced_controller_spec.rb”的文件,内容如下:

require "rails_helper"

class ApplicationController < ActionController::Base; end

module Outer
  module Inner
    class FoosController < ApplicationController; end
  end
end

RSpec.describe Outer::Inner::FoosController, type: :controller do
  controller do
    def index
      @name = self.class.name
      @controller_name = controller_name
      render :plain => "Hello World"
    end
  end

  it "creates anonymous controller derived from the namespace" do
    expect(controller).to be_a_kind_of(Outer::Inner::FoosController)
  end

  it "gets the class name as described" do
    expect{ get :index }.to change{
      assigns[:name]
    }.to eq('Outer::Inner::FoosController')
  end

  it "gets the controller_name as described" do
    expect{ get :index }.to change{
      assigns[:controller_name]
    }.to eq('foos')
  end
end

我运行 rspec spec

那么这些示例应该全部通过。

在测试的控制器中引用应用程序路由

假设有一个名为“spec/controllers/application_controller_spec.rb”的文件,内容如下:

require "rails_helper"

Rails.application.routes.draw do
  match "/login" => "sessions#new", :as => "login", :via => "get"
end

RSpec.describe ApplicationController, type: :controller do
  controller do
    def index
      redirect_to login_url
    end
  end

  it "redirects to the login page" do
    get :index
    expect(response).to redirect_to("/login")
  end
end

我运行 rspec spec

那么这些示例应该全部通过。