def on_change(opts={}, &block)

      query = {
        'feed' => 'continuous',
        'heartbeat' => opts[:heartbeat] || 20_000 }
        #'since' => 0 } # that's already the default
      query['include_docs'] = true if block.arity > 2
      query = query.map { |k, v| "#{k}=#{v}" }.join('&')

      socket = TCPSocket.open(@http.host, @http.port)

      auth = @http.options[:basic_auth]

      if auth
        auth = Base64.encode64(auth.join(':')).strip
        auth = "Authorization: Basic #{auth}\r\n"
      else
        auth = ''
      end

      socket.print("GET /#{path}/_changes?#{query} HTTP/1.1\r\n")
      socket.print("User-Agent: rufus-jig #{Rufus::Jig::VERSION}\r\n")
      #socket.print("Accept: application/json;charset=UTF-8\r\n")
      socket.print(auth)
      socket.print("\r\n")

      # consider reply

      answer = socket.gets.strip
      status = answer.match(/^HTTP\/.+ (\d{3}) /)[1].to_i

      raise Rufus::Jig::HttpError.new(status, answer) if status != 200

      # discard headers

      loop do
        data = socket.gets
        break if data.nil? || data == "\r\n"
      end

      # the on_change loop

      loop do
        data = socket.gets
        break if data.nil?
        data = (Rufus::Json.decode(data) rescue nil)
        next unless data.is_a?(Hash)
        args = [ data['id'], (data['deleted'] == true) ]
        args << data['doc'] if block.arity > 2
        block.call(*args)
      end

      on_change(opts, &block) if opts[:reconnect]
    end
def on_change(opts={}, &block)

      query = {
        'feed' => 'continuous',
        'heartbeat' => opts[:heartbeat] || 20_000 }
        #'since' => 0 } # that's already the default
      query['include_docs'] = true if block.arity > 2
      query = query.map { |k, v| "#{k}=#{v}" }.join('&')

      socket = TCPSocket.open(@http.host, @http.port)

      auth = @http.options[:basic_auth]

      if auth
        auth = Base64.encode64(auth.join(':')).strip
        auth = "Authorization: Basic #{auth}\r\n"
      else
        auth = ''
      end

      socket.print("GET /#{path}/_changes?#{query} HTTP/1.1\r\n")
      socket.print("User-Agent: rufus-jig #{Rufus::Jig::VERSION}\r\n")
      #socket.print("Accept: application/json;charset=UTF-8\r\n")
      socket.print(auth)
      socket.print("\r\n")

      # consider reply

      answer = socket.gets.strip
      status = answer.match(/^HTTP\/.+ (\d{3}) /)[1].to_i

      raise Rufus::Jig::HttpError.new(status, answer) if status != 200

      # discard headers

      loop do
        data = socket.gets
        break if data.nil? || data == "\r\n"
      end

      # the on_change loop

      loop do
        data = socket.gets
        break if data.nil?
        data = (Rufus::Json.decode(data) rescue nil)
        next unless data.is_a?(Hash)
        args = [ data['id'], (data['deleted'] == true) ]
        args << data['doc'] if block.arity > 2
        block.call(*args)
      end

      on_change(opts, &block) if opts[:reconnect]
    end
Watches the database for changes.

   db.on_change do |doc_id, deleted|
     puts "doc #{doc_id} has been #{deleted ? 'deleted' : 'changed'}"
   end

   db.on_change do |doc_id, deleted, doc|
     puts "doc #{doc_id} has been #{deleted ? 'deleted' : 'changed'}"
     p doc
   end

 This is a blocking method. One might want to wrap it inside of a Thread.

 Note : doc inclusion (third parameter to the block) only works with
 CouchDB >= 0.11.