Ruby Metaprogramming - Subclassable Callback Module


/ Published in: Ruby
Save to your folder(s)



Copy this code and paste it in your HTML
  1. require 'rubygems'
  2. require 'active_model'
  3.  
  4. # use this if you want to create before and after filters around a commonly used
  5. # method, but still want to define the method in subclasses.
  6. module SubclassableCallbacks
  7. def self.included(base)
  8. base.extend ClassMethods
  9. end
  10.  
  11. module ClassMethods
  12. attr_accessor :subclassable_callbacks
  13.  
  14. def subclassable_callbacks(*methods)
  15. @subclassable_callbacks ||= []
  16.  
  17. unless methods.empty?
  18. @subclassable_callbacks = @subclassable_callbacks.concat(methods).flatten.compact.map(&:to_sym).uniq
  19. define_model_callbacks *methods
  20. class_eval do
  21. methods.each do |variable|
  22. define_method "implementation_#{variable.to_s}" do; end
  23. define_method variable do
  24. send("_run_#{variable.to_s}_callbacks") do
  25. send("implementation_#{variable.to_s}")
  26. end
  27. end
  28. alias_method "superclass_#{variable.to_s}", variable
  29. end
  30. end
  31. end
  32.  
  33. @subclassable_callbacks
  34. end
  35. end
  36.  
  37. def self.override
  38. ObjectSpace.each_object(Class) do |object|
  39. if object.ancestors.include?(SubclassableCallbacks)
  40. object.class_eval do
  41. methods = object.subclassable_callbacks
  42. last_index = object.superclass.ancestors.index(SubclassableCallbacks)
  43. if last_index && last_index > 0
  44. superclass_methods = object.superclass.ancestors[0..last_index - 1].map(&:subclassable_callbacks)
  45. methods = methods.concat(superclass_methods).flatten.uniq
  46. end
  47. methods.each do |name|
  48. alias_method "subclass_#{name}", "#{name}"
  49. alias_method "implementation_#{name}", "subclass_#{name}"
  50. alias_method "#{name}", "superclass_#{name}"
  51. end
  52. end
  53. end
  54. end
  55. end
  56. end
  57.  
  58. class BaseModel
  59. extend ActiveModel::Callbacks
  60. include SubclassableCallbacks
  61.  
  62. subclassable_callbacks :save
  63.  
  64. before_save { puts "[save:before]"}
  65. after_save { puts "[save:after]"}
  66. end
  67.  
  68. class SomeModel < BaseModel
  69. def save
  70. puts "[saving some_model...]"
  71. true
  72. end
  73. end
  74.  
  75. class AnotherModel < BaseModel
  76. def save
  77. puts "[saving another_model...]"
  78. true
  79. end
  80. end
  81.  
  82. SubclassableCallbacks.override
  83.  
  84. SomeModel.new.save
  85. #=> [save:before]
  86. #=> [saving some_model...]
  87. #=> [save:after]
  88. AnotherModel.new.save
  89. #=> [save:before]
  90. #=> [saving another_model...]
  91. #=> [save:after]

URL: ruby-metaprogramming-subclassable-callback-module

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.