/lib/httparty/parser.rb
http://github.com/jnunemaker/httparty · Ruby · 150 lines · 78 code · 18 blank · 54 comment · 9 complexity · e74fd24fcca1565accb183d539e51588 MD5 · raw file
- module HTTParty
- # The default parser used by HTTParty, supports xml, json, html, csv and
- # plain text.
- #
- # == Custom Parsers
- #
- # If you'd like to do your own custom parsing, subclassing HTTParty::Parser
- # will make that process much easier. There are a few different ways you can
- # utilize HTTParty::Parser as a superclass.
- #
- # @example Intercept the parsing for all formats
- # class SimpleParser < HTTParty::Parser
- # def parse
- # perform_parsing
- # end
- # end
- #
- # @example Add the atom format and parsing method to the default parser
- # class AtomParsingIncluded < HTTParty::Parser
- # SupportedFormats.merge!(
- # {"application/atom+xml" => :atom}
- # )
- #
- # def atom
- # perform_atom_parsing
- # end
- # end
- #
- # @example Only support the atom format
- # class ParseOnlyAtom < HTTParty::Parser
- # SupportedFormats = {"application/atom+xml" => :atom}
- #
- # def atom
- # perform_atom_parsing
- # end
- # end
- #
- # @abstract Read the Custom Parsers section for more information.
- class Parser
- SupportedFormats = {
- 'text/xml' => :xml,
- 'application/xml' => :xml,
- 'application/json' => :json,
- 'application/vnd.api+json' => :json,
- 'application/hal+json' => :json,
- 'text/json' => :json,
- 'application/javascript' => :plain,
- 'text/javascript' => :plain,
- 'text/html' => :html,
- 'text/plain' => :plain,
- 'text/csv' => :csv,
- 'application/csv' => :csv,
- 'text/comma-separated-values' => :csv
- }
- # The response body of the request
- # @return [String]
- attr_reader :body
- # The intended parsing format for the request
- # @return [Symbol] e.g. :json
- attr_reader :format
- # Instantiate the parser and call {#parse}.
- # @param [String] body the response body
- # @param [Symbol] format the response format
- # @return parsed response
- def self.call(body, format)
- new(body, format).parse
- end
- # @return [Hash] the SupportedFormats hash
- def self.formats
- const_get(:SupportedFormats)
- end
- # @param [String] mimetype response MIME type
- # @return [Symbol]
- # @return [nil] mime type not supported
- def self.format_from_mimetype(mimetype)
- formats[formats.keys.detect {|k| mimetype.include?(k)}]
- end
- # @return [Array<Symbol>] list of supported formats
- def self.supported_formats
- formats.values.uniq
- end
- # @param [Symbol] format e.g. :json, :xml
- # @return [Boolean]
- def self.supports_format?(format)
- supported_formats.include?(format)
- end
- def initialize(body, format)
- @body = body
- @format = format
- end
- # @return [Object] the parsed body
- # @return [nil] when the response body is nil, an empty string, spaces only or "null"
- def parse
- return nil if body.nil?
- return nil if body == "null"
- return nil if body.valid_encoding? && body.strip.empty?
- if body.valid_encoding? && body.encoding == Encoding::UTF_8
- @body = body.gsub(/\A#{UTF8_BOM}/, '')
- end
- if supports_format?
- parse_supported_format
- else
- body
- end
- end
- protected
- def xml
- MultiXml.parse(body)
- end
- UTF8_BOM = "\xEF\xBB\xBF".freeze
- def json
- JSON.parse(body, :quirks_mode => true, :allow_nan => true)
- end
- def csv
- CSV.parse(body)
- end
- def html
- body
- end
- def plain
- body
- end
- def supports_format?
- self.class.supports_format?(format)
- end
- def parse_supported_format
- send(format)
- rescue NoMethodError => e
- raise NotImplementedError, "#{self.class.name} has not implemented a parsing method for the #{format.inspect} format.", e.backtrace
- end
- end
- end