/activesupport/test/callbacks_test.rb

https://bitbucket.org/druly/rails_cherry_pick · Ruby · 706 lines · 578 code · 124 blank · 4 comment · 6 complexity · 2d16afb341ddbd1eeaf50859d9825ca8 MD5 · raw file

  1. require 'abstract_unit'
  2. require 'test/unit'
  3. module CallbacksTest
  4. class Phone
  5. include ActiveSupport::Callbacks
  6. define_callbacks :save, :rescuable => true
  7. set_callback :save, :before, :before_save1
  8. set_callback :save, :after, :after_save1
  9. def before_save1; self.history << :before; end
  10. def after_save1; self.history << :after; end
  11. def save
  12. run_callbacks :save do
  13. raise 'boom'
  14. end
  15. end
  16. def history
  17. @history ||= []
  18. end
  19. end
  20. class Record
  21. include ActiveSupport::Callbacks
  22. define_callbacks :save
  23. def self.before_save(*filters, &blk)
  24. set_callback(:save, :before, *filters, &blk)
  25. end
  26. def self.after_save(*filters, &blk)
  27. set_callback(:save, :after, *filters, &blk)
  28. end
  29. class << self
  30. def callback_symbol(callback_method)
  31. method_name = :"#{callback_method}_method"
  32. define_method(method_name) do
  33. history << [callback_method, :symbol]
  34. end
  35. method_name
  36. end
  37. def callback_string(callback_method)
  38. "history << [#{callback_method.to_sym.inspect}, :string]"
  39. end
  40. def callback_proc(callback_method)
  41. Proc.new { |model| model.history << [callback_method, :proc] }
  42. end
  43. def callback_object(callback_method)
  44. klass = Class.new
  45. klass.send(:define_method, callback_method) do |model|
  46. model.history << [:"#{callback_method}_save", :object]
  47. end
  48. klass.new
  49. end
  50. end
  51. def history
  52. @history ||= []
  53. end
  54. end
  55. class Person < Record
  56. [:before_save, :after_save].each do |callback_method|
  57. callback_method_sym = callback_method.to_sym
  58. send(callback_method, callback_symbol(callback_method_sym))
  59. send(callback_method, callback_string(callback_method_sym))
  60. send(callback_method, callback_proc(callback_method_sym))
  61. send(callback_method, callback_object(callback_method_sym.to_s.gsub(/_save/, '')))
  62. send(callback_method) { |model| model.history << [callback_method_sym, :block] }
  63. end
  64. def save
  65. run_callbacks :save
  66. end
  67. end
  68. class PersonSkipper < Person
  69. skip_callback :save, :before, :before_save_method, :if => :yes
  70. skip_callback :save, :after, :before_save_method, :unless => :yes
  71. skip_callback :save, :after, :before_save_method, :if => :no
  72. skip_callback :save, :before, :before_save_method, :unless => :no
  73. def yes; true; end
  74. def no; false; end
  75. end
  76. class ParentController
  77. include ActiveSupport::Callbacks
  78. define_callbacks :dispatch
  79. set_callback :dispatch, :before, :log, :per_key => {:unless => proc {|c| c.action_name == :index || c.action_name == :show }}
  80. set_callback :dispatch, :after, :log2
  81. attr_reader :action_name, :logger
  82. def initialize(action_name)
  83. @action_name, @logger = action_name, []
  84. end
  85. def log
  86. @logger << action_name
  87. end
  88. def log2
  89. @logger << action_name
  90. end
  91. def dispatch
  92. run_callbacks :dispatch, action_name do
  93. @logger << "Done"
  94. end
  95. self
  96. end
  97. end
  98. class Child < ParentController
  99. skip_callback :dispatch, :before, :log, :per_key => {:if => proc {|c| c.action_name == :update} }
  100. skip_callback :dispatch, :after, :log2
  101. end
  102. class OneTimeCompile < Record
  103. @@starts_true, @@starts_false = true, false
  104. def initialize
  105. super
  106. end
  107. before_save Proc.new {|r| r.history << [:before_save, :starts_true, :if] }, :per_key => {:if => :starts_true}
  108. before_save Proc.new {|r| r.history << [:before_save, :starts_false, :if] }, :per_key => {:if => :starts_false}
  109. before_save Proc.new {|r| r.history << [:before_save, :starts_true, :unless] }, :per_key => {:unless => :starts_true}
  110. before_save Proc.new {|r| r.history << [:before_save, :starts_false, :unless] }, :per_key => {:unless => :starts_false}
  111. def starts_true
  112. if @@starts_true
  113. @@starts_true = false
  114. return true
  115. end
  116. @@starts_true
  117. end
  118. def starts_false
  119. unless @@starts_false
  120. @@starts_false = true
  121. return false
  122. end
  123. @@starts_false
  124. end
  125. def save
  126. run_callbacks :save, :action
  127. end
  128. end
  129. class OneTimeCompileTest < Test::Unit::TestCase
  130. def test_optimized_first_compile
  131. around = OneTimeCompile.new
  132. around.save
  133. assert_equal [
  134. [:before_save, :starts_true, :if],
  135. [:before_save, :starts_true, :unless]
  136. ], around.history
  137. end
  138. end
  139. class AfterSaveConditionalPerson < Record
  140. after_save Proc.new { |r| r.history << [:after_save, :string1] }
  141. after_save Proc.new { |r| r.history << [:after_save, :string2] }
  142. def save
  143. run_callbacks :save
  144. end
  145. end
  146. class AfterSaveConditionalPersonCallbackTest < Test::Unit::TestCase
  147. def test_after_save_runs_in_the_reverse_order
  148. person = AfterSaveConditionalPerson.new
  149. person.save
  150. assert_equal [
  151. [:after_save, :string2],
  152. [:after_save, :string1]
  153. ], person.history
  154. end
  155. end
  156. class ConditionalPerson < Record
  157. # proc
  158. before_save Proc.new { |r| r.history << [:before_save, :proc] }, :if => Proc.new { |r| true }
  159. before_save Proc.new { |r| r.history << "b00m" }, :if => Proc.new { |r| false }
  160. before_save Proc.new { |r| r.history << [:before_save, :proc] }, :unless => Proc.new { |r| false }
  161. before_save Proc.new { |r| r.history << "b00m" }, :unless => Proc.new { |r| true }
  162. # symbol
  163. before_save Proc.new { |r| r.history << [:before_save, :symbol] }, :if => :yes
  164. before_save Proc.new { |r| r.history << "b00m" }, :if => :no
  165. before_save Proc.new { |r| r.history << [:before_save, :symbol] }, :unless => :no
  166. before_save Proc.new { |r| r.history << "b00m" }, :unless => :yes
  167. # string
  168. before_save Proc.new { |r| r.history << [:before_save, :string] }, :if => 'yes'
  169. before_save Proc.new { |r| r.history << "b00m" }, :if => 'no'
  170. before_save Proc.new { |r| r.history << [:before_save, :string] }, :unless => 'no'
  171. before_save Proc.new { |r| r.history << "b00m" }, :unless => 'yes'
  172. # Combined if and unless
  173. before_save Proc.new { |r| r.history << [:before_save, :combined_symbol] }, :if => :yes, :unless => :no
  174. before_save Proc.new { |r| r.history << "b00m" }, :if => :yes, :unless => :yes
  175. def yes; true; end
  176. def other_yes; true; end
  177. def no; false; end
  178. def other_no; false; end
  179. def save
  180. run_callbacks :save
  181. end
  182. end
  183. class CleanPerson < ConditionalPerson
  184. reset_callbacks :save
  185. end
  186. class MySuper
  187. include ActiveSupport::Callbacks
  188. define_callbacks :save
  189. end
  190. class AroundPerson < MySuper
  191. attr_reader :history
  192. set_callback :save, :before, :nope, :if => :no
  193. set_callback :save, :before, :nope, :unless => :yes
  194. set_callback :save, :after, :tweedle
  195. set_callback :save, :before, "tweedle_dee"
  196. set_callback :save, :before, proc {|m| m.history << "yup" }
  197. set_callback :save, :before, :nope, :if => proc { false }
  198. set_callback :save, :before, :nope, :unless => proc { true }
  199. set_callback :save, :before, :yup, :if => proc { true }
  200. set_callback :save, :before, :yup, :unless => proc { false }
  201. set_callback :save, :around, :tweedle_dum
  202. set_callback :save, :around, :w0tyes, :if => :yes
  203. set_callback :save, :around, :w0tno, :if => :no
  204. set_callback :save, :around, :tweedle_deedle
  205. def no; false; end
  206. def yes; true; end
  207. def nope
  208. @history << "boom"
  209. end
  210. def yup
  211. @history << "yup"
  212. end
  213. def w0tyes
  214. @history << "w0tyes before"
  215. yield
  216. @history << "w0tyes after"
  217. end
  218. def w0tno
  219. @history << "boom"
  220. yield
  221. end
  222. def tweedle_dee
  223. @history << "tweedle dee"
  224. end
  225. def tweedle_dum
  226. @history << "tweedle dum pre"
  227. yield
  228. @history << "tweedle dum post"
  229. end
  230. def tweedle
  231. @history << "tweedle"
  232. end
  233. def tweedle_deedle
  234. @history << "tweedle deedle pre"
  235. yield
  236. @history << "tweedle deedle post"
  237. end
  238. def initialize
  239. @history = []
  240. end
  241. def save
  242. run_callbacks :save do
  243. @history << "running"
  244. end
  245. end
  246. end
  247. class AroundPersonResult < MySuper
  248. attr_reader :result
  249. set_callback :save, :after, :tweedle_1
  250. set_callback :save, :around, :tweedle_dum
  251. set_callback :save, :after, :tweedle_2
  252. def tweedle_dum
  253. @result = yield
  254. end
  255. def tweedle_1
  256. :tweedle_1
  257. end
  258. def tweedle_2
  259. :tweedle_2
  260. end
  261. def save
  262. run_callbacks :save do
  263. :running
  264. end
  265. end
  266. end
  267. class HyphenatedCallbacks
  268. include ActiveSupport::Callbacks
  269. define_callbacks :save
  270. attr_reader :stuff
  271. set_callback :save, :before, :action, :per_key => {:if => :yes}
  272. def yes() true end
  273. def action
  274. @stuff = "ACTION"
  275. end
  276. def save
  277. run_callbacks :save, "hyphen-ated" do
  278. @stuff
  279. end
  280. end
  281. end
  282. module ExtendModule
  283. def self.extended(base)
  284. base.class_eval do
  285. set_callback :save, :before, :record3
  286. end
  287. end
  288. def record3
  289. @recorder << 3
  290. end
  291. end
  292. module IncludeModule
  293. def self.included(base)
  294. base.class_eval do
  295. set_callback :save, :before, :record2
  296. end
  297. end
  298. def record2
  299. @recorder << 2
  300. end
  301. end
  302. class ExtendCallbacks
  303. include ActiveSupport::Callbacks
  304. define_callbacks :save
  305. set_callback :save, :before, :record1
  306. include IncludeModule
  307. def save
  308. run_callbacks :save
  309. end
  310. attr_reader :recorder
  311. def initialize
  312. @recorder = []
  313. end
  314. private
  315. def record1
  316. @recorder << 1
  317. end
  318. end
  319. class AroundCallbacksTest < Test::Unit::TestCase
  320. def test_save_around
  321. around = AroundPerson.new
  322. around.save
  323. assert_equal [
  324. "tweedle dee",
  325. "yup", "yup",
  326. "tweedle dum pre",
  327. "w0tyes before",
  328. "tweedle deedle pre",
  329. "running",
  330. "tweedle deedle post",
  331. "w0tyes after",
  332. "tweedle dum post",
  333. "tweedle"
  334. ], around.history
  335. end
  336. end
  337. class AroundCallbackResultTest < Test::Unit::TestCase
  338. def test_save_around
  339. around = AroundPersonResult.new
  340. around.save
  341. assert_equal :running, around.result
  342. end
  343. end
  344. class SkipCallbacksTest < Test::Unit::TestCase
  345. def test_skip_person
  346. person = PersonSkipper.new
  347. assert_equal [], person.history
  348. person.save
  349. assert_equal [
  350. [:before_save, :string],
  351. [:before_save, :proc],
  352. [:before_save, :object],
  353. [:before_save, :block],
  354. [:after_save, :block],
  355. [:after_save, :object],
  356. [:after_save, :proc],
  357. [:after_save, :string],
  358. [:after_save, :symbol]
  359. ], person.history
  360. end
  361. end
  362. class CallbacksTest < Test::Unit::TestCase
  363. def test_save_phone
  364. phone = Phone.new
  365. assert_raise RuntimeError do
  366. phone.save
  367. end
  368. assert_equal [:before, :after], phone.history
  369. end
  370. def test_save_person
  371. person = Person.new
  372. assert_equal [], person.history
  373. person.save
  374. assert_equal [
  375. [:before_save, :symbol],
  376. [:before_save, :string],
  377. [:before_save, :proc],
  378. [:before_save, :object],
  379. [:before_save, :block],
  380. [:after_save, :block],
  381. [:after_save, :object],
  382. [:after_save, :proc],
  383. [:after_save, :string],
  384. [:after_save, :symbol]
  385. ], person.history
  386. end
  387. end
  388. class ConditionalCallbackTest < Test::Unit::TestCase
  389. def test_save_conditional_person
  390. person = ConditionalPerson.new
  391. person.save
  392. assert_equal [
  393. [:before_save, :proc],
  394. [:before_save, :proc],
  395. [:before_save, :symbol],
  396. [:before_save, :symbol],
  397. [:before_save, :string],
  398. [:before_save, :string],
  399. [:before_save, :combined_symbol],
  400. ], person.history
  401. end
  402. end
  403. class ResetCallbackTest < Test::Unit::TestCase
  404. def test_save_conditional_person
  405. person = CleanPerson.new
  406. person.save
  407. assert_equal [], person.history
  408. end
  409. end
  410. class CallbackTerminator
  411. include ActiveSupport::Callbacks
  412. define_callbacks :save, :terminator => "result == :halt"
  413. set_callback :save, :before, :first
  414. set_callback :save, :before, :second
  415. set_callback :save, :around, :around_it
  416. set_callback :save, :before, :third
  417. set_callback :save, :after, :first
  418. set_callback :save, :around, :around_it
  419. set_callback :save, :after, :second
  420. set_callback :save, :around, :around_it
  421. set_callback :save, :after, :third
  422. attr_reader :history, :saved, :halted
  423. def initialize
  424. @history = []
  425. end
  426. def around_it
  427. @history << "around1"
  428. yield
  429. @history << "around2"
  430. end
  431. def first
  432. @history << "first"
  433. end
  434. def second
  435. @history << "second"
  436. :halt
  437. end
  438. def third
  439. @history << "third"
  440. end
  441. def save
  442. run_callbacks :save do
  443. @saved = true
  444. end
  445. end
  446. def halted_callback_hook(filter)
  447. @halted = filter
  448. end
  449. end
  450. class CallbackObject
  451. def before(caller)
  452. caller.record << "before"
  453. end
  454. def before_save(caller)
  455. caller.record << "before save"
  456. end
  457. def around(caller)
  458. caller.record << "around before"
  459. yield
  460. caller.record << "around after"
  461. end
  462. end
  463. class UsingObjectBefore
  464. include ActiveSupport::Callbacks
  465. define_callbacks :save
  466. set_callback :save, :before, CallbackObject.new
  467. attr_accessor :record
  468. def initialize
  469. @record = []
  470. end
  471. def save
  472. run_callbacks :save do
  473. @record << "yielded"
  474. end
  475. end
  476. end
  477. class UsingObjectAround
  478. include ActiveSupport::Callbacks
  479. define_callbacks :save
  480. set_callback :save, :around, CallbackObject.new
  481. attr_accessor :record
  482. def initialize
  483. @record = []
  484. end
  485. def save
  486. run_callbacks :save do
  487. @record << "yielded"
  488. end
  489. end
  490. end
  491. class CustomScopeObject
  492. include ActiveSupport::Callbacks
  493. define_callbacks :save, :scope => [:kind, :name]
  494. set_callback :save, :before, CallbackObject.new
  495. attr_accessor :record
  496. def initialize
  497. @record = []
  498. end
  499. def save
  500. run_callbacks :save do
  501. @record << "yielded"
  502. "CallbackResult"
  503. end
  504. end
  505. end
  506. class UsingObjectTest < Test::Unit::TestCase
  507. def test_before_object
  508. u = UsingObjectBefore.new
  509. u.save
  510. assert_equal ["before", "yielded"], u.record
  511. end
  512. def test_around_object
  513. u = UsingObjectAround.new
  514. u.save
  515. assert_equal ["around before", "yielded", "around after"], u.record
  516. end
  517. def test_customized_object
  518. u = CustomScopeObject.new
  519. u.save
  520. assert_equal ["before save", "yielded"], u.record
  521. end
  522. def test_block_result_is_returned
  523. u = CustomScopeObject.new
  524. assert_equal "CallbackResult", u.save
  525. end
  526. end
  527. class CallbackTerminatorTest < Test::Unit::TestCase
  528. def test_termination
  529. terminator = CallbackTerminator.new
  530. terminator.save
  531. assert_equal ["first", "second", "third", "second", "first"], terminator.history
  532. end
  533. def test_termination_invokes_hook
  534. terminator = CallbackTerminator.new
  535. terminator.save
  536. assert_equal ":second", terminator.halted
  537. end
  538. def test_block_never_called_if_terminated
  539. obj = CallbackTerminator.new
  540. obj.save
  541. assert !obj.saved
  542. end
  543. end
  544. class HyphenatedKeyTest < Test::Unit::TestCase
  545. def test_save
  546. obj = HyphenatedCallbacks.new
  547. obj.save
  548. assert_equal "ACTION", obj.stuff
  549. end
  550. end
  551. class WriterSkipper < Person
  552. attr_accessor :age
  553. skip_callback :save, :before, :before_save_method, :if => lambda {self.age > 21}
  554. end
  555. class WriterCallbacksTest < Test::Unit::TestCase
  556. def test_skip_writer
  557. writer = WriterSkipper.new
  558. writer.age = 18
  559. assert_equal [], writer.history
  560. writer.save
  561. assert_equal [
  562. [:before_save, :symbol],
  563. [:before_save, :string],
  564. [:before_save, :proc],
  565. [:before_save, :object],
  566. [:before_save, :block],
  567. [:after_save, :block],
  568. [:after_save, :object],
  569. [:after_save, :proc],
  570. [:after_save, :string],
  571. [:after_save, :symbol]
  572. ], writer.history
  573. end
  574. end
  575. class ExtendCallbacksTest < Test::Unit::TestCase
  576. def test_save
  577. model = ExtendCallbacks.new.extend ExtendModule
  578. model.save
  579. assert_equal [1, 2, 3], model.recorder
  580. end
  581. end
  582. end