PageRenderTime 80ms CodeModel.GetById 27ms RepoModel.GetById 2ms app.codeStats 0ms

/activesupport/test/callbacks_test.rb

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