filters_test.rb 27.6 KB
Newer Older
1
require 'abstract_unit'
D
Initial  
David Heinemeier Hansson 已提交
2

3
# FIXME: crashes Ruby 1.9
D
Initial  
David Heinemeier Hansson 已提交
4 5 6
class FilterTest < Test::Unit::TestCase
  class TestController < ActionController::Base
    before_filter :ensure_login
7
    after_filter  :clean_up
D
Initial  
David Heinemeier Hansson 已提交
8 9

    def show
10
      render :inline => "ran action"
D
Initial  
David Heinemeier Hansson 已提交
11 12 13 14 15 16 17
    end

    private
      def ensure_login
        @ran_filter ||= []
        @ran_filter << "ensure_login"
      end
18

19 20 21 22
      def clean_up
        @ran_after_filter ||= []
        @ran_after_filter << "clean_up"
      end
D
Initial  
David Heinemeier Hansson 已提交
23
  end
24 25 26 27 28 29 30 31 32

  class ChangingTheRequirementsController < TestController
    before_filter :ensure_login, :except => [:go_wild]

    def go_wild
      render :text => "gobble"
    end
  end

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
  class TestMultipleFiltersController < ActionController::Base
    before_filter :try_1
    before_filter :try_2
    before_filter :try_3

    (1..3).each do |i|
      define_method "fail_#{i}" do
        render :text => i.to_s
      end
    end

    protected
    (1..3).each do |i|
      define_method "try_#{i}" do
        instance_variable_set :@try, i
48 49 50
        if action_name == "fail_#{i}"
          head(404)
        end
51 52 53
      end
    end
  end
54 55 56 57 58 59

  class RenderingController < ActionController::Base
    before_filter :render_something_else

    def show
      @ran_action = true
60
      render :inline => "ran action"
61 62 63 64
    end

    private
      def render_something_else
65
        render :inline => "something else"
66 67
      end
  end
68

69 70
  class ConditionalFilterController < ActionController::Base
    def show
71
      render :inline => "ran action"
72 73 74
    end

    def another_action
75
      render :inline => "ran action"
76 77 78
    end

    def show_without_filter
79
      render :inline => "ran action without filter"
80 81 82 83 84 85 86 87 88 89 90 91
    end

    private
      def ensure_login
        @ran_filter ||= []
        @ran_filter << "ensure_login"
      end

      def clean_up_tmp
        @ran_filter ||= []
        @ran_filter << "clean_up_tmp"
      end
92

93 94 95 96 97 98 99
      def rescue_action(e) raise(e) end
  end

  class ConditionalCollectionFilterController < ConditionalFilterController
    before_filter :ensure_login, :except => [ :show_without_filter, :another_action ]
  end

100
  class OnlyConditionSymController < ConditionalFilterController
101 102 103 104 105 106 107 108 109
    before_filter :ensure_login, :only => :show
  end

  class ExceptConditionSymController < ConditionalFilterController
    before_filter :ensure_login, :except => :show_without_filter
  end

  class BeforeAndAfterConditionController < ConditionalFilterController
    before_filter :ensure_login, :only => :show
110
    after_filter  :clean_up_tmp, :only => :show
111
  end
112 113

  class OnlyConditionProcController < ConditionalFilterController
114
    before_filter(:only => :show) {|c| c.instance_variable_set(:"@ran_proc_filter", true) }
115 116 117
  end

  class ExceptConditionProcController < ConditionalFilterController
118
    before_filter(:except => :show_without_filter) {|c| c.instance_variable_set(:"@ran_proc_filter", true) }
119 120 121
  end

  class ConditionalClassFilter
122
    def self.filter(controller) controller.instance_variable_set(:"@ran_class_filter", true) end
123 124 125 126 127 128 129 130 131 132 133
  end

  class OnlyConditionClassController < ConditionalFilterController
    before_filter ConditionalClassFilter, :only => :show
  end

  class ExceptConditionClassController < ConditionalFilterController
    before_filter ConditionalClassFilter, :except => :show_without_filter
  end

  class AnomolousYetValidConditionController < ConditionalFilterController
