I’ve got a couple Rails controllers that just serve static content like graphics and script assets. In production the content is all cached so it bypasses the controller but when I’m running locally I’ve got pages and pages of garbage filling up my development log.
The first attempt at silencing the whining controllers used an around_filter that looked something like this:1 2 3 4 5 6 7 8 9 |
class GraphicsController < ApplicationController around_filter :no_logging protected def no_logging logger.silence { yield } end end |
which is great, compact, easy to encapsulate, etc. But it doesn’t work. Debugging told me I needed to use a different logger object and put this code in a different place. I share this next piece of code not out of pride but as an example that I, like Adam Keys, am a student of the art of abysmal failure. Here’s my original implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# in ./lib/exempt_controllers_from_logging_entirely.rb module ExemptControllersFromLoggingEntirely @@controllers_to_exclude = %w(graphics styles scripts) def self.included(base) base.class_eval do def process_with_no_logging(request, *args) if @@controllers_to_exclude.any? {|controller| request.parameters[:controller] == controller } logger.silence { process_without_no_logging(request, *args) } else process_without_no_logging(request, *args) end end alias_method_chain :process, :no_logging end end end ActionController::Base.send :include, ExemptControllersFromLoggingEntirely |
Yay! It’s HUGE! And it uses all the unnecessary complexities of alias_method_chain and self.included and, oh joy, the controllers that use it are specified RIGHT IN THE CODE.
Sadly, this code worked fine. Ruby lacks an ugly-code warning system that tells me when I’m writing something unwieldly.
Here’s the refactored version:1 2 3 4 5 6 7 8 9 10 11 |
# in ./lib/exempted_from_logging.rb module ExemptedFromLogging def process(request, *args) logger.silence { super } end end ## Usage: ## class GraphicsController < ApplicationController ## include ExemptedFromLogging ## end |
Yeah.
Lesson?
- If you’re just overwriting a single method like this put it in a module that calls super
- If you’re bypassing a method entirely then you can think about aliasing or overwriting the original
- Avoid using self.included and self.inherited unless you’ve got something complex to do like adding both instance and class methods to an object
- Remember to revisit your old code
- Blog your failures for fun and
profithumility