EntryFullText もどき

"もどき" にも値しないかもしれないが。寄り道でも、もう少し進んでみた。
extract_after_hook は、ブロックで実装していたけれど、eval に変更した。
ブロックで実装するほうが自然な気がするけれど、今回は気にしない。

形は整ったから Praggerプラグインに乗せてみるかな。

require 'rubygems'
require 'hpricot'

def extract_text(text, opt)
  # argument no check

  keys = opt[:extract_capture].split(/\s/)  if opt.key?(:extract_capture)

  data = []

  text.scan(/#{opt[:extract]}/m) { |r|
    item = {}

    if opt.key?(:extract_capture)
      1.upto(keys.size) {|i| 
        item[keys[i-1].to_sym] = Regexp.last_match(i)
      }
    end

    if opt.key?(:extract_xpath)
      doc = Hpricot(r)

      opt[:extract_xpath].each {|key, val| 
        elem = doc.at(val)
        if elem.nil?
          item[key] = ''
        else
          item[key] = elem.to_s
        end
      }
    end

    if opt.key?(:extract_after_hook)
      eval(opt[:extract_after_hook])
    end

    data << item 
  }

  data
end
  • サンプル

というかテストコード

require 'test/unit'
require 'extract_text'

class Test_extract_text < Test::Unit::TestCase
  def setup
    @text =<<EOD
<root>
  <record>
    <item>
      <name>user01</name>
      <id>0</id>
      <date>2007/11/25</date>
      <comment>comment01 foobar</comment>
    </item>
    <item>
      <name>user02</name>
      <id>1</id>
      <date>2007/11/28</date>
      <comment>comment02 foobar</comment>
    </item>
    <item>
      <name>user03</name>
      <id>2</id>
      <date>2007/11/30</date>
      <comment>comment03 foobar</comment>
    </item>
  </record>
</root>
EOD
  end

  def teardown
  end

  def test_extract_after_hook_conf
    ok_data = [
      {:name    => "user01",
       :date    => "2007/11/25",
       :comment => "comment01 foobar",
       :id      => "0"},
      {:name    => "user02",
       :date    => "2007/11/28",
       :comment => "comment02 foobar",
       :id      => "1"},
      {:name    => "user03",
       :date    => "2007/11/30",
       :comment => "comment03 foobar",
       :id      => "2"}
    ]

    data = extract_text(@text,
      {
        :extract       => %q{<item>.+?</item>},
        :extract_xpath => {
          :name    => %q{//name},
          :date    => %q{//date},
          :id      => %q{//id},
          :comment => %q{//comment},
        },
        :extract_after_hook => <<-'EOD'
          item[:name]    = item[:name].match(/>(.+?)<\//).to_a[1]
          item[:date]    = item[:date].match(/>(.+?)<\//).to_a[1]
          item[:id]      = item[:id].match(/>(.+?)<\//).to_a[1]
          item[:comment] = item[:comment].match(/>(.+?)<\//).to_a[1]
        EOD
      }
    )

    assert_instance_of(Array, data)
    assert_equal(3, data.size)

    0.upto(data.size - 1) {|i|
      assert_instance_of(Hash, data[i])
      assert_equal(ok_data[i][:name], data[i][:name])
      assert_equal(ok_data[i][:date], data[i][:date])
      assert_equal(ok_data[i][:comment], data[i][:comment])
      assert_equal(ok_data[i][:id], data[i][:id])
    }
  end
end