134
    before_filter(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.instance_variable_set(:"@ran_proc_filter1", true)}, :except => :show_without_filter) { |c| c.instance_variable_set(:"@ran_proc_filter2", true)}
135 136
  end

137 138 139 140 141
  class ConditionalOptionsFilter < ConditionalFilterController
    before_filter :ensure_login, :if => Proc.new { |c| true }
    before_filter :clean_up_tmp, :if => Proc.new { |c| false }
  end

142 143 144 145 146 147 148 149
  class EmptyFilterChainController < TestController
    self.filter_chain.clear
    def show
      @action_executed = true
      render :text => "yawp!"
    end
  end

D
Initial  
David Heinemeier Hansson 已提交
150 151
  class PrependingController < TestController
    prepend_before_filter :wonderful_life
152
    # skip_before_filter :fire_flash
D
Initial  
David Heinemeier Hansson 已提交
153 154 155 156 157 158 159 160

    private
      def wonderful_life
        @ran_filter ||= []
        @ran_filter << "wonderful_life"
      end
  end

161 162 163 164 165 166 167
  class SkippingAndLimitedController < TestController
    skip_before_filter :ensure_login
    before_filter :ensure_login, :only => :index

    def index
      render :text => 'ok'
    end
168

169 170 171
    def public
    end
  end
172

173 174 175 176 177 178 179 180 181 182 183 184
  class SkippingAndReorderingController < TestController
    skip_before_filter :ensure_login
    before_filter :find_record
    before_filter :ensure_login

    private
      def find_record
        @ran_filter ||= []
        @ran_filter << "find_record"
      end
  end

185 186 187
  class ConditionalSkippingController < TestController
    skip_before_filter :ensure_login, :only => [ :login ]
    skip_after_filter  :clean_up,     :only => [ :login ]
188

189
    before_filter :find_user, :only => [ :change_password ]
190 191 192 193 194 195 196 197

    def login
      render :inline => "ran action"
    end

    def change_password
      render :inline => "ran action"
    end
198

199 200 201 202 203
    protected
      def find_user
        @ran_filter ||= []
        @ran_filter << "find_user"
      end
204
  end
205

206 207 208
  class ConditionalParentOfConditionalSkippingController < ConditionalFilterController
    before_filter :conditional_in_parent, :only => [:show, :another_action]
    after_filter  :conditional_in_parent, :only => [:show, :another_action]
209

210
    private
211

212 213 214 215 216
      def conditional_in_parent
        @ran_filter ||= []
        @ran_filter << 'conditional_in_parent'
      end
  end
217

218 219 220 221
  class ChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController
    skip_before_filter :conditional_in_parent, :only => :another_action
    skip_after_filter  :conditional_in_parent, :only => :another_action
  end
222

223 224 225 226
  class AnotherChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController
    skip_before_filter :conditional_in_parent, :only => :show
  end

D
Initial  
David Heinemeier Hansson 已提交
227
  class ProcController < PrependingController
228
    before_filter(proc { |c| c.instance_variable_set(:"@ran_proc_filter", true) })
D
Initial  
David Heinemeier Hansson 已提交
229 230 231
  end

  class ImplicitProcController < PrependingController
232
    before_filter { |c| c.instance_variable_set(:"@ran_proc_filter", true) }
D
Initial  
David Heinemeier Hansson 已提交
233 234 235 236
  end

  class AuditFilter
    def self.filter(controller)
237
      controller.instance_variable_set(:"@was_audited", true)
D
Initial  
David Heinemeier Hansson 已提交
238 239
    end
  end
240

D
Initial  
David Heinemeier Hansson 已提交
241 242 243 244
  class AroundFilter
    def before(controller)
      @execution_log = "before"
      controller.class.execution_log << " before aroundfilter " if controller.respond_to? :execution_log
245
      controller.instance_variable_set(:"@before_ran", true)
