Skip to content

Instantly share code, notes, and snippets.

@will3216
Created July 1, 2015 20:22
Show Gist options
  • Save will3216/a44c9ad3a18d69abaf67 to your computer and use it in GitHub Desktop.
Save will3216/a44c9ad3a18d69abaf67 to your computer and use it in GitHub Desktop.
Log information about callbacks for the Lifecycle of a Rails 3.2.19 (Wraps state-machine callbacks as well if state-machine is being used)
# This is used for debugging callbacks chains, to use this code:
# 1. Drop this into an initializer in your rails app
# 2. Set the LG_FILE_PATH to the file location you want the logging to go too
# 3. Set MODEL_TO_DEBUG to the class you want to debug, ie: MODEL_TO_DEBUG
# 4. Change the print_string method to save the relevant information. It has
# access to any instance methods on the object presently in the callback chain
# Note: Not sure how compatible this is with other versions of rails/state-machine
# VERSIONS PATCHED
# State-Machine 1.1.2
# Rails 3.2.19
unless Rails.env.production? || Rails.env.prod?
# Set these values!
MODEL_TO_DEBUG = MyClass
TAG_FOR_PRINT = 'IAmTrackingObjectLifeCycle'
LOG_FILE_PATH = "the/path/I/want/to/log.to"
file = File.open(LOG_FILE_PATH, "a")
TRACKING_LOGGER = Logger.new(file)
module CallbackPrint
# Modify this method! (Don't change the arguments unless you change them in all the places it is called!)
def print_string(kind, method, result, halted=nil)
print_hash = {
tag: TAG_FOR_PRINT,
timestamp: Time.now.to_f,
callback: method,
kind: kind,
result: result.inspect,
#some_column_name: instance_method_to_call,
#example: state,
#my_class_uid: self.uid,
staged_changes: changes,
previous_changes: previous_changes
}
print_hash.merge!({halted: halted}) unless halted.nil?
TRACKING_LOGGER.info print_hash.to_json
result
end
end
MODEL_TO_DEBUG.send(:include, CallbackPrint)
module ActiveSupport
module Callbacks
class Callback
def start(key=nil, object=nil)
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
case @kind
when :before
<<-RUBY_EVAL
if !halted && #{@compiled_options}
# This double assignment is to prevent warnings in 1.9.3 as
# the `result` variable is not always used except if the
# terminator code refers to it.
if self.class.to_s == "#{MODEL_TO_DEBUG.name}" && (#{@raw_filter.class.inspect} == Symbol || #{@raw_filter.class.inspect} == String)
self.print_string #{@kind.inspect.inspect}, #{@raw_filter.inspect.inspect}, (result = result = #{@filter})
else
result = result = #{@filter}
end
halted = (#{chain.config[:terminator]})
if halted
if self.class.to_s == "#{MODEL_TO_DEBUG.name}" && (#{@raw_filter.class.inspect} == Symbol || #{@raw_filter.class.inspect} == String)
self.print_string(#{@kind.inspect.inspect}, #{@raw_filter.inspect.inspect}, result, halted)
end
halted_callback_hook(#{@raw_filter.inspect.inspect})
end
end
RUBY_EVAL
when :around
name = "_conditional_callback_#{@kind}_#{next_id}"
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}(halted)
if #{@compiled_options} && !halted
#{@filter} do
#puts #{self.instance_variables.inject({}){|h,v| h[v] = self.instance_variable_get(v) unless [:@chain, :@klass].include?(v) || v.class == StateMachine::Machine; h}.inspect.inspect}
if self.class.to_s == "#{MODEL_TO_DEBUG.name}" && (#{@raw_filter.class.inspect} == Symbol || #{@raw_filter.class.inspect} == String)
self.print_string(#{@kind.inspect.inspect}, #{@raw_filter.inspect.inspect}, (yield self), halted)
else
yield self
end
end
else
if self.class.to_s == "#{MODEL_TO_DEBUG.name}" && (#{@raw_filter.class.inspect} == Symbol || #{@raw_filter.class.inspect} == String)
self.print_string(#{@kind.inspect.inspect}, #{@raw_filter.inspect.inspect}, (yield self), halted)
else
yield self
end
end
end
RUBY_EVAL
"#{name}(halted) do"
end
end
def end(key=nil, object=nil)
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
case @kind
when :after
<<-RUBY_EVAL
if #{@compiled_options}
if self.class.to_s == "#{MODEL_TO_DEBUG.name}" && (#{@raw_filter.class.inspect} == Symbol || #{@raw_filter.class.inspect} == String)
self.print_string #{@kind.inspect.inspect}, #{@raw_filter.inspect.inspect}, #{@filter}, halted
else
#{@filter}
end
end
RUBY_EVAL
when :around
<<-RUBY_EVAL
if self.class.to_s == "#{MODEL_TO_DEBUG.name}" && (#{@raw_filter.class.inspect} == Symbol || #{@raw_filter.class.inspect} == String)
self.print_string #{@kind.inspect.inspect}, #{@raw_filter.inspect.inspect}, value, halted
else
value
end
end
RUBY_EVAL
end
end
end
end
end
if MODEL_TO_DEBUG.respond_to?(:state_machine)
module StateMachine
module EvalHelpers
def evaluate_method(object, method, *args, &block)
klass = (class << object; self; end)
case method
when Symbol
if klass < MODEL_TO_DEBUG
object.send(:print_string, type.inspect, method, (object.method(method).arity == 0 ? object.send(method, &block) : object.send(method, *args, &block)))
else
object.method(method).arity == 0 ? object.send(method, &block) : object.send(method, *args, &block)
end
when Proc, Method
args.unshift(object)
arity = method.arity
if block_given? && Proc === method && arity != 0
if [1, 2].include?(arity)
args = args[0, arity - 1] + [block]
else
args << block
end
else
args = args[0, arity] if [0, 1].include?(arity)
end
if klass < MODEL_TO_DEBUG
object.send(:print_string, type.inspect, method, method.call(*args, &block))
else
method.call(*args, &block)
end
when String
if klass < MODEL_TO_DEBUG
# penis penis penis
object.send(:print_string, type.inspect, method, eval(method, object.instance_eval {binding}, &block))
else
eval(method, object.instance_eval {binding}, &block)
end
else
raise ArgumentError, 'Methods must be a symbol denoting the method to call, a block to be invoked, or a string to be evaluated'
end
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment