PageRenderTime 54ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/activesupport/test/callbacks_test.rb

https://github.com/cmeiklejohn/rails
Ruby | 649 lines | 534 code | 111 blank | 4 comment | 6 complexity | 2ab1659eb7236dbebc8e6793cca68f1a 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. class AroundCallbacksTest < Test::Unit::TestCase
  283. def test_save_around
  284. around = AroundPerson.new
  285. around.save
  286. assert_equal [
  287. "tweedle dee",
  288. "yup", "yup",
  289. "tweedle dum pre",
  290. "w0tyes before",
  291. "tweedle deedle pre",
  292. "running",
  293. "tweedle deedle post",
  294. "w0tyes after",
  295. "tweedle dum post",
  296. "tweedle"
  297. ], around.history
  298. end
  299. end
  300. class AroundCallbackResultTest < Test::Unit::TestCase
  301. def test_save_around
  302. around = AroundPersonResult.new
  303. around.save
  304. assert_equal :running, around.result
  305. end
  306. end
  307. class SkipCallbacksTest < Test::Unit::TestCase
  308. def test_skip_person
  309. person = PersonSkipper.new
  310. assert_equal [], person.history
  311. person.save
  312. assert_equal [
  313. [:before_save, :string],
  314. [:before_save, :proc],
  315. [:before_save, :object],
  316. [:before_save, :block],
  317. [:after_save, :block],
  318. [:after_save, :object],
  319. [:after_save, :proc],
  320. [:after_save, :string],
  321. [:after_save, :symbol]
  322. ], person.history
  323. end
  324. end
  325. class CallbacksTest < Test::Unit::TestCase
  326. def test_save_phone
  327. phone = Phone.new
  328. assert_raise RuntimeError do
  329. phone.save
  330. end
  331. assert_equal [:before, :after], phone.history
  332. end
  333. def test_save_person
  334. person = Person.new
  335. assert_equal [], person.history
  336. person.save
  337. assert_equal [
  338. [:before_save, :symbol],
  339. [:before_save, :string],
  340. [:before_save, :proc],
  341. [:before_save, :object],
  342. [:before_save, :block],
  343. [:after_save, :block],
  344. [:after_save, :object],
  345. [:after_save, :proc],
  346. [:after_save, :string],
  347. [:after_save, :symbol]
  348. ], person.history
  349. end
  350. end
  351. class ConditionalCallbackTest < Test::Unit::TestCase
  352. def test_save_conditional_person
  353. person = ConditionalPerson.new
  354. person.save
  355. assert_equal [
  356. [:before_save, :proc],
  357. [:before_save, :proc],
  358. [:before_save, :symbol],
  359. [:before_save, :symbol],
  360. [:before_save, :string],
  361. [:before_save, :string],
  362. [:before_save, :combined_symbol],
  363. ], person.history
  364. end
  365. end
  366. class ResetCallbackTest < Test::Unit::TestCase
  367. def test_save_conditional_person
  368. person = CleanPerson.new
  369. person.save
  370. assert_equal [], person.history
  371. end
  372. end
  373. class CallbackTerminator
  374. include ActiveSupport::Callbacks
  375. define_callbacks :save, :terminator => "result == :halt"
  376. set_callback :save, :before, :first
  377. set_callback :save, :before, :second
  378. set_callback :save, :around, :around_it
  379. set_callback :save, :before, :third
  380. set_callback :save, :after, :first
  381. set_callback :save, :around, :around_it
  382. set_callback :save, :after, :second
  383. set_callback :save, :around, :around_it
  384. set_callback :save, :after, :third
  385. attr_reader :history, :saved, :halted
  386. def initialize
  387. @history = []
  388. end
  389. def around_it
  390. @history << "around1"
  391. yield
  392. @history << "around2"
  393. end
  394. def first
  395. @history << "first"
  396. end
  397. def second
  398. @history << "second"
  399. :halt
  400. end
  401. def third
  402. @history << "third"
  403. end
  404. def save
  405. run_callbacks :save do
  406. @saved = true
  407. end
  408. end
  409. def halted_callback_hook(filter)
  410. @halted = filter
  411. end
  412. end
  413. class CallbackObject
  414. def before(caller)
  415. caller.record << "before"
  416. end
  417. def before_save(caller)
  418. caller.record << "before save"
  419. end
  420. def around(caller)
  421. caller.record << "around before"
  422. yield
  423. caller.record << "around after"
  424. end
  425. end
  426. class UsingObjectBefore
  427. include ActiveSupport::Callbacks
  428. define_callbacks :save
  429. set_callback :save, :before, CallbackObject.new
  430. attr_accessor :record
  431. def initialize
  432. @record = []
  433. end
  434. def save
  435. run_callbacks :save do
  436. @record << "yielded"
  437. end
  438. end
  439. end
  440. class UsingObjectAround
  441. include ActiveSupport::Callbacks
  442. define_callbacks :save
  443. set_callback :save, :around, CallbackObject.new
  444. attr_accessor :record
  445. def initialize
  446. @record = []
  447. end
  448. def save
  449. run_callbacks :save do
  450. @record << "yielded"
  451. end
  452. end
  453. end
  454. class CustomScopeObject
  455. include ActiveSupport::Callbacks
  456. define_callbacks :save, :scope => [:kind, :name]
  457. set_callback :save, :before, CallbackObject.new
  458. attr_accessor :record
  459. def initialize
  460. @record = []
  461. end
  462. def save
  463. run_callbacks :save do
  464. @record << "yielded"
  465. "CallbackResult"
  466. end
  467. end
  468. end
  469. class UsingObjectTest < Test::Unit::TestCase
  470. def test_before_object
  471. u = UsingObjectBefore.new
  472. u.save
  473. assert_equal ["before", "yielded"], u.record
  474. end
  475. def test_around_object
  476. u = UsingObjectAround.new
  477. u.save
  478. assert_equal ["around before", "yielded", "around after"], u.record
  479. end
  480. def test_customized_object
  481. u = CustomScopeObject.new
  482. u.save
  483. assert_equal ["before save", "yielded"], u.record
  484. end
  485. def test_block_result_is_returned
  486. u = CustomScopeObject.new
  487. assert_equal "CallbackResult", u.save
  488. end
  489. end
  490. class CallbackTerminatorTest < Test::Unit::TestCase
  491. def test_termination
  492. terminator = CallbackTerminator.new
  493. terminator.save
  494. assert_equal ["first", "second", "third", "second", "first"], terminator.history
  495. end
  496. def test_termination_invokes_hook
  497. terminator = CallbackTerminator.new
  498. terminator.save
  499. assert_equal ":second", terminator.halted
  500. end
  501. def test_block_never_called_if_terminated
  502. obj = CallbackTerminator.new
  503. obj.save
  504. assert !obj.saved
  505. end
  506. end
  507. class HyphenatedKeyTest < Test::Unit::TestCase
  508. def test_save
  509. obj = HyphenatedCallbacks.new
  510. obj.save
  511. assert_equal "ACTION", obj.stuff
  512. end
  513. end
  514. class WriterSkipper < Person
  515. attr_accessor :age
  516. skip_callback :save, :before, :before_save_method, :if => lambda {self.age > 21}
  517. end
  518. class WriterCallbacksTest < Test::Unit::TestCase
  519. def test_skip_writer
  520. writer = WriterSkipper.new
  521. writer.age = 18
  522. assert_equal [], writer.history
  523. writer.save
  524. assert_equal [
  525. [:before_save, :symbol],
  526. [:before_save, :string],
  527. [:before_save, :proc],
  528. [:before_save, :object],
  529. [:before_save, :block],
  530. [:after_save, :block],
  531. [:after_save, :object],
  532. [:after_save, :proc],
  533. [:after_save, :string],
  534. [:after_save, :symbol]
  535. ], writer.history
  536. end
  537. end
  538. end