D
Initial  
David Heinemeier Hansson 已提交
246 247 248
    end

    def after(controller)
249 250
      controller.instance_variable_set(:"@execution_log", @execution_log + " and after")
      controller.instance_variable_set(:"@after_ran", true)
D
Initial  
David Heinemeier Hansson 已提交
251
      controller.class.execution_log << " after aroundfilter " if controller.respond_to? :execution_log
252
    end
D
Initial  
David Heinemeier Hansson 已提交
253 254 255 256 257 258 259 260 261
  end

  class AppendedAroundFilter
    def before(controller)
      controller.class.execution_log << " before appended aroundfilter "
    end

    def after(controller)
      controller.class.execution_log << " after appended aroundfilter "
262 263 264
    end
  end

D
Initial  
David Heinemeier Hansson 已提交
265 266
  class AuditController < ActionController::Base
    before_filter(AuditFilter)
267

D
Initial  
David Heinemeier Hansson 已提交
268
    def show
269
      render :text => "hello"
D
Initial  
David Heinemeier Hansson 已提交
270 271 272 273 274 275 276
    end
  end

  class AroundFilterController < PrependingController
    around_filter AroundFilter.new
  end

277 278 279 280 281 282 283 284
  class BeforeAfterClassFilterController < PrependingController
    begin
      filter = AroundFilter.new
      before_filter filter
      after_filter filter
    end
  end

D
Initial  
David Heinemeier Hansson 已提交
285 286
  class MixedFilterController < PrependingController
    cattr_accessor :execution_log
287 288

    def initialize
D
Initial  
David Heinemeier Hansson 已提交
289 290 291 292 293 294 295 296 297
      @@execution_log = ""
    end

    before_filter { |c| c.class.execution_log << " before procfilter "  }
    prepend_around_filter AroundFilter.new

    after_filter  { |c| c.class.execution_log << " after procfilter " }
    append_around_filter AppendedAroundFilter.new
  end
298

299 300 301 302 303 304 305
  class MixedSpecializationController < ActionController::Base
    class OutOfOrder < StandardError; end

    before_filter :first
    before_filter :second, :only => :foo

    def foo
306
      render :text => 'foo'
307 308 309
    end

    def bar
310
      render :text => 'bar'
311 312 313 314 315 316 317 318 319 320 321 322
    end

    protected
      def first
        @first = true
      end

      def second
        raise OutOfOrder unless @first
      end
  end

323 324 325 326 327 328 329 330 331 332 333 334
  class DynamicDispatchController < ActionController::Base
    before_filter :choose

    %w(foo bar baz).each do |action|
      define_method(action) { render :text => action }
    end

    private
      def choose
        self.action_name = params[:choose]
      end
  end
D
Initial  
David Heinemeier Hansson 已提交
335

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
  class PrependingBeforeAndAfterController < ActionController::Base
    prepend_before_filter :before_all
    prepend_after_filter :after_all
    before_filter :between_before_all_and_after_all

    def before_all
      @ran_filter ||= []
      @ran_filter << 'before_all'
    end

    def after_all
      @ran_filter ||= []
      @ran_filter << 'after_all'
    end

    def between_before_all_and_after_all
      @ran_filter ||= []
      @ran_filter << 'between_before_all_and_after_all'
    end
    def show
      render :text => 'hello'
    end
  end
359

360
  class ErrorToRescue < Exception; end
361

362 363 364 365 366
  class RescuingAroundFilterWithBlock
    def filter(controller)
      begin
        yield
      rescue ErrorToRescue => ex
367
        controller.__send__ :render, :text => "I rescued this: #{ex.inspect}"
368 369 370
      end
    end
  end
371

372 373
  class RescuedController < ActionController::Base
    around_filter RescuingAroundFilterWithBlock.new
374

375 376 377
    def show
      raise ErrorToRescue.new("Something made the bad noise.")
    end
378

379 380 381 382 383
  private
    def rescue_action(exception)
      raise exception
    end
  end
384

