From: Matthew Moss Date: 2008-07-07T04:00:50+09:00 Subject: Re: Statistician II (#168) Here's my own solution for this quiz (which I made sure I could do reasonably before posting the quiz!). Very similar in appearance to Matthias' solution above. As a pastie: http://pastie.org/228598 require 'ostruct' module Statistician class Reportable < OpenStruct def Reportable.inherited(klass) # Give each individual Reportable some instance data. # Doing it this way ensures each klass gets it's own rules/ records, rather # than sharing amongst all Reportables. klass.instance_eval %{ @reportable_rules = [] @reportable_records = [] } end # Class methods def self.rule(str) r = Rule.new(str) @reportable_rules << r end def self.match(str) data = nil if @reportable_rules.find { |rule| data = rule.match(str) } return data end end def self.records @reportable_records end # Helpers class Rule def initialize(str) patt = Regexp.escape(str).gsub('\[', '(?:').gsub('\]', ')?').gsub(/<(.+?)>/, '(.+?)') @pattern = Regexp.new("^#{patt}$") @fields = str.scan(/<(.+?)>/).flatten.map { |f| f.to_sym } end def match(str) if md = @pattern.match(str) Hash[*@fields.zip(md.captures).flatten] else nil end end end end # class Reportable class Reporter attr_reader :unmatched def initialize(*reportables) @reportables = reportables @unmatched = [] end def parse(text) text.each do |line| line.strip! data = nil if reportable = @reportables.find { |k| data = k.match(line) } reportable.records << reportable.new(data) else @unmatched << line end end end end # class Reporter end # module Statistician