サイト更新チェッカークラスのプロトタイプ

self.site_url にチェックしたい URL を設定し、
self.prepare で前処理をし(今は何もしていない)、
self.process で BODY 部から正規表現でデータを抽出し、
self.after で抽出したデータを加工(スペースを消したり)する、といったクラスをUpdateChecker::Base のサブクラスで定義する。


self.fetch で上記の定義どおりに処理をして、データを抽出するようになっている。
self.save は今のところは CSV で保存するようにしている。


とりあえず今のところはこんな感じなんだけれど、いろいろと足りない。
一つのまとまったものになるのは、もう少し先になりそうだ。

#!/usr/bin/ruby

require 'open-uri'
require 'rubygems'
require 'fastercsv'

class UpdateChecker
  class Base
    @@instances = {}

    def self.site_url=(url)
      @@instances[self] = {}  unless @@instances.key?(self)
      @@instances[self][:site_url] = url
    end

    def self.prepare(&block)
      @@instances[self] = {}  unless @@instances.key?(self)
      @@instances[self][:prepare] = block
    end

    def self.process(&block)
      @@instances[self] = {}  unless @@instances.key?(self)
      @@instances[self][:process] = block
    end

    def self.fetch
      return []  unless @@instances.key?(self)

      contents = ''
      begin
        open(@@instances[self][:site_url]) do |http|
          contents = http.read
        end
      rescue Exception => e
        # TODO: OpenURI::HTTPError
        puts "#{@@instances[self][:site_url]} ... #{e.message}"
      end

      records = Extract.scan(contents, &@@instances[self][:process])

      @@instances[self][:records] = records.map do |entry|
        @@instances[self][:after].call(entry)
        entry
      end

      self
    end

    def self.after(&block)
      @@instances[self] = {}  unless @@instances.key?(self)
      @@instances[self][:after] = block
    end

    def self.save(filename)
      return false  unless @@instances.key?(self)

      FasterCSV.open(filename, "w") do |csv|
        @@instances[self][:records].each do |entry|
            puts entry
          csv << entry.values
        end
      end

      true
    end
  end

  class Extract
    attr_accessor :records, :fields

    def initialize(source)
      @source = source
      @records = []
      @fields = {}
    end

    def self.scan(source, &block)
      extract = self.new(source)

      begin
        # TODO: 
        while 1
          extract.fields = {}
          extract.instance_eval(&block)
          break  if extract.fields.empty?

          extract.records << extract.fields
        end
      rescue Exception => e
        puts e
      end

      extract.records
    end

    def method_missing(name, *argv)
      # TODO: 
      reg = argv[0] + '(.*)'
      val, @source = %r{#{reg}}m.match(@source).to_a.values_at(1, 2)
      @fields[name] = val  unless val.nil?
    end
  end
end

class Onsen < UpdateChecker::Base
  self.site_url = 'http://localhost/feed/onsen.html'

  process do
    title '<td width="135">(.+?)<\/td>'
    link '(http://[^\s]+?asx)'
  end

  after do |entry|
    entry[:title] = entry[:title].gsub(/<[^<>]*>/m, '').strip
  end
end

p onsen = Onsen.fetch
onsen.save('onsen.csv')