385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
  class NonYieldingAroundFilterController < ActionController::Base

    before_filter :filter_one
    around_filter :non_yielding_filter
    before_filter :filter_two
    after_filter :filter_three

    def index
      render :inline => "index"
    end

    #make sure the controller complains
    def rescue_action(e); raise e; end

    private

      def filter_one
        @filters  ||= []
        @filters  << "filter_one"
      end

      def filter_two
        @filters  << "filter_two"
      end

      def non_yielding_filter
        @filters  << "zomg it didn't yield"
        @filter_return_value
      end

      def filter_three
        @filters  << "filter_three"
      end

  end

  def test_non_yielding_around_filters_not_returning_false_do_not_raise
    controller = NonYieldingAroundFilterController.new
    controller.instance_variable_set "@filter_return_value", true
    assert_nothing_raised do
      test_process(controller, "index")
    end
  end

  def test_non_yielding_around_filters_returning_false_do_not_raise
    controller = NonYieldingAroundFilterController.new
    controller.instance_variable_set "@filter_return_value", false
    assert_nothing_raised do
      test_process(controller, "index")
    end
  end

  def test_after_filters_are_not_run_if_around_filter_returns_false
    controller = NonYieldingAroundFilterController.new
    controller.instance_variable_set "@filter_return_value", false
    test_process(controller, "index")
    assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters']
  end

  def test_after_filters_are_not_run_if_around_filter_does_not_yield
    controller = NonYieldingAroundFilterController.new
    controller.instance_variable_set "@filter_return_value", true
    test_process(controller, "index")
    assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters']
  end

451 452
  def test_empty_filter_chain
    assert_equal 0, EmptyFilterChainController.filter_chain.size
453 454
    test_process(EmptyFilterChainController)
    assert @controller.template.assigns['action_executed']
455 456
  end

D
Initial  
David Heinemeier Hansson 已提交
457
  def test_added_filter_to_inheritance_graph
458
    assert_equal [ :ensure_login ], TestController.before_filters
D
Initial  
David Heinemeier Hansson 已提交
459 460 461
  end

  def test_base_class_in_isolation
462
    assert_equal [ ], ActionController::Base.before_filters
D
Initial  
David Heinemeier Hansson 已提交
463
  end
464

D
Initial  
David Heinemeier Hansson 已提交
465
  def test_prepending_filter
466
    assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_filters
D
Initial  
David Heinemeier Hansson 已提交
467
  end
468

D
Initial  
David Heinemeier Hansson 已提交
469
  def test_running_filters
470 471
    test_process(PrependingController)
    assert_equal %w( wonderful_life ensure_login ), @controller.template.assigns["ran_filter"]
D
Initial  
David Heinemeier Hansson 已提交
472 473 474
  end

  def test_running_filters_with_proc
475 476
    test_process(ProcController)
    assert @controller.template.assigns["ran_proc_filter"]
D
Initial  
David Heinemeier Hansson 已提交
477
  end
478

D
Initial  
David Heinemeier Hansson 已提交
479
  def test_running_filters_with_implicit_proc
480 481
    test_process(ImplicitProcController)
    assert @controller.template.assigns["ran_proc_filter"]
D
Initial  
David Heinemeier Hansson 已提交
482
  end
483

D
Initial  
David Heinemeier Hansson 已提交
484
  def test_running_filters_with_class
485 486
    test_process(AuditController)
    assert @controller.template.assigns["was_audited"]
D
Initial  
David Heinemeier Hansson 已提交
487
  end
488

489
  def test_running_anomolous_yet_valid_condition_filters
490 491 492 493 494
    test_process(AnomolousYetValidConditionController)
    assert_equal %w( ensure_login ), @controller.template.assigns["ran_filter"]
    assert @controller.template.assigns["ran_class_filter"]
    assert @controller.template.assigns["ran_proc_filter1"]
    assert @controller.template.assigns["ran_proc_filter2"]
495

