PageRenderTime 58ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/activesupport/test/callbacks_test.rb

https://github.com/ghar/rails
Ruby | 639 lines | 526 code | 109 blank | 4 comment | 6 complexity | 1361790d1db3084633fe0f9b1fc63122 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
  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. end
  410. class CallbackObject
  411. def before(caller)
  412. caller.record << "before"
  413. end
  414. def before_save(caller)
  415. caller.record << "before save"
  416. end
  417. def around(caller)
  418. caller.record << "around before"
  419. yield
  420. caller.record << "around after"
  421. end
  422. end
  423. class UsingObjectBefore
  424. include ActiveSupport::Callbacks
  425. define_callbacks :save
  426. set_callback :save, :before, CallbackObject.new
  427. attr_accessor :record
  428. def initialize
  429. @record = []
  430. end
  431. def save
  432. run_callbacks :save do
  433. @record << "yielded"
  434. end
  435. end
  436. end
  437. class UsingObjectAround
  438. include ActiveSupport::Callbacks
  439. define_callbacks :save
  440. set_callback :save, :around, CallbackObject.new
  441. attr_accessor :record
  442. def initialize
  443. @record = []
  444. end
  445. def save
  446. run_callbacks :save do
  447. @record << "yielded"
  448. end
  449. end
  450. end
  451. class CustomScopeObject
  452. include ActiveSupport::Callbacks
  453. define_callbacks :save, :scope => [:kind, :name]
  454. set_callback :save, :before, CallbackObject.new
  455. attr_accessor :record
  456. def initialize
  457. @record = []
  458. end
  459. def save
  460. run_callbacks :save do
  461. @record << "yielded"
  462. "CallbackResult"
  463. end
  464. end
  465. end
  466. class UsingObjectTest < Test::Unit::TestCase
  467. def test_before_object
  468. u = UsingObjectBefore.new
  469. u.save
  470. assert_equal ["before", "yielded"], u.record
  471. end
  472. def test_around_object
  473. u = UsingObjectAround.new
  474. u.save
  475. assert_equal ["around before", "yielded", "around after"], u.record
  476. end
  477. def test_customized_object
  478. u = CustomScopeObject.new
  479. u.save
  480. assert_equal ["before save", "yielded"], u.record
  481. end
  482. def test_block_result_is_returned
  483. u = CustomScopeObject.new
  484. assert_equal "CallbackResult", u.save
  485. end
  486. end
  487. class CallbackTerminatorTest < Test::Unit::TestCase
  488. def test_termination
  489. terminator = CallbackTerminator.new
  490. terminator.save
  491. assert_equal ["first", "second", "third", "second", "first"], terminator.history
  492. end
  493. def test_block_never_called_if_terminated
  494. obj = CallbackTerminator.new
  495. obj.save
  496. assert !obj.saved
  497. end
  498. end
  499. class HyphenatedKeyTest < Test::Unit::TestCase
  500. def test_save
  501. obj = HyphenatedCallbacks.new
  502. obj.save
  503. assert_equal "ACTION", obj.stuff
  504. end
  505. end
  506. class WriterSkipper < Person
  507. attr_accessor :age
  508. skip_callback :save, :before, :before_save_method, :if => lambda {self.age > 21}
  509. end
  510. class WriterCallbacksTest < Test::Unit::TestCase
  511. def test_skip_writer
  512. writer = WriterSkipper.new
  513. writer.age = 18
  514. assert_equal [], writer.history
  515. writer.save
  516. assert_equal [
  517. [:before_save, :symbol],
  518. [:before_save, :string],
  519. [:before_save, :proc],
  520. [:before_save, :object],
  521. [:before_save, :block],
  522. [:after_save, :block],
  523. [:after_save, :object],
  524. [:after_save, :proc],
  525. [:after_save, :string],
  526. [:after_save, :symbol]
  527. ], writer.history
  528. end
  529. end
  530. end