class RETS::Base::Core

Constants

GET_OBJECT_DATA

Attributes

request_hash[R]

Can be called after any {RETS::Base::Core} call that hits the RETS Server. @return [String] SHA1 hash of the request

request_size[R]

Can be called after any {RETS::Base::Core} call that hits the RETS Server. @return [String] How big the request was

request_time[R]

Can be called after any {#get_object} or {#search} call that hits the RETS Server. @return [Float] How long the request took

rets_data[R]

Can be called after any {RETS::Base::Core} call that hits the RETS Server. @return [Hash]

Gives access to the miscellaneous RETS data, such as reply text, code, delimiter, count and so on depending on the API call made.
* *text* (String) - Reply text from the server
* *code* (String) - Reply code from the server

Public Class Methods

new(http, urls) click to toggle source
# File lib/rets/base/core.rb, line 26
def initialize(http, urls)
  @http = http
  @urls = urls
end

Public Instance Methods

get_metadata(args, &block) click to toggle source

Requests metadata from the RETS server.

@param [Hash] args @option args [String] :type Metadata to request, the same value if you were manually making the request, “METADATA-SYSTEM”, “METADATA-CLASS” and so on @option args [String] :id Filter the data returned by ID, “*” would return all available data @option args [Integer, Optional] :read_timeout How many seconds to wait before giving up

@yield For every group of metadata downloaded @yieldparam [String] :type Type of data that was parsed with “METADATA-” stripped out, for “METADATA-SYSTEM” this will be “SYSTEM” @yieldparam [Hash] :attrs Attributes of the data, generally Version, Date and Resource but can vary depending on what metadata you requested @yieldparam [Array] :metadata Array of hashes with metadata info

@raise [RETS::CapabilityNotFound] @raise [RETS::APIError] @raise [RETS::HTTPError] @see rets_data @see request_size @see request_hash

# File lib/rets/base/core.rb, line 75
def get_metadata(args, &block)
  raise ArgumentError, "No block passed" unless block_given?

  unless @urls[:getmetadata]
    raise RETS::CapabilityNotFound.new("No GetMetadata capability found for given user.")
  end

  @request_size, @request_hash, @request_time, @rets_data = nil, nil, nil, nil
  @http.request(:url => @urls[:getmetadata], :read_timeout => args[:read_timeout], :params => {:Format => :COMPACT, :Type => args[:type], :ID => args[:id]}) do |response|
    stream = RETS::StreamHTTP.new(response)
    sax = RETS::Base::SAXMetadata.new(block)

    Nokogiri::XML::SAX::Parser.new(sax).parse_io(stream)

    @request_size, @request_hash = stream.size, stream.hash
    @rets_data = sax.rets_data
  end

  nil
end
get_object(args) { |parsed_headers| ... } click to toggle source

Requests an object from the RETS server.

@param [Hash] args @option args [String] :resource Resource to load, typically Property @option args [String] :type Type of object you want, usually Photo @option args [String] :id What objects to return @option args [Array, Optional] :accept Array of MIME types to accept, by default this is image/png, image/gif and image/jpeg @option args [Boolean, Optional] :location Return the location of the object rather than the contents of it @option args [Integer, Optional] :read_timeout How many seconds to wait before timing out

@yield For every object downloaded @yieldparam [Hash] :headers Object headers

* *object-id* (String) - Objects ID
* *content-id* (String) - Content ID
* *content-type* (String) - MIME type of the content
* *description* (String, Optional) - A description of the object
* *location* (String, Optional) - Where the file is located, only returned is *location* is true

@yieldparam [String, Optional] :content Content for the object, not called when location is set

@raise [RETS::CapabilityNotFound] @raise [RETS::APIError] @raise [RETS::HTTPError] @see rets_data @see request_size @see request_hash

# File lib/rets/base/core.rb, line 122
def get_object(args, &block)
  raise ArgumentError, "No block passed" unless block_given?

  unless @urls[:getobject]
    raise RETS::CapabilityNotFound.new("No GetObject capability found for given user.")
  end

  req = {:url => @urls[:getobject], :read_timeout => args[:read_timeout], :headers => {}}
  req[:params] = {:Resource => args[:resource], :Type => args[:type], :Location => (args[:location] ? 1 : 0), :ID => args[:id]}
  if args[:accept].is_a?(Array)
    req[:headers]["Accept"] = args[:accept].join(",")
  else
    req[:headers]["Accept"] = "image/png,image/gif,image/jpeg"
  end

  # Will get swapped to a streaming call rather than a download-and-parse later, easy to do as it's called with a block now
  start = Time.now.utc.to_f

  @request_size, @request_hash, @request_time, @rets_data = nil, nil, nil, nil
  @http.request(req) do |response|
    body = response.read_body

    @request_time = Time.now.utc.to_f - start
    @request_size, @request_hash = body.length, Digest::SHA1.hexdigest(body)

    # Make sure we aren't erroring
    if body =~ /(<RETS (.+)\>)/
      # RETSIQ (and probably others) return a <RETS> tag on a location request without any error inside
      # since parsing errors out of full image data calls is a tricky pain. We're going to keep the
      # loose error checking, but will confirm that it has an actual error code
      code, text = @http.get_rets_response(Nokogiri::XML($1).xpath("//RETS").first)
      unless code == "0"
        @rets_data = {:code => code, :text => text}

        if code == "20403"
          return
        else
          raise RETS::APIError.new("#{code}: #{text}", code, text)
        end
      end
    end

    # Using a wildcard somewhere
    if response.content_type == "multipart/parallel"
      boundary = response.type_params["boundary"]
      boundary.gsub!(/^"|"$/, "")

      parts = body.split("--#{boundary}\r\n")
      parts.last.gsub!("\r\n--#{boundary}--", "")
      parts.each do |part|
        part.strip!
        next if part == ""

        headers, content = part.split("\r\n\r\n", 2)

        parsed_headers = {}
        headers.split("\r\n").each do |line|
          name, value = line.split(":", 2)
          next unless value and value != ""

          parsed_headers[name.downcase] = value.strip
        end

        # Check off the first children because some Rap Rets seems to use RETS-Status
        # and it will include it with an object while returning actual data.
        # It only does this for multipart requests, single image pulls will use <RETS> like it should.
        if parsed_headers["content-type"] == "text/xml"
          code, text = @http.get_rets_response(Nokogiri::XML(content).children.first)
          next if code == "20403"
        end

        if block.arity == 1
          yield parsed_headers
        else
          yield parsed_headers, content
        end

      end

    # Either text (error) or an image of some sorts, which is irrelevant for this
    else
      headers = {}
      GET_OBJECT_DATA.each do |field|
        next unless response.header[field] and response.header[field] != ""
        headers[field] = response.header[field].strip
      end

      if block.arity == 1
        yield headers
      else
        yield headers, body
      end
    end
  end

  nil
end
has_capability?(type) click to toggle source

Whether the RETS server has the requested capability.

@param [Symbol] type Lowercase of the capability, “getmetadata”, “getobject” and so on @return [Boolean]

# File lib/rets/base/core.rb, line 52
def has_capability?(type)
  @urls.has_key?(type)
end
logout() click to toggle source

Attempts to logout of the RETS server.

@raise [RETS::CapabilityNotFound] @raise [RETS::APIError] @raise [RETS::HTTPError]

# File lib/rets/base/core.rb, line 37
def logout
  unless @urls[:logout]
    raise RETS::CapabilityNotFound.new("No Logout capability found for given user.")
  end

  @http.request(:url => @urls[:logout])

  nil
end