496 497 498 499 500
    test_process(AnomolousYetValidConditionController, "show_without_filter")
    assert_equal nil, @controller.template.assigns["ran_filter"]
    assert !@controller.template.assigns["ran_class_filter"]
    assert !@controller.template.assigns["ran_proc_filter1"]
    assert !@controller.template.assigns["ran_proc_filter2"]
501 502
  end

503
  def test_running_conditional_options
504 505
    test_process(ConditionalOptionsFilter)
    assert_equal %w( ensure_login ), @controller.template.assigns["ran_filter"]
506 507
  end

508
  def test_running_collection_condition_filters
509 510 511 512 513 514
    test_process(ConditionalCollectionFilterController)
    assert_equal %w( ensure_login ), @controller.template.assigns["ran_filter"]
    test_process(ConditionalCollectionFilterController, "show_without_filter")
    assert_equal nil, @controller.template.assigns["ran_filter"]
    test_process(ConditionalCollectionFilterController, "another_action")
    assert_equal nil, @controller.template.assigns["ran_filter"]
515 516 517
  end

  def test_running_only_condition_filters
518 519 520 521
    test_process(OnlyConditionSymController)
    assert_equal %w( ensure_login ), @controller.template.assigns["ran_filter"]
    test_process(OnlyConditionSymController, "show_without_filter")
    assert_equal nil, @controller.template.assigns["ran_filter"]
522

523 524 525 526
    test_process(OnlyConditionProcController)
    assert @controller.template.assigns["ran_proc_filter"]
    test_process(OnlyConditionProcController, "show_without_filter")
    assert !@controller.template.assigns["ran_proc_filter"]
527

528 529 530 531
    test_process(OnlyConditionClassController)
    assert @controller.template.assigns["ran_class_filter"]
    test_process(OnlyConditionClassController, "show_without_filter")
    assert !@controller.template.assigns["ran_class_filter"]
532 533 534
  end

  def test_running_except_condition_filters
535 536 537 538
    test_process(ExceptConditionSymController)
    assert_equal %w( ensure_login ), @controller.template.assigns["ran_filter"]
    test_process(ExceptConditionSymController, "show_without_filter")
    assert_equal nil, @controller.template.assigns["ran_filter"]
539

540 541 542 543
    test_process(ExceptConditionProcController)
    assert @controller.template.assigns["ran_proc_filter"]
    test_process(ExceptConditionProcController, "show_without_filter")
    assert !@controller.template.assigns["ran_proc_filter"]
544

545 546 547 548
    test_process(ExceptConditionClassController)
    assert @controller.template.assigns["ran_class_filter"]
    test_process(ExceptConditionClassController, "show_without_filter")
    assert !@controller.template.assigns["ran_class_filter"]
549 550 551
  end

  def test_running_before_and_after_condition_filters
552 553 554 555
    test_process(BeforeAndAfterConditionController)
    assert_equal %w( ensure_login clean_up_tmp), @controller.template.assigns["ran_filter"]
    test_process(BeforeAndAfterConditionController, "show_without_filter")
    assert_equal nil, @controller.template.assigns["ran_filter"]
556
  end
557

D
Initial  
David Heinemeier Hansson 已提交
558
  def test_around_filter
559 560 561
    test_process(AroundFilterController)
    assert @controller.template.assigns["before_ran"]
    assert @controller.template.assigns["after_ran"]
D
Initial  
David Heinemeier Hansson 已提交
562
  end
563

564
  def test_before_after_class_filter
565 566 567
    test_process(BeforeAfterClassFilterController)
    assert @controller.template.assigns["before_ran"]
    assert @controller.template.assigns["after_ran"]
568 569
  end

D
Initial  
David Heinemeier Hansson 已提交
570
  def test_having_properties_in_around_filter
571 572
    test_process(AroundFilterController)
    assert_equal "before and after", @controller.template.assigns["execution_log"]
D
Initial  
David Heinemeier Hansson 已提交
573 574 575 576 577
  end

  def test_prepending_and_appending_around_filter
    controller = test_process(MixedFilterController)
    assert_equal " before aroundfilter  before procfilter  before appended aroundfilter " +
