読者です 読者をやめる 読者になる 読者になる

ウェブサービスを作っています。

トラックバック先の AutoDiscovery

ruby trackback

RDF に基づいて、渡されたパーマリンクに対するトラックバック Ping URL を自動検出します。
参考: トラックバック技術仕様書
トラックバック関連の gem ってないんですかね。


Timeout::Error も捕捉しています。

require 'net/http'
require 'uri'
Net::HTTP.version_1_2

class Trackback
  def self.discover(url)
    uri = URI(url)

    begin
      Net::HTTP.start(uri.host, uri.port) { |http|
        req = Net::HTTP::Get.new(uri.request_uri)
        req['User-Agent'] = 'TrackBack/1.0'

        http.request(req) { |response|
          return nil unless response.code == '200'

          content = response.body
          url_no_anchor = url.sub(/#.*$/, '')
          content.scan(%r!<rdf:RDF.*?</rdf:RDF>!m) do |rdf|
            perm_url = (rdf =~ /dc:identifier="([^"]+)"/m) ? $1 : nil
            next unless perm_url == url || perm_url == url_no_anchor
            if rdf =~ /trackback:ping="([^"]+)"/m
              return $1
            elsif rdf =~ /about="([^"]+)"/m
              return $1
            end
          end
        }
      }
    rescue Timeout::Error
    end

    nil
  end
end


テスト。書きづらいです。FakeWeb を使っています。

  describe '::discover' do
    def fakeweb_stub(name)
      path = File.expand_path("../fakeweb_stubs/#{ name }.html", File.dirname(__FILE__))
      open(path) { |f| f.read }
    end

    before { FakeWeb.clean_registry }

    it 'は、RDF に基づいて、トラックバック先を自動検出すること' do
      FakeWeb.register_uri(:get, 'http://d.hatena.ne.jp/milk1000cc/20090507/1241702656',
        :string => fakeweb_stub(:article))
      Trackback.discover('http://d.hatena.ne.jp/milk1000cc/20090507/1241702656').should ==
        'http://d.hatena.ne.jp/milk1000cc/20090507/1241702656/tb'
      Trackback.discover('http://d.hatena.ne.jp/milk1000cc/20090507/1241702656#fuga').should ==
        'http://d.hatena.ne.jp/milk1000cc/20090507/1241702656/tb'

      FakeWeb.register_uri(:get, 'http://d.hatena.ne.jp/milk1000cc/20090507/1241702657',
        :string => fakeweb_stub(:article2))
      Trackback.discover('http://d.hatena.ne.jp/milk1000cc/20090507/1241702657').should ==
        'http://d.hatena.ne.jp/milk1000cc/20090507/1241702657/abt'
      Trackback.discover('http://d.hatena.ne.jp/milk1000cc/20090507/1241702657#fuga').should ==
        'http://d.hatena.ne.jp/milk1000cc/20090507/1241702657/abt'

      FakeWeb.register_uri(:get, 'http://d.hatena.ne.jp/milk1000cc/',
        :string => fakeweb_stub(:index))
      Trackback.discover('http://d.hatena.ne.jp/milk1000cc/').should be_nil
      Trackback.discover('http://d.hatena.ne.jp/milk1000cc/#fuga').should be_nil
    end

    it 'は、HTTP ステータスコードが 200 以外ならば、nil を返すこと' do
      FakeWeb.register_uri(:get, 'http://example.com/404', :string => 'Not Found', :status => ['404', 'Not Found'])
      Trackback.discover('http://example.com/404').should be_nil
    end

    it 'は、タイムアウトしたら nil を返すこと' do
      Net::HTTP.stub!(:start).and_raise(Timeout::Error.new('execution expired'))
      Trackback.discover('http://example.com').should be_nil
    end
  end