Friday, March 10, 2006

So Sweet with Ruby

The Ruby programming language can be so sweet. (Ok, this is a bit off-topic, but still cool Web development subject.) I wanted to do some custom Web log analysis recently using Ruby, and it just came out so nicely, a clean object-oriented class in no more than ten lines of code, and a trivial filter. Ruby Structs, regular expressions, and language conveniences to the rescue! I just had to put it on display.

First, a Struct named WebLog to contain the data from the regular expression match:

Struct.new("WebLog", :all, :host, :ident, :user, :time_string,
           :method, :url, :http, :stat, :size, :ref, :ua)
This defines accessors for each named field so you can use it rather like a C struct, e.g. log.host, log.stat.

Then a subclass, with the regular expression tucked away in a class variable. (This RegExp expects "combined" log format.)

class WebLog < Struct::WebLog
  #  host  ident user   time      method url  http   stat  size ref       ua
  @@expr =
   /^(.*?) (.*?) (.*?) \[(.*?)\] "(.*?) (.*?) (.*?)" (.*?) (.*?) "(.*?)" "(.*?)"$/

  def initialize(line)
    super(*@@expr.match(line))
  end
end

The initialize method runs as part of every call to WebLog.new. It calls super, which invokes the Struct::WebLog initializer, and passes all of the match data resulting for the given line. The result is an object with the match data parsed into field.

In the code, @@expr means a class variable named expr, shared by all instances of the class. The result of @@expr.match works like an array: the superclass initialize method expects arguments for each member of the struct, and the * operator in a method call unpacks any array-like structure into an argument list.

The rest of the code runs a custom log analysis. ARGF contains all the lines of all the files on the command line, or if none are give it contains all the lines of standard input. This creates a WebLog object for each line and prints its user agent string (ua). How much neater could it be?

ARGF.each do |line|
  matches = WebLog.new(line)
  puts matches.ua
end

1 Comments:

At May 02, 2012 2:17 AM, Blogger Cris Monde said...

I thought that's literally sweet! Thanks for sharing this information to us. Hope to see more of your post here!

Cris | Website Developer Philippines

 

Post a Comment

Links to this post:

Create a Link

<< Home