578
                 " after appended aroundfilter  after aroundfilter  after procfilter ",
D
Initial  
David Heinemeier Hansson 已提交
579 580
                 MixedFilterController.execution_log
  end
581

582 583 584
  def test_rendering_breaks_filtering_chain
    response = test_process(RenderingController)
    assert_equal "something else", response.body
585
    assert !@controller.template.assigns["ran_action"]
586
  end
D
Initial  
David Heinemeier Hansson 已提交
587

588 589 590 591 592 593 594 595 596 597 598 599
  def test_filters_with_mixed_specialization_run_in_order
    assert_nothing_raised do
      response = test_process(MixedSpecializationController, 'bar')
      assert_equal 'bar', response.body
    end

    assert_nothing_raised do
      response = test_process(MixedSpecializationController, 'foo')
      assert_equal 'foo', response.body
    end
  end

600 601 602 603 604 605 606 607 608
  def test_dynamic_dispatch
    %w(foo bar baz).each do |action|
      request = ActionController::TestRequest.new
      request.query_parameters[:choose] = action
      response = DynamicDispatchController.process(request, ActionController::TestResponse.new)
      assert_equal action, response.body
    end
  end

609 610
  def test_running_prepended_before_and_after_filter
    assert_equal 3, PrependingBeforeAndAfterController.filter_chain.length
611 612
    test_process(PrependingBeforeAndAfterController)
    assert_equal %w( before_all between_before_all_and_after_all after_all ), @controller.template.assigns["ran_filter"]
613
  end
614

615
  def test_skipping_and_limiting_controller
616 617 618 619
    test_process(SkippingAndLimitedController, "index")
    assert_equal %w( ensure_login ), @controller.template.assigns["ran_filter"]
    test_process(SkippingAndLimitedController, "public")
    assert_nil @controller.template.assigns["ran_filter"]
620 621 622
  end

  def test_skipping_and_reordering_controller
623 624
    test_process(SkippingAndReorderingController, "index")
    assert_equal %w( find_record ensure_login ), @controller.template.assigns["ran_filter"]
625
  end
626

627
  def test_conditional_skipping_of_filters
628 629 630 631
    test_process(ConditionalSkippingController, "login")
    assert_nil @controller.template.assigns["ran_filter"]
    test_process(ConditionalSkippingController, "change_password")
    assert_equal %w( ensure_login find_user ), @controller.template.assigns["ran_filter"]
632

633 634 635 636
    test_process(ConditionalSkippingController, "login")
    assert_nil @controller.template.controller.instance_variable_get("@ran_after_filter")
    test_process(ConditionalSkippingController, "change_password")
    assert_equal %w( clean_up ), @controller.template.controller.instance_variable_get("@ran_after_filter")
637 638
  end

639
  def test_conditional_skipping_of_filters_when_parent_filter_is_also_conditional
640 641 642 643
    test_process(ChildOfConditionalParentController)
    assert_equal %w( conditional_in_parent conditional_in_parent ), @controller.template.assigns['ran_filter']
    test_process(ChildOfConditionalParentController, 'another_action')
    assert_nil @controller.template.assigns['ran_filter']
644
  end
645

646
  def test_condition_skipping_of_filters_when_siblings_also_have_conditions
647 648 649 650 651 652
    test_process(ChildOfConditionalParentController)
    assert_equal %w( conditional_in_parent conditional_in_parent ), @controller.template.assigns['ran_filter'], "1"
    test_process(AnotherChildOfConditionalParentController)
    assert_equal nil, @controller.template.assigns['ran_filter']
    test_process(ChildOfConditionalParentController)
    assert_equal %w( conditional_in_parent conditional_in_parent ), @controller.template.assigns['ran_filter']
653 654
  end

655
  def test_changing_the_requirements
656 657
    test_process(ChangingTheRequirementsController, "go_wild")
    assert_equal nil, @controller.template.assigns['ran_filter']
658
  end
659

