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