File: parser.rb

package info (click to toggle)
ruby-httparty 0.13.7-1%2Bdeb9u1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 736 kB
  • sloc: ruby: 4,741; xml: 425; sh: 35; makefile: 11
file content (143 lines) | stat: -rw-r--r-- 3,680 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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,
      '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 supports_format?
        parse_supported_format
      else
        body
      end
    end

    protected

    def xml
      MultiXml.parse(body)
    end

    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