660 661 662 663 664
  def test_a_rescuing_around_filter
    response = nil
    assert_nothing_raised do
      response = test_process(RescuedController)
    end
665

666 667 668
    assert response.success?
    assert_equal("I rescued this: #<FilterTest::ErrorToRescue: Something made the bad noise.>", response.body)
  end
669

D
Initial  
David Heinemeier Hansson 已提交
670
  private
671
    def test_process(controller, action = "show")
672
      ActionController::Base.class_eval { include ActionController::ProcessWithTest } unless ActionController::Base < ActionController::ProcessWithTest
D
Initial  
David Heinemeier Hansson 已提交
673
      request = ActionController::TestRequest.new
674
      request.action = action
675
      controller = controller.new if controller.is_a?(Class)
676
      @controller = controller
677
      @controller.process(request, ActionController::TestResponse.new)
D
Initial  
David Heinemeier Hansson 已提交
678
    end
679
end
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766



class PostsController < ActionController::Base
  def rescue_action(e); raise e; end

  module AroundExceptions
    class Error < StandardError ; end
    class Before < Error ; end
    class After < Error ; end
  end
  include AroundExceptions

  class DefaultFilter
    include AroundExceptions
  end

  module_eval %w(raises_before raises_after raises_both no_raise no_filter).map { |action| "def #{action}; default_action end" }.join("\n")

  private
    def default_action
      render :inline => "#{action_name} called"
    end
end

class ControllerWithSymbolAsFilter < PostsController
  around_filter :raise_before, :only => :raises_before
  around_filter :raise_after, :only => :raises_after
  around_filter :without_exception, :only => :no_raise

  private
    def raise_before
      raise Before
      yield
    end

    def raise_after
      yield
      raise After
    end

    def without_exception
      # Do stuff...
      1 + 1

      yield

      # Do stuff...
      1 + 1
    end
end

class ControllerWithFilterClass < PostsController
  class YieldingFilter < DefaultFilter
    def self.filter(controller)
      yield
      raise After
    end
  end

  around_filter YieldingFilter, :only => :raises_after
end

class ControllerWithFilterInstance < PostsController
  class YieldingFilter < DefaultFilter
    def filter(controller)
      yield
      raise After
    end
  end

  around_filter YieldingFilter.new, :only => :raises_after
end

class ControllerWithFilterMethod < PostsController
  class YieldingFilter < DefaultFilter
    def filter(controller)
      yield
      raise After
    end
  end

  around_filter YieldingFilter.new.method(:filter), :only => :raises_after
end

class ControllerWithProcFilter < PostsController
  around_filter(:only => :no_raise) do |c,b|
767
    c.instance_variable_set(:"@before", true)
768
    b.call
769
    c.instance_variable_set(:"@after", true)
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
  end
end

class ControllerWithNestedFilters < ControllerWithSymbolAsFilter
  around_filter :raise_before, :raise_after, :without_exception, :only => :raises_both
end

class ControllerWithAllTypesOfFilters < PostsController
  before_filter :before
  around_filter :around
  after_filter :after
  around_filter :around_again

  private
  def before
    @ran_filter ||= []
    @ran_filter << 'before'
  end

  def around
    @ran_filter << 'around (before yield)'
    yield
    @ran_filter << 'around (after yield)'
  end

  def after
    @ran_filter << 'after'
  end

  def around_again
    @ran_filter << 'around_again (before yield)'
    yield
    @ran_filter << 'around_again (after yield)'
  end
end

class ControllerWithTwoLessFilters < ControllerWithAllTypesOfFilters
  skip_filter :around_again
  skip_filter :after
end

