jnunemaker-httparty 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/History CHANGED
@@ -1,3 +1,7 @@
1
+ == 0.4.0 2009-03-29
2
+ * 1 minor change
3
+ * Switched xml and json parsing to crack (same code as before just moved to gem for easier reuse in other projects)
4
+
1
5
  == 0.3.1 2009-02-10
2
6
  * 1 minor fix, 1 minor enhancement
3
7
  * Fixed unescaping umlauts (siebertm)
data/Manifest CHANGED
@@ -19,13 +19,10 @@ features/steps/remote_service_steps.rb
19
19
  features/supports_redirection.feature
20
20
  History
21
21
  httparty.gemspec
22
- lib/core_extensions.rb
23
22
  lib/httparty/cookie_hash.rb
23
+ lib/httparty/core_extensions.rb
24
24
  lib/httparty/exceptions.rb
25
25
  lib/httparty/module_inheritable_attributes.rb
26
- lib/httparty/parsers/json.rb
27
- lib/httparty/parsers/xml.rb
28
- lib/httparty/parsers.rb
29
26
  lib/httparty/request.rb
30
27
  lib/httparty/response.rb
31
28
  lib/httparty/version.rb
@@ -43,8 +40,6 @@ spec/fixtures/twitter.xml
43
40
  spec/fixtures/undefined_method_add_node_for_nil.xml
44
41
  spec/hash_spec.rb
45
42
  spec/httparty/cookie_hash_spec.rb
46
- spec/httparty/parsers/json_spec.rb
47
- spec/httparty/parsers/xml_spec.rb
48
43
  spec/httparty/request_spec.rb
49
44
  spec/httparty/response_spec.rb
50
45
  spec/httparty_spec.rb
data/README CHANGED
@@ -28,6 +28,7 @@ options. Below is an example of how easy it is.
28
28
 
29
29
  == REQUIREMENTS:
30
30
 
31
+ * Crack http://github.com/jnunemaker/crack/ - For XML and JSON parsing.
31
32
  * You like to party!
32
33
 
33
34
  == INSTALL:
data/Rakefile CHANGED
@@ -14,6 +14,7 @@ Echoe.new(ProjectName, HTTParty::Version) do |p|
14
14
  p.url = "http://#{ProjectName}.rubyforge.org"
15
15
  p.author = "John Nunemaker"
16
16
  p.email = "nunemaker@gmail.com"
17
+ p.extra_deps = [['jnunemaker-crack', '>= 0.1.0']]
17
18
  p.need_tar_gz = false
18
19
  p.docs_host = WebsitePath
19
20
  end
data/httparty.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{httparty}
5
- s.version = "0.3.1"
5
+ s.version = "0.4.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["John Nunemaker"]
9
- s.date = %q{2009-02-10}
9
+ s.date = %q{2009-03-29}
10
10
  s.default_executable = %q{httparty}
11
11
  s.description = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
12
12
  s.email = %q{nunemaker@gmail.com}
@@ -27,11 +27,14 @@ Gem::Specification.new do |s|
27
27
  s.specification_version = 2
28
28
 
29
29
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
30
+ s.add_runtime_dependency(%q<jnunemaker-crack>, [">= 0.1.0"])
30
31
  s.add_development_dependency(%q<echoe>, [">= 0"])
31
32
  else
33
+ s.add_dependency(%q<jnunemaker-crack>, [">= 0.1.0"])
32
34
  s.add_dependency(%q<echoe>, [">= 0"])
33
35
  end
34
36
  else
37
+ s.add_dependency(%q<jnunemaker-crack>, [">= 0.1.0"])
35
38
  s.add_dependency(%q<echoe>, [">= 0"])
36
39
  end
37
40
  end
data/lib/httparty.rb CHANGED
@@ -2,8 +2,10 @@ $:.unshift(File.dirname(__FILE__))
2
2
 
3
3
  require 'net/http'
4
4
  require 'net/https'
5
- require 'core_extensions'
6
5
  require 'httparty/module_inheritable_attributes'
6
+ require 'rubygems'
7
+ gem 'crack'
8
+ require 'crack'
7
9
 
8
10
  module HTTParty
9
11
 
@@ -195,7 +197,7 @@ module HTTParty
195
197
  end
196
198
 
197
199
  require 'httparty/cookie_hash'
200
+ require 'httparty/core_extensions'
198
201
  require 'httparty/exceptions'
199
202
  require 'httparty/request'
200
203
  require 'httparty/response'
201
- require 'httparty/parsers'
@@ -107,9 +107,9 @@ module HTTParty
107
107
  return nil if body.nil? or body.empty?
108
108
  case format
109
109
  when :xml
110
- HTTParty::Parsers::XML.parse(body)
110
+ Crack::XML.parse(body)
111
111
  when :json
112
- HTTParty::Parsers::JSON.decode(body)
112
+ Crack::JSON.parse(body)
113
113
  when :yaml
114
114
  YAML::load(body)
115
115
  else
@@ -1,3 +1,3 @@
1
1
  module HTTParty #:nodoc:
