EM::DefaultDeferrable の使い道

というわけで
http://d.hatena.ne.jp/eagletmt/20110211/1297413439

class HtmlDocument
  include EM::Deferrable
  ...

で良いのであった…


では EM::DefaultDeferrable は何のために提供されているのかというと

DefaultDeferrable is an otherwise empty class that includes Deferrable. This is very useful when you just need to return a Deferrable object as a way of communicating deferred status to some other part of a program.

http://eventmachine.rubyforge.org/EventMachine/DefaultDeferrable.html

とあるので,単に完了/失敗を伝えたいときに使うとよさそう.
em-http-request のようにクライアント用途で EventMachine を使うと,いつ EM.stop するかがめんどくさかったりするので,例えばこういう使い方があるのかもしれない.

#!/usr/bin/ruby
# coding: utf-8
require 'em-http'
require 'nokogiri'

class HtmlDocument
  include EM::Deferrable
  attr_reader :http, :parser

  def initialize(http)
    @http = http
  end

  def succeed
    @parser = Nokogiri::HTML.parse @http.response
    super
  end
end

def get_document(uri)
  http = EM::HttpRequest.new(uri).get
  doc = HtmlDocument.new http
  http.callback { doc.succeed }
  http.errback { doc.fail }
  doc
end

EM.run do
  multi = EM::MultiRequest.new

  [
    'http://www.google.co.jp/',
    'http://www.hatena.ne.jp/h/o/g/e',
    'http://eagletmt.com/',
  ].each do |uri|
    defer = EM::DefaultDeferrable.new
    multi.add defer
    doc = get_document uri
    doc.errback do
      puts "ng: #{doc.http.error}: #{doc.http.uri}"
      defer.fail
    end
    doc.callback do
      puts "ok: #{doc.http.uri}: #{doc.http.response_header.status}: #{doc.parser.xpath('//title').inner_text}"
      m = EM::MultiRequest.new
      doc.parser.xpath('//a[@href]').each do |a|
        href = a['href']
        if href.start_with? 'http://'
          doc2 = get_document href
          doc2.errback do
            puts "  ng: #{doc2.http.error}: #{doc2.http.uri}"
          end
          doc2.callback do
            puts "  ok: #{doc2.http.uri}: #{doc2.http.response_header.status}: #{doc2.parser.xpath('//title').inner_text}"
          end
          m.add doc2
        end
      end
      if m.requests.empty?
        defer.succeed
      else
        m.callback do
          defer.succeed
        end
      end
    end
  end

  multi.callback do
    EM.stop
  end
end