class YieldingAroundFiltersTest < Test::Unit::TestCase
  include PostsController::AroundExceptions

  def test_filters_registering
    assert_equal 1, ControllerWithFilterMethod.filter_chain.size
    assert_equal 1, ControllerWithFilterClass.filter_chain.size
    assert_equal 1, ControllerWithFilterInstance.filter_chain.size
    assert_equal 3, ControllerWithSymbolAsFilter.filter_chain.size
    assert_equal 6, ControllerWithNestedFilters.filter_chain.size
    assert_equal 4, ControllerWithAllTypesOfFilters.filter_chain.size
  end

  def test_base
    controller = PostsController
    assert_nothing_raised { test_process(controller,'no_raise') }
    assert_nothing_raised { test_process(controller,'raises_before') }
    assert_nothing_raised { test_process(controller,'raises_after') }
    assert_nothing_raised { test_process(controller,'no_filter') }
  end

  def test_with_symbol
    controller = ControllerWithSymbolAsFilter
    assert_nothing_raised { test_process(controller,'no_raise') }
    assert_raise(Before) { test_process(controller,'raises_before') }
    assert_raise(After) { test_process(controller,'raises_after') }
    assert_nothing_raised { test_process(controller,'no_raise') }
  end

  def test_with_class
    controller = ControllerWithFilterClass
    assert_nothing_raised { test_process(controller,'no_raise') }
    assert_raise(After) { test_process(controller,'raises_after') }
  end

  def test_with_instance
    controller = ControllerWithFilterInstance
    assert_nothing_raised { test_process(controller,'no_raise') }
    assert_raise(After) { test_process(controller,'raises_after') }
  end

  def test_with_method
    controller = ControllerWithFilterMethod
    assert_nothing_raised { test_process(controller,'no_raise') }
    assert_raise(After) { test_process(controller,'raises_after') }
  end

  def test_with_proc
858 859 860
    test_process(ControllerWithProcFilter,'no_raise')
    assert @controller.template.assigns['before']
    assert @controller.template.assigns['after']
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
  end

  def test_nested_filters
    controller = ControllerWithNestedFilters
    assert_nothing_raised do
      begin
        test_process(controller,'raises_both')
      rescue Before, After
      end
    end
    assert_raise Before do
      begin
        test_process(controller,'raises_both')
      rescue After
      end
    end
  end

  def test_filter_order_with_all_filter_types
880 881
    test_process(ControllerWithAllTypesOfFilters,'no_raise')
    assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) around (after yield) after', @controller.template.assigns['ran_filter'].join(' ')
882 883 884
  end

  def test_filter_order_with_skip_filter_method
885 886
    test_process(ControllerWithTwoLessFilters,'no_raise')
    assert_equal 'before around (before yield) around (after yield)', @controller.template.assigns['ran_filter'].join(' ')
887 888
  end

889 890 891
  def test_first_filter_in_multiple_before_filter_chain_halts
    controller = ::FilterTest::TestMultipleFiltersController.new
    response = test_process(controller, 'fail_1')
892
    assert_equal ' ', response.body
893 894 895 896 897 898 899
    assert_equal 1, controller.instance_variable_get(:@try)
    assert controller.instance_variable_get(:@before_filter_chain_aborted)
  end

  def test_second_filter_in_multiple_before_filter_chain_halts
    controller = ::FilterTest::TestMultipleFiltersController.new
    response = test_process(controller, 'fail_2')
900
    assert_equal ' ', response.body
901 902 903 904 905 906 907
    assert_equal 2, controller.instance_variable_get(:@try)
    assert controller.instance_variable_get(:@before_filter_chain_aborted)
  end

  def test_last_filter_in_multiple_before_filter_chain_halts
    controller = ::FilterTest::TestMultipleFiltersController.new
    response = test_process(controller, 'fail_3')
908
    assert_equal ' ', response.body
909 910 911 912
    assert_equal 3, controller.instance_variable_get(:@try)
    assert controller.instance_variable_get(:@before_filter_chain_aborted)
  end

913 914
  protected
    def test_process(controller, action = "show")
915
      ActionController::Base.class_eval { include ActionController::ProcessWithTest } unless ActionController::Base < ActionController::ProcessWithTest
916 917
      request = ActionController::TestRequest.new
      request.action = action
918
      controller = controller.new if controller.is_a?(Class)
919
      @controller = controller
920
      @controller.process(request, ActionController::TestResponse.new)
921 922
    end
end