aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/test/cases/callbacks_test.rb
blob: d38f773afd7b491b4c0d7437b9e5fc500fe4e12f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# frozen_string_literal: true

require "cases/helper"

class CallbacksTest < ActiveSupport::TestCase
  class CallbackValidator
    def around_create(model)
      model.callbacks << :before_around_create
      yield
      model.callbacks << :after_around_create
      false
    end
  end

  class ModelCallbacks
    attr_reader :callbacks
    extend ActiveModel::Callbacks

    define_model_callbacks :create
    define_model_callbacks :initialize, only: :after
    define_model_callbacks :multiple,   only: [:before, :around]
    define_model_callbacks :empty,      only: []

    before_create :before_create
    around_create CallbackValidator.new

    after_create do |model|
      model.callbacks << :after_create
      false
    end

    after_create { |model| model.callbacks << :final_callback }

    def initialize(options = {})
      @callbacks = []
      @valid = options[:valid]
      @before_create_returns = options.fetch(:before_create_returns, true)
      @before_create_throws = options[:before_create_throws]
    end

    def before_create
      @callbacks << :before_create
      throw(@before_create_throws) if @before_create_throws
      @before_create_returns
    end

    def create
      run_callbacks :create do
        @callbacks << :create
        @valid
      end
    end
  end

  test "complete callback chain" do
    model = ModelCallbacks.new
    model.create
    assert_equal model.callbacks, [ :before_create, :before_around_create, :create,
                                    :after_around_create, :after_create, :final_callback]
  end

  test "the callback chain is not halted when around or after callbacks return false" do
    model = ModelCallbacks.new
    model.create
    assert_equal model.callbacks.last, :final_callback
  end

  test "the callback chain is not halted when a before callback returns false)" do
    model = ModelCallbacks.new(before_create_returns: false)
    model.create
    assert_equal model.callbacks.last, :final_callback
  end

  test "the callback chain is halted when a callback throws :abort" do
    model = ModelCallbacks.new(before_create_throws: :abort)
    model.create
    assert_equal model.callbacks, [:before_create]
  end

  test "after callbacks are not executed if the block returns false" do
    model = ModelCallbacks.new(valid: false)
    model.create
    assert_equal model.callbacks, [ :before_create, :before_around_create,
                                    :create, :after_around_create]
  end

  test "only selects which types of callbacks should be created" do
    assert_not_respond_to ModelCallbacks, :before_initialize
    assert_not_respond_to ModelCallbacks, :around_initialize
    assert_respond_to ModelCallbacks, :after_initialize
  end

  test "only selects which types of callbacks should be created from an array list" do
    assert_respond_to ModelCallbacks, :before_multiple
    assert_respond_to ModelCallbacks, :around_multiple
    assert_not_respond_to ModelCallbacks, :after_multiple
  end

  test "no callbacks should be created" do
    assert_not_respond_to ModelCallbacks, :before_empty
    assert_not_respond_to ModelCallbacks, :around_empty
    assert_not_respond_to ModelCallbacks, :after_empty
  end

  class Violin
    attr_reader :history
    def initialize
      @history = []
    end
    extend ActiveModel::Callbacks
    define_model_callbacks :create
    def callback1; history << "callback1"; end
    def callback2; history << "callback2"; end
    def create
      run_callbacks(:create) { }
      self
    end
  end
  class Violin1 < Violin
    after_create :callback1, :callback2
  end
  class Violin2 < Violin
    after_create :callback1
    after_create :callback2
  end

  test "after_create callbacks with both callbacks declared in one line" do
    assert_equal ["callback1", "callback2"], Violin1.new.create.history
  end

  test "after_create callbacks with both callbacks declared in different lines" do
    assert_equal ["callback1", "callback2"], Violin2.new.create.history
  end
end