Skip to content

Instantly share code, notes, and snippets.

@jonmagic
Last active August 29, 2015 14:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonmagic/231ad687f6f1d0c46101 to your computer and use it in GitHub Desktop.
Save jonmagic/231ad687f6f1d0c46101 to your computer and use it in GitHub Desktop.
module Addon
def self.included(base)
base.class_eval do
before_save :update_addon
end
end
def update_addon
puts "updating addon"
end
end
class Base
def self.callback_chain
@callback_chain ||= Hash.new {|k,v| k[v] = [] }
end
def self.before_save(callback_method_symbol, options = nil)
callback_chain[:before_save] << {callback_method_symbol => options || {}}
end
def self.after_save(callback_method_symbol, options = nil)
callback_chain[:after_save] << {callback_method_symbol => options || {}}
end
end
module CallbackResearch
ARCA_CALLBACK_FINDER_REGEXP = /\A(.+)\:(\d+)\:in\s`.+'\z/
private_constant :ARCA_CALLBACK_FINDER_REGEXP
def self.included(base)
callback_file_path, = caller[0].partition(":")
base.class_eval do
define_singleton_method(:callback_research_data) do
@callback_research_data ||= Hash.new {|k,v| k[v] = [] }
end
[:before_save, :after_save].each do |callback_symbol|
callback_method = singleton_class.instance_method(callback_symbol)
define_singleton_method(callback_method.name) do |*args|
line = caller.first
callback_file_path, callback_line_number = ARCA_CALLBACK_FINDER_REGEXP.match(line)[1..2]
caller.each do |line|
if match = /\A(.+):\d+:in\s`<class:#{name.split("::").last}>'/.match(line)
self.callback_research_data[:model_file_path] = match[1]
break
end
end
options = args.dup
callback_research_data[callback_symbol] << {
options.shift => (options.last || {}).merge({
:callback_file_path => callback_file_path,
:callback_line_number => callback_line_number.to_i,
:model_class_name => name
})
}
callback_method.bind(self).call(*args)
end
end
end
end
end
class Foo < Base
def initialize
@instance_state = "hello world"
end
before_save :blang
def save
self.class.callback_chain.each do |key, value|
value.each do |callback_details|
puts "#{key}: #{callback_details}, #{@instance_state}"
end
end
end
end
require_relative "base"
require_relative "callback_research"
class Base
include CallbackResearch
end
require_relative "foo"
require_relative "bar"
puts "analyzing Foo"
puts Foo.callback_research_data
puts ""
puts "analyzing Bar"
puts Sub::Bar.callback_research_data
puts ""
puts "testing Foo"
Foo.new.save
puts ""
puts "testing Bar"
Sub::Bar.new.save
@jonmagic
Copy link
Author

jonmagic commented Aug 8, 2015

$ ruby runner.rb 
analyzing Foo
{:model_file_path=>"/Users/jonmagic/Projects/ruby-composition/foo.rb", :before_save=>[{:blang=>{:callback_file_path=>"/Users/jonmagic/Projects/ruby-composition/foo.rb", :callback_line_number=>6, :model_class_name=>"Foo"}}]}

analyzing Bar
{:model_file_path=>"/Users/jonmagic/Projects/ruby-composition/sub/bar.rb", :before_save=>[{:update_addon=>{:callback_file_path=>"/Users/jonmagic/Projects/ruby-composition/addon.rb", :callback_line_number=>4, :model_class_name=>"Sub::Bar"}}], :after_save=>[{:ding=>{:if=>:dong?, :callback_file_path=>"/Users/jonmagic/Projects/ruby-composition/sub/bar.rb", :callback_line_number=>7, :model_class_name=>"Sub::Bar"}}]}

testing Foo
before_save: {:blang=>{}}, hello world

testing Bar
before_save: {:update_addon=>{}}, 
after_save: {:ding=>{:if=>:dong?}}, 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment