/activesupport/test/callbacks_test.rb
Ruby | 639 lines | 526 code | 109 blank | 4 comment | 6 complexity | 1361790d1db3084633fe0f9b1fc63122 MD5 | raw file
- require 'abstract_unit'
- require 'test/unit'
- module CallbacksTest
- class Phone
- include ActiveSupport::Callbacks
- define_callbacks :save, :rescuable => true
- set_callback :save, :before, :before_save1
- set_callback :save, :after, :after_save1
- def before_save1; self.history << :before; end
- def after_save1; self.history << :after; end
- def save
- run_callbacks :save do
- raise 'boom'
- end
- end
- def history
- @history ||= []
- end
- end
- class Record
- include ActiveSupport::Callbacks
- define_callbacks :save
- def self.before_save(*filters, &blk)
- set_callback(:save, :before, *filters, &blk)
- end
- def self.after_save(*filters, &blk)
- set_callback(:save, :after, *filters, &blk)
- end
- class << self
- def callback_symbol(callback_method)
- method_name = :"#{callback_method}_method"
- define_method(method_name) do
- history << [callback_method, :symbol]
- end
- method_name
- end
- def callback_string(callback_method)
- "history << [#{callback_method.to_sym.inspect}, :string]"
- end
- def callback_proc(callback_method)
- Proc.new { |model| model.history << [callback_method, :proc] }
- end
- def callback_object(callback_method)
- klass = Class.new
- klass.send(:define_method, callback_method) do |model|
- model.history << [:"#{callback_method}_save", :object]
- end
- klass.new
- end
- end
- def history
- @history ||= []
- end
- end
- class Person < Record
- [:before_save, :after_save].each do |callback_method|
- callback_method_sym = callback_method.to_sym
- send(callback_method, callback_symbol(callback_method_sym))
- send(callback_method, callback_string(callback_method_sym))
- send(callback_method, callback_proc(callback_method_sym))
- send(callback_method, callback_object(callback_method_sym.to_s.gsub(/_save/, '')))
- send(callback_method) { |model| model.history << [callback_method_sym, :block] }
- end
- def save
- run_callbacks :save
- end
- end
- class PersonSkipper < Person
- skip_callback :save, :before, :before_save_method, :if => :yes
- skip_callback :save, :after, :before_save_method, :unless => :yes
- skip_callback :save, :after, :before_save_method, :if => :no
- skip_callback :save, :before, :before_save_method, :unless => :no
- def yes; true; end
- def no; false; end
- end
- class ParentController
- include ActiveSupport::Callbacks
- define_callbacks :dispatch
- set_callback :dispatch, :before, :log, :per_key => {:unless => proc {|c| c.action_name == :index || c.action_name == :show }}
- set_callback :dispatch, :after, :log2
- attr_reader :action_name, :logger
- def initialize(action_name)
- @action_name, @logger = action_name, []
- end
- def log
- @logger << action_name
- end
- def log2
- @logger << action_name
- end
- def dispatch
- run_callbacks :dispatch, action_name do
- @logger << "Done"
- end
- self
- end
- end
- class Child < ParentController
- skip_callback :dispatch, :before, :log, :per_key => {:if => proc {|c| c.action_name == :update} }
- skip_callback :dispatch, :after, :log2
- end
- class OneTimeCompile < Record
- @@starts_true, @@starts_false = true, false
- def initialize
- super
- end
- before_save Proc.new {|r| r.history << [:before_save, :starts_true, :if] }, :per_key => {:if => :starts_true}
- before_save Proc.new {|r| r.history << [:before_save, :starts_false, :if] }, :per_key => {:if => :starts_false}
- before_save Proc.new {|r| r.history << [:before_save, :starts_true, :unless] }, :per_key => {:unless => :starts_true}
- before_save Proc.new {|r| r.history << [:before_save, :starts_false, :unless] }, :per_key => {:unless => :starts_false}
- def starts_true
- if @@starts_true
- @@starts_true = false
- return true
- end
- @@starts_true
- end
- def starts_false
- unless @@starts_false
- @@starts_false = true
- return false
- end
- @@starts_false
- end
- def save
- run_callbacks :save, :action
- end
- end
- class OneTimeCompileTest < Test::Unit::TestCase
- def test_optimized_first_compile
- around = OneTimeCompile.new
- around.save
- assert_equal [
- [:before_save, :starts_true, :if],
- [:before_save, :starts_true, :unless]
- ], around.history
- end
- end
- class AfterSaveConditionalPerson < Record
- after_save Proc.new { |r| r.history << [:after_save, :string1] }
- after_save Proc.new { |r| r.history << [:after_save, :string2] }
- def save
- run_callbacks :save
- end
- end
- class AfterSaveConditionalPersonCallbackTest < Test::Unit::TestCase
- def test_after_save_runs_in_the_reverse_order
- person = AfterSaveConditionalPerson.new
- person.save
- assert_equal [
- [:after_save, :string2],
- [:after_save, :string1]
- ], person.history
- end
- end
- class ConditionalPerson < Record
- # proc
- before_save Proc.new { |r| r.history << [:before_save, :proc] }, :if => Proc.new { |r| true }
- before_save Proc.new { |r| r.history << "b00m" }, :if => Proc.new { |r| false }
- before_save Proc.new { |r| r.history << [:before_save, :proc] }, :unless => Proc.new { |r| false }
- before_save Proc.new { |r| r.history << "b00m" }, :unless => Proc.new { |r| true }
- # symbol
- before_save Proc.new { |r| r.history << [:before_save, :symbol] }, :if => :yes
- before_save Proc.new { |r| r.history << "b00m" }, :if => :no
- before_save Proc.new { |r| r.history << [:before_save, :symbol] }, :unless => :no
- before_save Proc.new { |r| r.history << "b00m" }, :unless => :yes
- # string
- before_save Proc.new { |r| r.history << [:before_save, :string] }, :if => 'yes'
- before_save Proc.new { |r| r.history << "b00m" }, :if => 'no'
- before_save Proc.new { |r| r.history << [:before_save, :string] }, :unless => 'no'
- before_save Proc.new { |r| r.history << "b00m" }, :unless => 'yes'
- # Combined if and unless
- before_save Proc.new { |r| r.history << [:before_save, :combined_symbol] }, :if => :yes, :unless => :no
- before_save Proc.new { |r| r.history << "b00m" }, :if => :yes, :unless => :yes
- def yes; true; end
- def other_yes; true; end
- def no; false; end
- def other_no; false; end
- def save
- run_callbacks :save
- end
- end
- class CleanPerson < ConditionalPerson
- reset_callbacks :save
- end
- class MySuper
- include ActiveSupport::Callbacks
- define_callbacks :save
- end
- class AroundPerson < MySuper
- attr_reader :history
- set_callback :save, :before, :nope, :if => :no
- set_callback :save, :before, :nope, :unless => :yes
- set_callback :save, :after, :tweedle
- set_callback :save, :before, "tweedle_dee"
- set_callback :save, :before, proc {|m| m.history << "yup" }
- set_callback :save, :before, :nope, :if => proc { false }
- set_callback :save, :before, :nope, :unless => proc { true }
- set_callback :save, :before, :yup, :if => proc { true }
- set_callback :save, :before, :yup, :unless => proc { false }
- set_callback :save, :around, :tweedle_dum
- set_callback :save, :around, :w0tyes, :if => :yes
- set_callback :save, :around, :w0tno, :if => :no
- set_callback :save, :around, :tweedle_deedle
- def no; false; end
- def yes; true; end
- def nope
- @history << "boom"
- end
- def yup
- @history << "yup"
- end
- def w0tyes
- @history << "w0tyes before"
- yield
- @history << "w0tyes after"
- end
- def w0tno
- @history << "boom"
- yield
- end
- def tweedle_dee
- @history << "tweedle dee"
- end
- def tweedle_dum
- @history << "tweedle dum pre"
- yield
- @history << "tweedle dum post"
- end
- def tweedle
- @history << "tweedle"
- end
- def tweedle_deedle
- @history << "tweedle deedle pre"
- yield
- @history << "tweedle deedle post"
- end
- def initialize
- @history = []
- end
- def save
- run_callbacks :save do
- @history << "running"
- end
- end
- end
-
- class AroundPersonResult < MySuper
- attr_reader :result
- set_callback :save, :after, :tweedle_1
- set_callback :save, :around, :tweedle_dum
- set_callback :save, :after, :tweedle_2
- def tweedle_dum
- @result = yield
- end
-
- def tweedle_1
- :tweedle_1
- end
- def tweedle_2
- :tweedle_2
- end
-
- def save
- run_callbacks :save do
- :running
- end
- end
- end
- class HyphenatedCallbacks
- include ActiveSupport::Callbacks
- define_callbacks :save
- attr_reader :stuff
- set_callback :save, :before, :action, :per_key => {:if => :yes}
- def yes() true end
- def action
- @stuff = "ACTION"
- end
- def save
- run_callbacks :save, "hyphen-ated" do
- @stuff
- end
- end
- end
- class AroundCallbacksTest < Test::Unit::TestCase
- def test_save_around
- around = AroundPerson.new
- around.save
- assert_equal [
- "tweedle dee",
- "yup", "yup",
- "tweedle dum pre",
- "w0tyes before",
- "tweedle deedle pre",
- "running",
- "tweedle deedle post",
- "w0tyes after",
- "tweedle dum post",
- "tweedle"
- ], around.history
- end
- end
-
- class AroundCallbackResultTest < Test::Unit::TestCase
- def test_save_around
- around = AroundPersonResult.new
- around.save
- assert_equal :running, around.result
- end
- end
- class SkipCallbacksTest < Test::Unit::TestCase
- def test_skip_person
- person = PersonSkipper.new
- assert_equal [], person.history
- person.save
- assert_equal [
- [:before_save, :string],
- [:before_save, :proc],
- [:before_save, :object],
- [:before_save, :block],
- [:after_save, :block],
- [:after_save, :object],
- [:after_save, :proc],
- [:after_save, :string],
- [:after_save, :symbol]
- ], person.history
- end
- end
- class CallbacksTest < Test::Unit::TestCase
- def test_save_phone
- phone = Phone.new
- assert_raise RuntimeError do
- phone.save
- end
- assert_equal [:before, :after], phone.history
- end
- def test_save_person
- person = Person.new
- assert_equal [], person.history
- person.save
- assert_equal [
- [:before_save, :symbol],
- [:before_save, :string],
- [:before_save, :proc],
- [:before_save, :object],
- [:before_save, :block],
- [:after_save, :block],
- [:after_save, :object],
- [:after_save, :proc],
- [:after_save, :string],
- [:after_save, :symbol]
- ], person.history
- end
- end
- class ConditionalCallbackTest < Test::Unit::TestCase
- def test_save_conditional_person
- person = ConditionalPerson.new
- person.save
- assert_equal [
- [:before_save, :proc],
- [:before_save, :proc],
- [:before_save, :symbol],
- [:before_save, :symbol],
- [:before_save, :string],
- [:before_save, :string],
- [:before_save, :combined_symbol],
- ], person.history
- end
- end
- class ResetCallbackTest < Test::Unit::TestCase
- def test_save_conditional_person
- person = CleanPerson.new
- person.save
- assert_equal [], person.history
- end
- end
- class CallbackTerminator
- include ActiveSupport::Callbacks
- define_callbacks :save, :terminator => "result == :halt"
- set_callback :save, :before, :first
- set_callback :save, :before, :second
- set_callback :save, :around, :around_it
- set_callback :save, :before, :third
- set_callback :save, :after, :first
- set_callback :save, :around, :around_it
- set_callback :save, :after, :second
- set_callback :save, :around, :around_it
- set_callback :save, :after, :third
- attr_reader :history, :saved
- def initialize
- @history = []
- end
- def around_it
- @history << "around1"
- yield
- @history << "around2"
- end
- def first
- @history << "first"
- end
- def second
- @history << "second"
- :halt
- end
- def third
- @history << "third"
- end
- def save
- run_callbacks :save do
- @saved = true
- end
- end
- end
- class CallbackObject
- def before(caller)
- caller.record << "before"
- end
- def before_save(caller)
- caller.record << "before save"
- end
- def around(caller)
- caller.record << "around before"
- yield
- caller.record << "around after"
- end
- end
- class UsingObjectBefore
- include ActiveSupport::Callbacks
- define_callbacks :save
- set_callback :save, :before, CallbackObject.new
- attr_accessor :record
- def initialize
- @record = []
- end
- def save
- run_callbacks :save do
- @record << "yielded"
- end
- end
- end
- class UsingObjectAround
- include ActiveSupport::Callbacks
- define_callbacks :save
- set_callback :save, :around, CallbackObject.new
- attr_accessor :record
- def initialize
- @record = []
- end
- def save
- run_callbacks :save do
- @record << "yielded"
- end
- end
- end
- class CustomScopeObject
- include ActiveSupport::Callbacks
- define_callbacks :save, :scope => [:kind, :name]
- set_callback :save, :before, CallbackObject.new
- attr_accessor :record
- def initialize
- @record = []
- end
- def save
- run_callbacks :save do
- @record << "yielded"
- "CallbackResult"
- end
- end
- end
- class UsingObjectTest < Test::Unit::TestCase
- def test_before_object
- u = UsingObjectBefore.new
- u.save
- assert_equal ["before", "yielded"], u.record
- end
- def test_around_object
- u = UsingObjectAround.new
- u.save
- assert_equal ["around before", "yielded", "around after"], u.record
- end
- def test_customized_object
- u = CustomScopeObject.new
- u.save
- assert_equal ["before save", "yielded"], u.record
- end
- def test_block_result_is_returned
- u = CustomScopeObject.new
- assert_equal "CallbackResult", u.save
- end
- end
- class CallbackTerminatorTest < Test::Unit::TestCase
- def test_termination
- terminator = CallbackTerminator.new
- terminator.save
- assert_equal ["first", "second", "third", "second", "first"], terminator.history
- end
- def test_block_never_called_if_terminated
- obj = CallbackTerminator.new
- obj.save
- assert !obj.saved
- end
- end
- class HyphenatedKeyTest < Test::Unit::TestCase
- def test_save
- obj = HyphenatedCallbacks.new
- obj.save
- assert_equal "ACTION", obj.stuff
- end
- end
- class WriterSkipper < Person
- attr_accessor :age
- skip_callback :save, :before, :before_save_method, :if => lambda {self.age > 21}
- end
- class WriterCallbacksTest < Test::Unit::TestCase
- def test_skip_writer
- writer = WriterSkipper.new
- writer.age = 18
- assert_equal [], writer.history
- writer.save
- assert_equal [
- [:before_save, :symbol],
- [:before_save, :string],
- [:before_save, :proc],
- [:before_save, :object],
- [:before_save, :block],
- [:after_save, :block],
- [:after_save, :object],
- [:after_save, :proc],
- [:after_save, :string],
- [:after_save, :symbol]
- ], writer.history
- end
- end
- end