Reputation: 131112
We are porting MiniProfiler to Ruby and wanted to add auto instrumentation for Rails views and partials.
I am aware of the existing instrumentation support but ideally want to get "start" and "stop" events. We would also like to support earlier versions of Rails that do not have notification support.
I hacked up a short instrumenter and tested hooking up render with before and after calls:
def prof(klass, method)
with_profiling = (method.to_s + "_with_profiling").intern
without_profiling = (method.to_s + "_without_profiling").intern
klass.send :alias_method, without_profiling, method
klass.send :define_method, with_profiling do |*args, &orig|
puts "before #{method} #{args}"
self.send without_profiling, *args, &orig
puts "after #{method}"
end
klass.send :alias_method, method, with_profiling
end
prof ActionView::Template, :render
However, once this is activated render
is not instrumented properly. In particular this works on some of the partials, but explodes with a:
ActionView::Template::Error (undefined method `html_safe' for nil:NilClass)
What is wrong with this method hook? What is a proper robust way to hook methods so they are not fragile to this problem (maintaining the trivial API: prof klass, method
)
Upvotes: 2
Views: 454
Reputation: 211580
You've redefined the method to return the result of your final puts
which is usually nil
. A fix might be:
klass.send :define_method, with_profiling do |*args, &orig|
puts "before #{method} #{args}"
result = self.send without_profiling, *args, &rig
puts "after #{method}"
result
end
Upvotes: 2