Retrying an Exception in Ruby

Fri Oct 28 00:00:00 -0700 2011
When dealing with over-the-wire services, I've found it helpful to retry certain errors automatically. This small helper makes it easy!

mass_assignment v1.0 is a gem

Wed Apr 13 00:00:00 -0700 2011

For two years now I've been using my mass_assignment plugin to improve the security and readability of my Ruby on Rails code. Today I decided to get with the program and make it a gem. Since it's been stable and production-tested for a long time now I think it deserves the 1.0 version.

What It Is

A robust mass assignment method with a small and obvious syntax.

The Traditional Approach

The normal mass assignment protection comes from attr_protected and attr_accessible. There are a few problems with this approach:

  • Often never implemented, leaving a wide-open system. Rails blogs are full of dire warnings about forgetting your attr_protected.
  • Once implemented, easy to forget when adding new attributes, leading to bugs (in an attr_accessible system) or security holes (in an attr_protected system).
  • Restricts coding syntax. You can’t easily use update_attributes() or attributes= because your whitelist/blacklist gets in your own way.
  • Not contextual. The list of allowed attributes can’t change to accomodate different user permissions or situations.

The MassAssignment Approach

This plugin’s solution is to let you specify an obvious and explicit list of allowed attributes when you mass assign attributes.

  • The list of allowed attributes is in your controller at calltime, so it’s easier to remember and update (it’s not a hidden, magical system).
  • The list of allowed attributes is optional, so it doesn’t get in your way. You can use update_attributes() and attributes= for your own code again.
  • Assignment permissions are enforced by the controller, where permissions belong. You can evaluate the current user or current situation and write the whitelist on the fly.

See the README on github for examples and more.

Rails Log Parsing with Timber

Fri Jul 23 00:00:00 -0700 2010

I’m needing to do more analysis of my production logs, and at every turn I see “requires Hodel 3000 Complaint blah blah”. But that’s not what I have — I have standard logs! And thanks to buffered logging, they are quite parseable as they are.

http://github.com/cainlevy/timber

This fresh plugin provides simple objects to parse a production log into requests and extract whatever data you need. By default it extracts controller, method, and request time. But if you have special data in your log files that you want to analyze, it’s quite easy to add.

For example, I want to use Oink to analyze which requests most frequently bloat my worker thread sizes. Here’s a snip that adds Oink parsing to the Request class:

module Timber
  module Plugins

    module Oink
      # create simple :pid attribute
      attr_reader :pid

      # create memory attribute, but return as integer
      def memory
        @_memory ||= @memory ? @memory.to_i : nil
      end
    end
  end
  
  # add to Request
  Request.class_eval {include Plugins::Oink}

  # add the regexp => vars pattern for the Oink log format
  Request::PATTERNS[/Memory usage: (\d+) \| PID: (\d+)/] = [:memory, :pid]
end

With that, I can rewrite the Oink log parser to offload the log parsing to Timber and simplify it down to its own reporting logic:

module Oink
  class MemoryUsageReporter < Base
    def print(output)
      output.puts "---- MEMORY THRESHOLD ----"
      output.puts "THRESHOLD: #{@threshold/1024} MB\n"
      output.puts "\n-- REQUESTS --\n" if @format == :verbose

      @inputs.each do |input|
        logfile = Timber::Log.new(input)
        while request = logfile.shift
          if pid = @pids[request.pid] and request.memory
            delta = request.memory - pid[:last_memory_reading]

            if delta > @threshold
              @bad_actions[request.action] ||= 0
              @bad_actions[request.action] += 1
              @bad_requests.push(OinkedMemoryRequest.new(request.action, request.time, request.lines, delta))

              if @format == :verbose
                request.lines.each { |b| output.puts b }
                output.puts "---------------------------------------------------------------------"
              end
            end

          else
            @pids[request.pid] = {:last_memory_reading => request.memory}
          end
        end
      end

      print_summary(output)
    end
  end
end