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