Sam Saffron
Sam Saffron

Reputation: 131112

Why does this method fail to instrument ActionView::Template :render?

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

Answers (1)

tadman
tadman

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

Related Questions