fork download
  1. #!/usr/bin/env ruby
  2. # -*- coding: utf-8 -*-
  3.  
  4. module Callbacks
  5. def self.included(base)
  6. base.extend ClassMethods
  7. end
  8.  
  9. module ClassMethods
  10. def define_callbacks(*names, &block)
  11. names.each do |name|
  12. unless method_defined?("#{name}_with_callback")
  13. define_method(:"#{name}_with_callback") do |*args, &block|
  14. _run_callbacks(:"#{name}", [:before, :around], *args, &block)
  15. result = send(:"#{name}_without_callback", *args, &block)
  16. _run_callbacks(:"#{name}", [:around, :after], *args, &block)
  17. result
  18. end
  19. alias_method :"#{name}_without_callback", :"#{name}"
  20. alias_method :"#{name}", :"#{name}_with_callback"
  21. end
  22. end
  23. end
  24. end
  25.  
  26. def _callbacks
  27. @_callbacks ||= {}
  28. end
  29. private :_callbacks
  30.  
  31. def _run_callbacks(name, types, *args, &block)
  32. named_callbacks = _callbacks[name] || {}
  33. types.each do |type|
  34. (named_callbacks[type] || []).each do |callback|
  35. callback.call(*args, &block)
  36. end
  37. end
  38. end
  39. private :_run_callbacks
  40.  
  41. def _insert_callbacks(names, type, block, options = {})
  42. prepend = options[:prepend]
  43. names.each do |name|
  44. named_callbacks = (_callbacks[:"#{name}"] ||= {})
  45. callbacks = (named_callbacks[type] ||= [])
  46. if prepend
  47. callbacks.unshift block
  48. else
  49. callbacks.push block
  50. end
  51. end
  52. end
  53. private :_insert_callbacks
  54.  
  55. def _remove_callbacks(names, type, block)
  56. names.each do |name|
  57. named_callbacks = (_callbacks[:"#{name}"] ||= {})
  58. callbacks = (named_callbacks[type] ||= [])
  59. callbacks.delete block
  60. end
  61. end
  62. private :_remove_callbacks
  63.  
  64. [:before, :after, :around].each do |type|
  65. define_method(:"#{type}_filter") do |*names, &block|
  66. _insert_callbacks(names, type, block)
  67. end
  68.  
  69. define_method(:"prepend_#{type}_filter") do |*names, &block|
  70. _insert_callbacks(names, type, block, :prepend => true)
  71. end
  72.  
  73. define_method(:"remove_#{type}_filter") do |*names, &block|
  74. _remove_callbacks(names, type, block)
  75. end
  76.  
  77. alias_method :"append_#{type}_filter", :"#{type}_filter"
  78. end
  79. end
  80.  
  81.  
  82. class Record
  83. include Callbacks
  84.  
  85. def save(v)
  86. puts "#{v}: - save"
  87. end
  88. define_callbacks :save
  89. end
  90.  
  91. record = Record.new
  92. record.before_filter :save do |v|
  93. puts "#{v}: saving..."
  94. end
  95. record.after_filter :save do |v|
  96. puts "#{v}: saved"
  97. end
  98.  
  99. record.save 123
  100.  
  101.  
Success #stdin #stdout 0s 4896KB
stdin
Standard input is empty
stdout
123: saving...
123: - save
123: saved