2
- Version = '0.3.1'
2
+ Version = '0.4.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jnunemaker-httparty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
@@ -9,11 +9,22 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-10 00:00:00 -08:00
12
+ date: 2009-03-29 00:00:00 -07:00
13
13
  default_executable: httparty
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: jnunemaker-crack
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.0
24
+ version:
15
25
  - !ruby/object:Gem::Dependency
16
26
  name: echoe
27
+ type: :development
17
28
  version_requirement:
18
29
  version_requirements: !ruby/object:Gem::Requirement
19
30
  requirements:
@@ -1,175 +0,0 @@
1
- require 'time'
2
-
3
- # Copyright (c) 2008 Sam Smoot.
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining
6
- # a copy of this software and associated documentation files (the
7
- # "Software"), to deal in the Software without restriction, including
8
- # without limitation the rights to use, copy, modify, merge, publish,
9
- # distribute, sublicense, and/or sell copies of the Software, and to
10
- # permit persons to whom the Software is furnished to do so, subject to
11
- # the following conditions:
12
- #
13
- # The above copyright notice and this permission notice shall be
14
- # included in all copies or substantial portions of the Software.
15
- #
16
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
-
24
- class Object #:nodoc:
25
- # @return <TrueClass, FalseClass>
26
- #
27
- # @example [].blank? #=> true
28
- # @example [1].blank? #=> false
29
- # @example [nil].blank? #=> false
30
- #
31
- # Returns true if the object is nil or empty (if applicable)
32
- def blank?
33
- nil? || (respond_to?(:empty?) && empty?)
34
- end unless method_defined?(:blank?)
35
- end # class Object
36
-
37
- class Numeric #:nodoc:
38
- # @return <TrueClass, FalseClass>
39
- #
40
- # Numerics can't be blank
41
- def blank?
42
- false
43
- end unless method_defined?(:blank?)
44
- end # class Numeric
45
-
46
- class NilClass #:nodoc:
47
- # @return <TrueClass, FalseClass>
48
- #
49
- # Nils are always blank
50
- def blank?
51
- true
52
- end unless method_defined?(:blank?)
53
- end # class NilClass
54
-
55
- class TrueClass #:nodoc:
56
- # @return <TrueClass, FalseClass>
57
- #
58
- # True is not blank.
59
- def blank?
60
- false
61
- end unless method_defined?(:blank?)
62
- end # class TrueClass
63
-
64
- class FalseClass #:nodoc:
65
- # False is always blank.
66
- def blank?
67
- true
68
- end unless method_defined?(:blank?)
69
- end # class FalseClass
70
-
71
- class String #:nodoc:
72
- # @example "".blank? #=> true
73
- # @example " ".blank? #=> true
74
- # @example " hey ho ".blank? #=> false
75
- #
76
- # @return <TrueClass, FalseClass>
77
- #
78
- # Strips out whitespace then tests if the string is empty.
79
- def blank?
80
- strip.empty?
81
- end unless method_defined?(:blank?)
82
-
83
- def snake_case
84
- return self.downcase if self =~ /^[A-Z]+$/
85
- self.gsub(/([A-Z]+)(?=[A-Z][a-z]?)|\B[A-Z]/, '_\&') =~ /_*(.*)/
86
- return $+.downcase
87
- end unless method_defined?(:snake_case)
88
- end # class String
89
-
90
- class Hash #:nodoc:
91
- # @return <String> This hash as a query string
92
- #
93
- # @example
94
- # { :name => "Bob",
95
- # :address => {
96
- # :street => '111 Ruby Ave.',
97
- # :city => 'Ruby Central',
98
- # :phones => ['111-111-1111', '222-222-2222']
99
- # }
100
- # }.to_params
101
- # #=> "name=Bob&address[city]=Ruby Central&address[phones][]=111-111-1111&address[phones][]=222-222-2222&address[street]=111 Ruby Ave."
102
- def to_params
103
- params = self.map { |k,v| normalize_param(k,v) }.join
104
- params.chop! # trailing &
105
- params
106
- end
107
-
108
- # @param key<Object> The key for the param.
109
- # @param value<Object> The value for the param.
110
- #
111
- # @return <String> This key value pair as a param
112
- #
113
- # @example normalize_param(:name, "Bob Jones") #=> "name=Bob%20Jones&"
114
- def normalize_param(key, value)
115
- param = ''
116
- stack = []
117
-
118
- if value.is_a?(Array)
119
- param << value.map { |element| normalize_param("#{key}[]", element) }.join
120
- elsif value.is_a?(Hash)
121
- stack << [key,value]
122
- else
123
- param << "#{key}=#{URI.encode(value.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}&"
124
- end
125
-
126
- stack.each do |parent, hash|
127
- hash.each do |key, value|
128
- if value.is_a?(Hash)
129
- stack << ["#{parent}[#{key}]", value]
130
- else
131
- param << normalize_param("#{parent}[#{key}]", value)
132
- end
133
- end
134
- end
135
-
136
- param
137
- end
138
-
139
- # @return <String> The hash as attributes for an XML tag.
140
- #
141
- # @example
142
- # { :one => 1, "two"=>"TWO" }.to_xml_attributes
143
- # #=> 'one="1" two="TWO"'
144
- def to_xml_attributes
145
- map do |k,v|
146
- %{#{k.to_s.snake_case.sub(/^(.{1,1})/) { |m| m.downcase }}="#{v}"}
147
- end.join(' ')
148
- end
149
- end
150
-
151
- class BlankSlate #:nodoc:
152
- instance_methods.each { |m| undef_method m unless m =~ /^__/ }
153
- end
154
-
155
- # 1.8.6 has mistyping of transitive in if statement
156
- require "rexml/document"
157
- module REXML #:nodoc:
158
- class Document < Element #:nodoc:
159
- def write( output=$stdout, indent=-1, transitive=false, ie_hack=false )
160
- if xml_decl.encoding != "UTF-8" && !output.kind_of?(Output)
161
- output = Output.new( output, xml_decl.encoding )
162
- end
163
- formatter = if indent > -1
164
- if transitive
165
- REXML::Formatters::Transitive.new( indent, ie_hack )
166
- else
167
- REXML::Formatters::Pretty.new( indent, ie_hack )
168
- end
169
- else
170
- REXML::Formatters::Default.new( ie_hack )
171
- end
172
- formatter.write( self, output )
173
- end
174
- end
175
- end
@@ -1,4 +0,0 @@
1
- Dir[File.dirname(__FILE__) + "/parsers/*.rb"].sort.each do |path|
2
- filename = File.basename(path)
3
- require "httparty/parsers/#{filename}"
4
- end
@@ -1,74 +0,0 @@
1
- # Copyright (c) 2004-2008 David Heinemeier Hansson
2
- # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
3
- # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
4
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
5
-
6
- require 'yaml'
7
- require 'strscan'
8
-
9
- module HTTParty
10
- module Parsers #:nodoc:
11
- module JSON #:nodoc:
12
- class ParseError < StandardError #:nodoc:
13
- end
14
-
15
- def self.decode(json)
16
- YAML.load(unescape(convert_json_to_yaml(json)))
17
- rescue ArgumentError => e
18
- raise ParseError, "Invalid JSON string"
19
- end
20
-
21
- protected
22
-
23
- def self.unescape(str)
24
- str.gsub(/\\u([0-9a-f]{4})/) {
25
- [$1.hex].pack("U")
26
- }
27
- end
28
-
29
- # matches YAML-formatted dates
30
- DATE_REGEX = /^\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?$/
31
-
32
- # Ensure that ":" and "," are always followed by a space
33
- def self.convert_json_to_yaml(json) #:nodoc:
34
- scanner, quoting, marks, pos, times = StringScanner.new(json), false, [], nil, []
35
- while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
36
- case char = scanner[1]
37
- when '"', "'"
38
- if !quoting
39
- quoting = char
40
- pos = scanner.pos
41
- elsif quoting == char
42
- if json[pos..scanner.pos-2] =~ DATE_REGEX
43
- # found a date, track the exact positions of the quotes so we can remove them later.
44
- # oh, and increment them for each current mark, each one is an extra padded space that bumps
45
- # the position in the final YAML output
46
- total_marks = marks.size
47
- times << pos+total_marks << scanner.pos+total_marks
48
- end
49
- quoting = false
50
- end
51
- when ":",","
52
- marks << scanner.pos - 1 unless quoting
53
- end
54
- end
55
-
56
- if marks.empty?
57
- json.gsub(/\\\//, '/')
58
- else
59
- left_pos = [-1].push(*marks)
60
- right_pos = marks << json.length
61
- output = []
62
- left_pos.each_with_index do |left, i|
63
- output << json[left.succ..right_pos[i]]
64
- end
65
- output = output * " "
66
-
67
- times.each { |i| output[i-1] = ' ' }
68
- output.gsub!(/\\\//, '/')
69
- output
70
- end
71
- end
72
- end
73
- end
74
- end
@@ -1,209 +0,0 @@
1
- require 'rexml/parsers/streamparser'
2
- require 'rexml/parsers/baseparser'
3
- require 'rexml/light/node'
4
-
5
- # This is a slighly modified version of the XMLUtilityNode from
6
- # http://merb.devjavu.com/projects/merb/ticket/95 (has.sox@gmail.com)
7
- # It's mainly just adding vowels, as I ht cd wth n vwls :)
8
- # This represents the hard part of the work, all I did was change the
9
- # underlying parser.
10
- class REXMLUtilityNode #:nodoc:
11
- attr_accessor :name, :attributes, :children, :type
12
-
13
- def self.typecasts
14
- @@typecasts
15
- end
16
-
17
- def self.typecasts=(obj)
18
- @@typecasts = obj
19
- end
20
-
21
- def self.available_typecasts
22
- @@available_typecasts
23
- end
24
-
25
- def self.available_typecasts=(obj)
26
- @@available_typecasts = obj
27
- end
28
-
29
- self.typecasts = {}
30
- self.typecasts["integer"] = lambda{|v| v.nil? ? nil : v.to_i}
31
- self.typecasts["boolean"] = lambda{|v| v.nil? ? nil : (v.strip != "false")}
32
- self.typecasts["datetime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc}
33
- self.typecasts["date"] = lambda{|v| v.nil? ? nil : Date.parse(v)}
34
- self.typecasts["dateTime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc}
35
- self.typecasts["decimal"] = lambda{|v| v.nil? ? nil : BigDecimal(v.to_s)}
36
- self.typecasts["double"] = lambda{|v| v.nil? ? nil : v.to_f}
37
- self.typecasts["float"] = lambda{|v| v.nil? ? nil : v.to_f}
38
- self.typecasts["symbol"] = lambda{|v| v.nil? ? nil : v.to_sym}
39
- self.typecasts["string"] = lambda{|v| v.to_s}
40
- self.typecasts["yaml"] = lambda{|v| v.nil? ? nil : YAML.load(v)}
41
- self.typecasts["base64Binary"] = lambda{|v| v.unpack('m').first }
42
-
43
- self.available_typecasts = self.typecasts.keys
44
-
45
- def initialize(name, attributes = {})
46
- @name = name.tr("-", "_")
47
- # leave the type alone if we don't know what it is
48
- @type = self.class.available_typecasts.include?(attributes["type"]) ? attributes.delete("type") : attributes["type"]
49
-
50
- @nil_element = attributes.delete("nil") == "true"
51
- @attributes = undasherize_keys(attributes)
52
- @children = []
53
- @text = false
54
- end
55
-
56
- def add_node(node)
57
- @text = true if node.is_a? String
58
- @children << node
59
- end
60
-
61
- def to_hash
62
- if @type == "file"
63
- f = StringIO.new((@children.first || '').unpack('m').first)
64
- class << f
65
- attr_accessor :original_filename, :content_type
66
- end
67
- f.original_filename = attributes['name'] || 'untitled'
68
- f.content_type = attributes['content_type'] || 'application/octet-stream'
69
- return {name => f}
70
- end
71
-
72
- if @text
73
- return { name => typecast_value( translate_xml_entities( inner_html ) ) }
74
- else
75
- #change repeating groups into an array
76
- groups = @children.inject({}) { |s,e| (s[e.name] ||= []) << e; s }
77
-
78
- out = nil
79
- if @type == "array"
80
- out = []
81
- groups.each do |k, v|
82
- if v.size == 1
83
- out << v.first.to_hash.entries.first.last
84
- else
85
- out << v.map{|e| e.to_hash[k]}
86
- end
87
- end
88
- out = out.flatten
89
-
90
- else # If Hash
91
- out = {}
92
- groups.each do |k,v|
93
- if v.size == 1
94
- out.merge!(v.first)
95
- else
96
- out.merge!( k => v.map{|e| e.to_hash[k]})
97
- end
98
- end
99
- out.merge! attributes unless attributes.empty?
100
- out = out.empty? ? nil : out
101
- end
102
-
103
- if @type && out.nil?
104
- { name => typecast_value(out) }
105
- else
106
- { name => out }
107
- end
108
- end
109
- end
110
-
111
- # Typecasts a value based upon its type. For instance, if
112
- # +node+ has #type == "integer",
113
- # {{[node.typecast_value("12") #=> 12]}}
114
- #
115
- # @param value<String> The value that is being typecast.
116
- #
117
- # @details [:type options]
118
- # "integer"::
119
- # converts +value+ to an integer with #to_i
120
- # "boolean"::
121
- # checks whether +value+, after removing spaces, is the literal
122
- # "true"
123
- # "datetime"::
124
- # Parses +value+ using Time.parse, and returns a UTC Time
125
- # "date"::
126
- # Parses +value+ using Date.parse
127
- #
128
- # @return <Integer, TrueClass, FalseClass, Time, Date, Object>
129
- # The result of typecasting +value+.
130
- #
131
- # @note
132
- # If +self+ does not have a "type" key, or if it's not one of the
133
- # options specified above, the raw +value+ will be returned.
134
- def typecast_value(value)
135
- return value unless @type
136
- proc = self.class.typecasts[@type]
137
- proc.nil? ? value : proc.call(value)
138
- end
139
-
140
- # Convert basic XML entities into their literal values.
141
- #
142
- # @param value<#gsub> An XML fragment.
143
- #
144
- # @return <#gsub> The XML fragment after converting entities.
145
- def translate_xml_entities(value)
146
- value.gsub(/&lt;/, "<").
147
- gsub(/&gt;/, ">").
148
- gsub(/&quot;/, '"').
149
- gsub(/&apos;/, "'").
150
- gsub(/&amp;/, "&")
151
- end
152
-
153
- # Take keys of the form foo-bar and convert them to foo_bar
154
- def undasherize_keys(params)
155
- params.keys.each do |key, value|
156
- params[key.tr("-", "_")] = params.delete(key)
157
- end
158
- params
159
- end
160
-
161
- # Get the inner_html of the REXML node.
162
- def inner_html
163
- @children.join
164
- end
165
-
166
- # Converts the node into a readable HTML node.
167
- #
168
- # @return <String> The HTML node in text form.
169
- def to_html
170
- attributes.merge!(:type => @type ) if @type
171
- "<#{name}#{attributes.to_xml_attributes}>#{@nil_element ? '' : inner_html}</#{name}>"
172
- end
173
-
174
- # @alias #to_html #to_s
175
- def to_s
176
- to_html
177
- end
178
- end
179
-
180
- module HTTParty
181
- module Parsers #:nodoc:
182
- module XML #:nodoc:
183
- def self.parse(xml)
184
- stack = []
185
- parser = REXML::Parsers::BaseParser.new(xml)
186
-
187
- while true
188
- event = parser.pull
189
- case event[0]
190
- when :end_document
191
- break
192
- when :end_doctype, :start_doctype
193
- # do nothing
194
- when :start_element
195
- stack.push REXMLUtilityNode.new(event[1], event[2])
196
- when :end_element
197
- if stack.size > 1
198
- temp = stack.pop
199
- stack.last.add_node(temp)
200
- end
201
- when :text, :cdata
202
- stack.last.add_node(event[1]) unless event[1].strip.length == 0 || stack.empty?
203
- end
204
- end
205
- stack.pop.to_hash
206
- end
207
- end
208
- end
209
- end
@@ -1,42 +0,0 @@
1
- require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
-
3
- describe HTTParty::Parsers::JSON do
4
- TESTS = {
5
- %q({"data": "G\u00fcnter"}) => {"data" => "Günter"},
6
- %q({"returnTo":{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}},
7
- %q({returnTo:{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}},
8
- %q({"return\\"To\\":":{"\/categories":"\/"}}) => {"return\"To\":" => {"/categories" => "/"}},
9
- %q({"returnTo":{"\/categories":1}}) => {"returnTo" => {"/categories" => 1}},
10
- %({"returnTo":[1,"a"]}) => {"returnTo" => [1, "a"]},
11
- %({"returnTo":[1,"\\"a\\",", "b"]}) => {"returnTo" => [1, "\"a\",", "b"]},
12
- %({a: "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"},
13
- %({a: "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"},
14
- %({a: "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)},
15
- %({a: "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)},
16
- # no time zone
17
- %({a: "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"},
18
- %([]) => [],
19
- %({}) => {},
20
- %(1) => 1,
21
- %("") => "",
22
- %("\\"") => "\"",
23
- %(null) => nil,
24
- %(true) => true,
25
- %(false) => false,
26
- %q("http:\/\/test.host\/posts\/1") => "http://test.host/posts/1"
27
- }
28
-
29
- TESTS.each do |json, expected|
30
- it "should decode json (#{json})" do
31
- lambda {
32
- HTTParty::Parsers::JSON.decode(json).should == expected
33
- }.should_not raise_error
34
- end
35
- end
36
-
37
- it "should raise error for failed decoding" do
38
- lambda {
39
- HTTParty::Parsers::JSON.decode(%({: 1}))
40
- }.should raise_error(HTTParty::Parsers::JSON::ParseError)
41
- end
42
- end
@@ -1,445 +0,0 @@
1
- require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
-
3
- require "date"
4
- require 'bigdecimal'
5
-
6
- describe HTTParty::Parsers::XML, "#parse" do
7
- it "should transform a simple tag with content" do
8
- xml = "<tag>This is the contents</tag>"
9
- HTTParty::Parsers::XML.parse(xml).should == { 'tag' => 'This is the contents' }
10
- end
11
-
12
- it "should work with cdata tags" do
13
- xml = <<-END
14
- <tag>
15
- <![CDATA[
16
- text inside cdata
17
- ]]>
18
- </tag>
19
- END
20
- HTTParty::Parsers::XML.parse(xml)["tag"].strip.should == "text inside cdata"
21
- end
22
-
23
- it "should transform a simple tag with attributes" do
24
- xml = "<tag attr1='1' attr2='2'></tag>"
25
- hash = { 'tag' => { 'attr1' => '1', 'attr2' => '2' } }
26
- HTTParty::Parsers::XML.parse(xml).should == hash
27
- end
28
-
29
- it "should transform repeating siblings into an array" do
30
- xml =<<-XML
31
- <opt>
32
- <user login="grep" fullname="Gary R Epstein" />
33
- <user login="stty" fullname="Simon T Tyson" />
34
- </opt>
35
- XML
36
-
37
- HTTParty::Parsers::XML.parse(xml)['opt']['user'].should be_an_instance_of(Array)
38
-
39
- hash = {
40
- 'opt' => {
41
- 'user' => [{
42
- 'login' => 'grep',
43
- 'fullname' => 'Gary R Epstein'
44
- },{
45
- 'login' => 'stty',
46
- 'fullname' => 'Simon T Tyson'
47
- }]
48
- }
49
- }
50
-
51
- HTTParty::Parsers::XML.parse(xml).should == hash
52
- end
53
-
54
- it "should not transform non-repeating siblings into an array" do
55
- xml =<<-XML
56
- <opt>
57
- <user login="grep" fullname="Gary R Epstein" />
58
- </opt>
59
- XML
60
-
61
- HTTParty::Parsers::XML.parse(xml)['opt']['user'].should be_an_instance_of(Hash)
62
-
63
- hash = {
64
- 'opt' => {
65
- 'user' => {
66
- 'login' => 'grep',
67
- 'fullname' => 'Gary R Epstein'
68
- }
69
- }
70
- }
71
-
72
- HTTParty::Parsers::XML.parse(xml).should == hash
73
- end
74
-
75
- it "should typecast an integer" do
76
- xml = "<tag type='integer'>10</tag>"
77
- HTTParty::Parsers::XML.parse(xml)['tag'].should == 10
78
- end
79
-
80
- it "should typecast a true boolean" do
81
- xml = "<tag type='boolean'>true</tag>"
82
- HTTParty::Parsers::XML.parse(xml)['tag'].should be_true
83
- end
84
-
85
- it "should typecast a false boolean" do
86
- ["false"].each do |w|
87
- HTTParty::Parsers::XML.parse("<tag type='boolean'>#{w}</tag>")['tag'].should be_false
88
- end
89
- end
90
-
91
- it "should typecast a datetime" do
92
- xml = "<tag type='datetime'>2007-12-31 10:32</tag>"
93
- HTTParty::Parsers::XML.parse(xml)['tag'].should == Time.parse( '2007-12-31 10:32' ).utc
94
- end
95
-
96
- it "should typecast a date" do
97
- xml = "<tag type='date'>2007-12-31</tag>"
98
- HTTParty::Parsers::XML.parse(xml)['tag'].should == Date.parse('2007-12-31')
99
- end
100
-
101
- it "should unescape html entities" do
102
- values = {
103
- "<" => "&lt;",
104
- ">" => "&gt;",
105
- '"' => "&quot;",
106
- "'" => "&apos;",
107
- "&" => "&amp;"
108
- }
109
- values.each do |k,v|
110
- xml = "<tag>Some content #{v}</tag>"
111
- HTTParty::Parsers::XML.parse(xml)['tag'].should match(Regexp.new(k))
112
- end
113
- end
114
-
115
- it "should undasherize keys as tags" do
116
- xml = "<tag-1>Stuff</tag-1>"
117
- HTTParty::Parsers::XML.parse(xml).keys.should include( 'tag_1' )
118
- end
119
-
120
- it "should undasherize keys as attributes" do
121
- xml = "<tag1 attr-1='1'></tag1>"
122
- HTTParty::Parsers::XML.parse(xml)['tag1'].keys.should include( 'attr_1')
123
- end
124
-
125
- it "should undasherize keys as tags and attributes" do
126
- xml = "<tag-1 attr-1='1'></tag-1>"
127
- HTTParty::Parsers::XML.parse(xml).keys.should include( 'tag_1' )
128
- HTTParty::Parsers::XML.parse(xml)['tag_1'].keys.should include( 'attr_1')
129
- end
130
-
131
- it "should render nested content correctly" do
132
- xml = "<root><tag1>Tag1 Content <em><strong>This is strong</strong></em></tag1></root>"
133
- HTTParty::Parsers::XML.parse(xml)['root']['tag1'].should == "Tag1 Content <em><strong>This is strong</strong></em>"
134
- end
135
-
136
- it "should render nested content with split text nodes correctly" do
137
- xml = "<root>Tag1 Content<em>Stuff</em> Hi There</root>"
138
- HTTParty::Parsers::XML.parse(xml)['root'].should == "Tag1 Content<em>Stuff</em> Hi There"
139
- end
140
-
141
- it "should ignore attributes when a child is a text node" do
142
- xml = "<root attr1='1'>Stuff</root>"
143
- HTTParty::Parsers::XML.parse(xml).should == { "root" => "Stuff" }
144
- end
145
-
146
- it "should ignore attributes when any child is a text node" do
147
- xml = "<root attr1='1'>Stuff <em>in italics</em></root>"
148
- HTTParty::Parsers::XML.parse(xml).should == { "root" => "Stuff <em>in italics</em>" }
149
- end
150
-
151
- it "should correctly transform multiple children" do
152
- xml = <<-XML
153
- <user gender='m'>
154
- <age type='integer'>35</age>
155
- <name>Home Simpson</name>
156
- <dob type='date'>1988-01-01</dob>
157
- <joined-at type='datetime'>2000-04-28 23:01</joined-at>
158
- <is-cool type='boolean'>true</is-cool>
159
- </user>
160
- XML
161
-
162
- hash = {
163
- "user" => {
164
- "gender" => "m",
165
- "age" => 35,
166
- "name" => "Home Simpson",
167
- "dob" => Date.parse('1988-01-01'),
168
- "joined_at" => Time.parse("2000-04-28 23:01"),
169
- "is_cool" => true
170
- }
171
- }
172
-
173
- HTTParty::Parsers::XML.parse(xml).should == hash
174
- end
175
-
176
- it "should properly handle nil values (ActiveSupport Compatible)" do
177
- topic_xml = <<-EOT
178
- <topic>
179
- <title></title>
180
- <id type="integer"></id>
181
- <approved type="boolean"></approved>
182
- <written-on type="date"></written-on>
183
- <viewed-at type="datetime"></viewed-at>
184
- <content type="yaml"></content>
185
- <parent-id></parent-id>
186
- </topic>
187
- EOT
188
-
189
- expected_topic_hash = {
190
- 'title' => nil,
191
- 'id' => nil,
192
- 'approved' => nil,
193
- 'written_on' => nil,
194
- 'viewed_at' => nil,
195
- 'content' => nil,
196
- 'parent_id' => nil
197
- }
198
- HTTParty::Parsers::XML.parse(topic_xml)["topic"].should == expected_topic_hash
199
- end
200
-
201
- it "should handle a single record from xml (ActiveSupport Compatible)" do
202
- topic_xml = <<-EOT
203
- <topic>
204
- <title>The First Topic</title>
205
- <author-name>David</author-name>
206
- <id type="integer">1</id>
207
- <approved type="boolean"> true </approved>
208
- <replies-count type="integer">0</replies-count>
209
- <replies-close-in type="integer">2592000000</replies-close-in>
210
- <written-on type="date">2003-07-16</written-on>
211
- <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
212
- <content type="yaml">--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n</content>
213
- <author-email-address>david@loudthinking.com</author-email-address>
214
- <parent-id></parent-id>
215
- <ad-revenue type="decimal">1.5</ad-revenue>
216
- <optimum-viewing-angle type="float">135</optimum-viewing-angle>
217
- <resident type="symbol">yes</resident>
218
- </topic>
219
- EOT
220
-
221
- expected_topic_hash = {
222
- 'title' => "The First Topic",
223
- 'author_name' => "David",
224
- 'id' => 1,
225
- 'approved' => true,
226
- 'replies_count' => 0,
227
- 'replies_close_in' => 2592000000,
228
- 'written_on' => Date.new(2003, 7, 16),
229
- 'viewed_at' => Time.utc(2003, 7, 16, 9, 28),
230
- # Changed this line where the key is :message. The yaml specifies this as a symbol, and who am I to change what you specify
231
- # The line in ActiveSupport is
232
- # 'content' => { 'message' => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
233
- 'content' => { :message => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
234
- 'author_email_address' => "david@loudthinking.com",
235
- 'parent_id' => nil,
236
- 'ad_revenue' => BigDecimal("1.50"),
237
- 'optimum_viewing_angle' => 135.0,
238
- 'resident' => :yes
239
- }
240
-
241
- HTTParty::Parsers::XML.parse(topic_xml)["topic"].each do |k,v|
242
- v.should == expected_topic_hash[k]
243
- end
244
- end
245
-
246
- it "should handle multiple records (ActiveSupport Compatible)" do
247
- topics_xml = <<-EOT
248
- <topics type="array">
249
- <topic>
250
- <title>The First Topic</title>
251
- <author-name>David</author-name>
252
- <id type="integer">1</id>
253
- <approved type="boolean">false</approved>
254
- <replies-count type="integer">0</replies-count>
255
- <replies-close-in type="integer">2592000000</replies-close-in>
256
- <written-on type="date">2003-07-16</written-on>
257
- <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
258
- <content>Have a nice day</content>
259
- <author-email-address>david@loudthinking.com</author-email-address>
260
- <parent-id nil="true"></parent-id>
261
- </topic>
262
- <topic>
263
- <title>The Second Topic</title>
264
- <author-name>Jason</author-name>
265
- <id type="integer">1</id>
266
- <approved type="boolean">false</approved>
267
- <replies-count type="integer">0</replies-count>
268
- <replies-close-in type="integer">2592000000</replies-close-in>
269
- <written-on type="date">2003-07-16</written-on>
270
- <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
271
- <content>Have a nice day</content>
272
- <author-email-address>david@loudthinking.com</author-email-address>
273
- <parent-id></parent-id>
274
- </topic>
275
- </topics>
276
- EOT
277
-
278
- expected_topic_hash = {
279
- 'title' => "The First Topic",
280
- 'author_name' => "David",
281
- 'id' => 1,
282
- 'approved' => false,
283
- 'replies_count' => 0,
284
- 'replies_close_in' => 2592000000,
285
- 'written_on' => Date.new(2003, 7, 16),
286
- 'viewed_at' => Time.utc(2003, 7, 16, 9, 28),
287
- 'content' => "Have a nice day",
288
- 'author_email_address' => "david@loudthinking.com",
289
- 'parent_id' => nil
290
- }
291
- # puts HTTParty::Parsers::XML.parse(topics_xml)['topics'].first.inspect
292
- HTTParty::Parsers::XML.parse(topics_xml)["topics"].first.each do |k,v|
293
- v.should == expected_topic_hash[k]
294
- end
295
- end
296
-
297
- it "should handle a single record from_xml with attributes other than type (ActiveSupport Compatible)" do
298
- topic_xml = <<-EOT
299
- <rsp stat="ok">
300
- <photos page="1" pages="1" perpage="100" total="16">
301
- <photo id="175756086" owner="55569174@N00" secret="0279bf37a1" server="76" title="Colored Pencil PhotoBooth Fun" ispublic="1" isfriend="0" isfamily="0"/>
302
- </photos>
303
- </rsp>
304
- EOT
305
-
306
- expected_topic_hash = {
307
- 'id' => "175756086",
308
- 'owner' => "55569174@N00",
309
- 'secret' => "0279bf37a1",
310
- 'server' => "76",
311
- 'title' => "Colored Pencil PhotoBooth Fun",
312
- 'ispublic' => "1",
313
- 'isfriend' => "0",
314
- 'isfamily' => "0",
315
- }
316
- HTTParty::Parsers::XML.parse(topic_xml)["rsp"]["photos"]["photo"].each do |k,v|
317
- v.should == expected_topic_hash[k]
318
- end
319
- end
320
-
321
- it "should handle an emtpy array (ActiveSupport Compatible)" do
322
- blog_xml = <<-XML
323
- <blog>
324
- <posts type="array"></posts>
325
- </blog>
326
- XML
327
- expected_blog_hash = {"blog" => {"posts" => []}}
328
- HTTParty::Parsers::XML.parse(blog_xml).should == expected_blog_hash
329
- end
330
-
331
- it "should handle empty array with whitespace from xml (ActiveSupport Compatible)" do
332
- blog_xml = <<-XML
333
- <blog>
334
- <posts type="array">
335
- </posts>
336
- </blog>
337
- XML
338
- expected_blog_hash = {"blog" => {"posts" => []}}
339
- HTTParty::Parsers::XML.parse(blog_xml).should == expected_blog_hash
340
- end
341
-
342
- it "should handle array with one entry from_xml (ActiveSupport Compatible)" do
343
- blog_xml = <<-XML
344
- <blog>
345
- <posts type="array">
346
- <post>a post</post>
347
- </posts>
348
- </blog>
349
- XML
350
- expected_blog_hash = {"blog" => {"posts" => ["a post"]}}
351
- HTTParty::Parsers::XML.parse(blog_xml).should == expected_blog_hash
352
- end
353
-
354
- it "should handle array with multiple entries from xml (ActiveSupport Compatible)" do
355
- blog_xml = <<-XML
356
- <blog>
357
- <posts type="array">
358
- <post>a post</post>
359
- <post>another post</post>
360
- </posts>
361
- </blog>
362
- XML
363
- expected_blog_hash = {"blog" => {"posts" => ["a post", "another post"]}}
364
- HTTParty::Parsers::XML.parse(blog_xml).should == expected_blog_hash
365
- end
366
-
367
- it "should handle file types (ActiveSupport Compatible)" do
368
- blog_xml = <<-XML
369
- <blog>
370
- <logo type="file" name="logo.png" content_type="image/png">
371
- </logo>
372
- </blog>
373
- XML
374
- hash = HTTParty::Parsers::XML.parse(blog_xml)
375
- hash.should have_key('blog')
376
- hash['blog'].should have_key('logo')
377
-
378
- file = hash['blog']['logo']
379
- file.original_filename.should == 'logo.png'
380
- file.content_type.should == 'image/png'
381
- end
382
-
383
- it "should handle file from xml with defaults (ActiveSupport Compatible)" do
384
- blog_xml = <<-XML
385
- <blog>
386
- <logo type="file">
387
- </logo>
388
- </blog>
389
- XML
390
- file = HTTParty::Parsers::XML.parse(blog_xml)['blog']['logo']
391
- file.original_filename.should == 'untitled'
392
- file.content_type.should == 'application/octet-stream'
393
- end
394
-
395
- it "should handle xsd like types from xml (ActiveSupport Compatible)" do
396
- bacon_xml = <<-EOT
397
- <bacon>
398
- <weight type="double">0.5</weight>
399
- <price type="decimal">12.50</price>
400
- <chunky type="boolean"> 1 </chunky>
401
- <expires-at type="dateTime">2007-12-25T12:34:56+0000</expires-at>
402
- <notes type="string"></notes>
403
- <illustration type="base64Binary">YmFiZS5wbmc=</illustration>
404
- </bacon>
405
- EOT
406
-
407
- expected_bacon_hash = {
408
- 'weight' => 0.5,
409
- 'chunky' => true,
410
- 'price' => BigDecimal("12.50"),
411
- 'expires_at' => Time.utc(2007,12,25,12,34,56),
412
- 'notes' => "",
413
- 'illustration' => "babe.png"
414
- }
415
-
416
- HTTParty::Parsers::XML.parse(bacon_xml)["bacon"].should == expected_bacon_hash
417
- end
418
-
419
- it "should let type trickle through when unknown (ActiveSupport Compatible)" do
420
- product_xml = <<-EOT
421
- <product>
422
- <weight type="double">0.5</weight>
423
- <image type="ProductImage"><filename>image.gif</filename></image>
424
-
425
- </product>
426
- EOT
427
-
428
- expected_product_hash = {
429
- 'weight' => 0.5,
430
- 'image' => {'type' => 'ProductImage', 'filename' => 'image.gif' },
431
- }
432
-
433
- HTTParty::Parsers::XML.parse(product_xml)["product"].should == expected_product_hash
434
- end
435
-
436
- it "should handle unescaping from xml (ActiveResource Compatible)" do
437
- xml_string = '<person><bare-string>First &amp; Last Name</bare-string><pre-escaped-string>First &amp;amp; Last Name</pre-escaped-string></person>'
438
- expected_hash = {
439
- 'bare_string' => 'First & Last Name',
440
- 'pre_escaped_string' => 'First &amp; Last Name'
441
- }
442
-
443
- HTTParty::Parsers::XML.parse(xml_string)['person'].should == expected_hash
444
- end
445
- end