jnunemaker-happymapper 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History +6 -0
- data/Manifest +5 -1
- data/examples/amazon.rb +1 -1
- data/examples/current_weather.rb +4 -4
- data/examples/dashed_elements.rb +20 -0
- data/happymapper.gemspec +3 -3
- data/lib/happymapper/item.rb +29 -18
- data/lib/happymapper/version.rb +1 -1
- data/lib/happymapper.rb +50 -70
- data/lib/libxml_ext/libxml_helper.rb +1 -37
- data/spec/fixtures/family_tree.xml +7 -0
- data/spec/fixtures/multiple_namespaces.xml +170 -0
- data/spec/fixtures/product_default_namespace.xml +10 -0
- data/spec/fixtures/product_no_namespace.xml +10 -0
- data/spec/fixtures/product_single_namespace.xml +10 -0
- data/spec/happymapper_item_spec.rb +43 -22
- data/spec/happymapper_spec.rb +290 -132
- data/spec/spec_helper.rb +4 -0
- metadata +8 -2
data/History
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
== 0.2.0
|
2
|
+
* 1 major enhancement, 2 minor ehancements
|
3
|
+
* Automatic handling of namespaces (part by Robert Lowrey and rest by John Nunemaker)
|
4
|
+
* Added :root option to tag method. This allows setting an object as the root element, which sets xpath to use / and sets single to true
|
5
|
+
* Now defaulting tag names for classes in modules to last constant downcased
|
6
|
+
|
1
7
|
== 0.1.7 2009-01-29
|
2
8
|
* 1 minor enhancement
|
3
9
|
* Support dashes in elements (Josh Nichols)
|
data/Manifest
CHANGED
@@ -3,7 +3,6 @@ examples/current_weather.rb
|
|
3
3
|
examples/dashed_elements.rb
|
4
4
|
examples/post.rb
|
5
5
|
examples/twitter.rb
|
6
|
-
happymapper.gemspec
|
7
6
|
History
|
8
7
|
lib/happymapper/attribute.rb
|
9
8
|
lib/happymapper/element.rb
|
@@ -18,8 +17,13 @@ README
|
|
18
17
|
spec/fixtures/address.xml
|
19
18
|
spec/fixtures/commit.xml
|
20
19
|
spec/fixtures/current_weather.xml
|
20
|
+
spec/fixtures/family_tree.xml
|
21
|
+
spec/fixtures/multiple_namespaces.xml
|
21
22
|
spec/fixtures/pita.xml
|
22
23
|
spec/fixtures/posts.xml
|
24
|
+
spec/fixtures/product_default_namespace.xml
|
25
|
+
spec/fixtures/product_no_namespace.xml
|
26
|
+
spec/fixtures/product_single_namespace.xml
|
23
27
|
spec/fixtures/radar.xml
|
24
28
|
spec/fixtures/statuses.xml
|
25
29
|
spec/happymapper_attribute_spec.rb
|
data/examples/amazon.rb
CHANGED
@@ -23,7 +23,7 @@ module PITA
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
item = PITA::Items.parse(file_contents, :single => true
|
26
|
+
item = PITA::Items.parse(file_contents, :single => true)
|
27
27
|
item.items.each do |i|
|
28
28
|
puts i.asin, i.detail_page_url, i.manufacturer, ''
|
29
29
|
end
|
data/examples/current_weather.rb
CHANGED
@@ -5,10 +5,10 @@ file_contents = File.read(dir + '/../spec/fixtures/current_weather.xml')
|
|
5
5
|
|
6
6
|
class CurrentWeather
|
7
7
|
include HappyMapper
|
8
|
-
tag '
|
9
|
-
element :temperature, Integer, :tag => '
|
10
|
-
element :feels_like, Integer, :tag => '
|
11
|
-
element :current_condition, String, :tag => '
|
8
|
+
tag 'ob'
|
9
|
+
element :temperature, Integer, :tag => 'temp'
|
10
|
+
element :feels_like, Integer, :tag => 'feels-like'
|
11
|
+
element :current_condition, String, :tag => 'current-condition', :attributes => {:icon => String}
|
12
12
|
end
|
13
13
|
|
14
14
|
CurrentWeather.parse(file_contents).each do |current_weather|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require File.join(dir, 'happymapper')
|
3
|
+
|
4
|
+
file_contents = File.read(dir + '/../spec/fixtures/commit.xml')
|
5
|
+
|
6
|
+
module GitHub
|
7
|
+
class Commit
|
8
|
+
include HappyMapper
|
9
|
+
|
10
|
+
tag "commit", :root => true
|
11
|
+
element :url, String
|
12
|
+
element :tree, String
|
13
|
+
element :message, String
|
14
|
+
element :id, String
|
15
|
+
element :'committed-date', Date
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
commit = GitHub::Commit.parse(file_contents)
|
20
|
+
puts commit.committed_date, commit.url, commit.id
|
data/happymapper.gemspec
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{happymapper}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.2.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-01-
|
9
|
+
s.date = %q{2009-01-29}
|
10
10
|
s.description = %q{object to xml mapping library}
|
11
11
|
s.email = %q{nunemaker@gmail.com}
|
12
12
|
s.extra_rdoc_files = ["lib/happymapper/attribute.rb", "lib/happymapper/element.rb", "lib/happymapper/item.rb", "lib/happymapper/version.rb", "lib/happymapper.rb", "lib/libxml_ext/libxml_helper.rb", "README", "TODO"]
|
13
|
-
s.files = ["examples/amazon.rb", "examples/current_weather.rb", "examples/post.rb", "examples/twitter.rb", "happymapper.gemspec", "History", "lib/happymapper/attribute.rb", "lib/happymapper/element.rb", "lib/happymapper/item.rb", "lib/happymapper/version.rb", "lib/happymapper.rb", "lib/libxml_ext/libxml_helper.rb", "License", "Manifest", "Rakefile", "README", "spec/fixtures/address.xml", "spec/fixtures/commit.xml", "spec/fixtures/current_weather.xml", "spec/fixtures/pita.xml", "spec/fixtures/posts.xml", "spec/fixtures/radar.xml", "spec/fixtures/statuses.xml", "spec/happymapper_attribute_spec.rb", "spec/happymapper_element_spec.rb", "spec/happymapper_item_spec.rb", "spec/happymapper_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "TODO", "website/css/common.css", "website/index.html"]
|
13
|
+
s.files = ["examples/amazon.rb", "examples/current_weather.rb", "examples/dashed_elements.rb", "examples/post.rb", "examples/twitter.rb", "happymapper.gemspec", "History", "lib/happymapper/attribute.rb", "lib/happymapper/element.rb", "lib/happymapper/item.rb", "lib/happymapper/version.rb", "lib/happymapper.rb", "lib/libxml_ext/libxml_helper.rb", "License", "Manifest", "Rakefile", "README", "spec/fixtures/address.xml", "spec/fixtures/commit.xml", "spec/fixtures/current_weather.xml", "spec/fixtures/family_tree.xml", "spec/fixtures/multiple_namespaces.xml", "spec/fixtures/pita.xml", "spec/fixtures/posts.xml", "spec/fixtures/product_default_namespace.xml", "spec/fixtures/product_no_namespace.xml", "spec/fixtures/product_single_namespace.xml", "spec/fixtures/radar.xml", "spec/fixtures/statuses.xml", "spec/happymapper_attribute_spec.rb", "spec/happymapper_element_spec.rb", "spec/happymapper_item_spec.rb", "spec/happymapper_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "TODO", "website/css/common.css", "website/index.html"]
|
14
14
|
s.has_rdoc = true
|
15
15
|
s.homepage = %q{http://happymapper.rubyforge.org}
|
16
16
|
s.post_install_message = %q{May you have many happy mappings!}
|
data/lib/happymapper/item.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module HappyMapper
|
2
2
|
class Item
|
3
|
-
attr_accessor :type, :tag, :options
|
4
|
-
attr_reader :name
|
3
|
+
attr_accessor :name, :type, :tag, :options, :namespace
|
5
4
|
|
6
5
|
Types = [String, Float, Time, Date, DateTime, Integer, Boolean]
|
7
6
|
|
@@ -10,26 +9,36 @@ module HappyMapper
|
|
10
9
|
# grandchildren and all others down the chain (// in expath)
|
11
10
|
# :single => Boolean False if object should be collection, True for single object
|
12
11
|
def initialize(name, type, o={})
|
13
|
-
self.name
|
14
|
-
self.
|
12
|
+
self.name = name.to_s
|
13
|
+
self.type = type
|
14
|
+
self.tag = o.delete(:tag) || name.to_s
|
15
|
+
self.options = {
|
16
|
+
:single => false,
|
17
|
+
:deep => false,
|
18
|
+
}.merge(o)
|
19
|
+
|
15
20
|
@xml_type = self.class.to_s.split('::').last.downcase
|
16
21
|
end
|
17
|
-
|
18
|
-
def name=(new_name)
|
19
|
-
@name = new_name.to_s
|
20
|
-
end
|
21
22
|
|
22
|
-
def from_xml_node(node
|
23
|
+
def from_xml_node(node)
|
23
24
|
if primitive?
|
24
|
-
value_from_xml_node(node
|
25
|
+
value_from_xml_node(node) do |value_before_type_cast|
|
25
26
|
typecast(value_before_type_cast)
|
26
27
|
end
|
27
28
|
else
|
28
|
-
|
29
|
-
type.parse(node, options.merge(:use_default_namespace => use_default_namespace))
|
29
|
+
type.parse(node, options)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
def xpath
|
34
|
+
xpath = ''
|
35
|
+
xpath += './/' if options[:deep]
|
36
|
+
xpath += namespace if namespace
|
37
|
+
xpath += tag
|
38
|
+
# puts "xpath: #{xpath}"
|
39
|
+
xpath
|
40
|
+
end
|
41
|
+
|
33
42
|
def primitive?
|
34
43
|
Types.include?(type)
|
35
44
|
end
|
@@ -42,6 +51,10 @@ module HappyMapper
|
|
42
51
|
!element?
|
43
52
|
end
|
44
53
|
|
54
|
+
def method_name
|
55
|
+
@method_name ||= name.tr('-', '_')
|
56
|
+
end
|
57
|
+
|
45
58
|
def typecast(value)
|
46
59
|
return value if value.kind_of?(type) || value.nil?
|
47
60
|
begin
|
@@ -72,13 +85,11 @@ module HappyMapper
|
|
72
85
|
end
|
73
86
|
end
|
74
87
|
|
75
|
-
private
|
76
|
-
def value_from_xml_node(node
|
77
|
-
node.register_default_namespace(namespace.chop) if namespace
|
78
|
-
|
88
|
+
private
|
89
|
+
def value_from_xml_node(node)
|
79
90
|
if element?
|
80
|
-
|
81
|
-
|
91
|
+
result = node.find_first(xpath)
|
92
|
+
# puts "vfxn: #{xpath} #{result.inspect}"
|
82
93
|
if result
|
83
94
|
value = yield(result.content)
|
84
95
|
if options[:attributes].is_a?(Hash)
|
data/lib/happymapper/version.rb
CHANGED
data/lib/happymapper.rb
CHANGED
@@ -25,7 +25,7 @@ module HappyMapper
|
|
25
25
|
attribute = Attribute.new(name, type, options)
|
26
26
|
@attributes[to_s] ||= []
|
27
27
|
@attributes[to_s] << attribute
|
28
|
-
|
28
|
+
attr_accessor attribute.method_name.intern
|
29
29
|
end
|
30
30
|
|
31
31
|
def attributes
|
@@ -36,7 +36,7 @@ module HappyMapper
|
|
36
36
|
element = Element.new(name, type, options)
|
37
37
|
@elements[to_s] ||= []
|
38
38
|
@elements[to_s] << element
|
39
|
-
|
39
|
+
attr_accessor element.method_name.intern
|
40
40
|
end
|
41
41
|
|
42
42
|
def elements
|
@@ -51,90 +51,70 @@ module HappyMapper
|
|
51
51
|
element name, type, {:single => false}.merge(options)
|
52
52
|
end
|
53
53
|
|
54
|
-
|
54
|
+
# Options:
|
55
|
+
# :root => Boolean, true means this is xml root
|
56
|
+
def tag(new_tag_name, o={})
|
57
|
+
options = {:root => false}.merge(o)
|
58
|
+
@root = options.delete(:root)
|
55
59
|
@tag_name = new_tag_name.to_s
|
56
60
|
end
|
57
61
|
|
58
62
|
def get_tag_name
|
59
|
-
@tag_name ||=
|
63
|
+
@tag_name ||= begin
|
64
|
+
to_s.split('::')[-1].downcase
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def is_root?
|
69
|
+
@root
|
60
70
|
end
|
61
71
|
|
62
|
-
def parse(xml, o={})
|
63
|
-
options = {
|
64
|
-
|
65
|
-
|
66
|
-
}.merge(o)
|
72
|
+
def parse(xml, o={})
|
73
|
+
xpath, collection, options = '', [], {:single => false}.merge(o)
|
74
|
+
doc = xml.is_a?(LibXML::XML::Node) ? xml : xml.to_libxml_doc
|
75
|
+
node = doc.respond_to?(:root) ? doc.root : doc
|
67
76
|
|
68
|
-
|
69
|
-
doc = xml.is_a?(LibXML::XML::Node) ? xml : xml.to_libxml_doc
|
77
|
+
# puts doc.inspect, doc.respond_to?(:root) ? doc.root.inspect : ''
|
70
78
|
|
71
|
-
|
72
|
-
|
73
|
-
node.
|
74
|
-
|
75
|
-
else
|
76
|
-
doc.find("//#{get_tag_name}")
|
79
|
+
unless node.namespaces.default.nil?
|
80
|
+
namespace = "default_ns:"
|
81
|
+
node.namespaces.default_prefix = namespace.chop
|
82
|
+
# warn "Default XML namespace present -- results are unpredictable"
|
77
83
|
end
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
node.register_default_namespace(namespace.chop)
|
82
|
-
node.find("#{namespace}#{get_tag_name}")
|
83
|
-
else
|
84
|
-
nested = '.' unless doc.respond_to?(:root)
|
85
|
-
path = "#{nested}//#{get_tag_name}"
|
86
|
-
doc.find(path)
|
84
|
+
|
85
|
+
if node.namespaces.to_a.size > 0 && namespace.nil? && !node.namespaces.namespace.nil?
|
86
|
+
namespace = node.namespaces.namespace.prefix + ":"
|
87
87
|
end
|
88
|
-
|
89
|
-
collection = create_collection(nodes, namespace)
|
90
88
|
|
91
|
-
#
|
92
|
-
|
93
|
-
|
89
|
+
# xpath += doc.respond_to?(:root) ? '' : '.'
|
90
|
+
xpath += is_root? ? '/' : './/'
|
91
|
+
xpath += namespace if namespace
|
92
|
+
xpath += get_tag_name
|
93
|
+
# puts "parse: #{xpath}"
|
94
94
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
attributes.each { |attr| obj.send("#{normalize_name attr.name}=", attr.from_xml_node(el)) }
|
103
|
-
elements.each { |elem| obj.send("#{normalize_name elem.name}=", elem.from_xml_node(el, namespace)) }
|
104
|
-
acc << obj
|
95
|
+
nodes = node.find(xpath)
|
96
|
+
nodes.each do |n|
|
97
|
+
obj = new
|
98
|
+
|
99
|
+
attributes.each do |attr|
|
100
|
+
obj.send("#{attr.method_name}=",
|
101
|
+
attr.from_xml_node(n))
|
105
102
|
end
|
103
|
+
|
104
|
+
elements.each do |elem|
|
105
|
+
elem.namespace = namespace
|
106
|
+
obj.send("#{elem.method_name}=",
|
107
|
+
elem.from_xml_node(n))
|
108
|
+
end
|
109
|
+
collection << obj
|
106
110
|
end
|
107
|
-
|
108
|
-
def create_getter(name)
|
109
|
-
name = normalize_name(name)
|
110
|
-
|
111
|
-
class_eval <<-EOS, __FILE__, __LINE__
|
112
|
-
def #{name}
|
113
|
-
@#{name}
|
114
|
-
end
|
115
|
-
EOS
|
116
|
-
end
|
117
|
-
|
118
|
-
def create_setter(name)
|
119
|
-
name = normalize_name(name)
|
120
|
-
|
121
|
-
class_eval <<-EOS, __FILE__, __LINE__
|
122
|
-
def #{name}=(value)
|
123
|
-
@#{name} = value
|
124
|
-
end
|
125
|
-
EOS
|
126
|
-
end
|
127
|
-
|
128
|
-
def create_accessor(name)
|
129
|
-
name = normalize_name(name)
|
130
111
|
|
131
|
-
|
132
|
-
|
133
|
-
|
112
|
+
# per http://libxml.rubyforge.org/rdoc/classes/LibXML/XML/Document.html#M000354
|
113
|
+
nodes = nil
|
114
|
+
GC.start
|
134
115
|
|
135
|
-
|
136
|
-
|
137
|
-
end
|
116
|
+
options[:single] || is_root? ? collection.first : collection
|
117
|
+
end
|
138
118
|
end
|
139
119
|
end
|
140
120
|
|
@@ -5,12 +5,7 @@ class XML::Node
|
|
5
5
|
# Open up XML::Node from libxml and add convenience methods inspired
|
6
6
|
# by hpricot.
|
7
7
|
# (http://code.whytheluckystiff.net/hpricot/wiki/HpricotBasics)
|
8
|
-
|
9
|
-
# * provide better handling of default namespaces
|
10
|
-
|
11
|
-
# an array of default namespaces to past into
|
12
|
-
attr_accessor :default_namespaces
|
13
|
-
|
8
|
+
|
14
9
|
# find the child node with the given xpath
|
15
10
|
def at(xpath)
|
16
11
|
self.find_first(xpath)
|
@@ -51,37 +46,6 @@ class XML::Node
|
|
51
46
|
def xpath
|
52
47
|
self.path
|
53
48
|
end
|
54
|
-
|
55
|
-
# provide a name for the default namespace
|
56
|
-
def register_default_namespace(name)
|
57
|
-
default_namespace = namespaces.default
|
58
|
-
|
59
|
-
if default_namespace
|
60
|
-
register_namespace("#{name}:#{default_namespace.href}")
|
61
|
-
else
|
62
|
-
raise "No default namespace found"
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# register a namespace, of the form "foo:http://example.com/ns"
|
67
|
-
def register_namespace(name_and_href)
|
68
|
-
(@default_namespaces ||= []) << name_and_href
|
69
|
-
end
|
70
|
-
|
71
|
-
def find_with_default_ns(xpath_expr, namespace=nil)
|
72
|
-
find_base(xpath_expr, namespace || default_namespaces)
|
73
|
-
end
|
74
|
-
|
75
|
-
def find_first_with_default_ns(xpath_expr, namespace=nil)
|
76
|
-
find_first_base(xpath_expr, namespace || default_namespaces)
|
77
|
-
end
|
78
|
-
|
79
|
-
|
80
|
-
alias_method :find_base, :find unless method_defined?(:find_base)
|
81
|
-
alias_method :find, :find_with_default_ns
|
82
|
-
|
83
|
-
alias_method :find_first_base, :find_first unless method_defined?(:find_first_base)
|
84
|
-
alias_method :find_first, :find_first_with_default_ns
|
85
49
|
end
|
86
50
|
|
87
51
|
class String
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<familytree xmlns="http://api.familysearch.org/familytree/v1" xmlns:fsapi-v1="http://api.familysearch.org/v1" version="1.0.20071213.942" statusMessage="OK" statusCode="200">
|
3
|
+
<persons>
|
4
|
+
<person version="1199378491000" modified="2008-01-03T09:41:31-07:00" id="KWQS-BBQ">
|
5
|
+
</person>
|
6
|
+
</persons>
|
7
|
+
</familytree>
|
@@ -0,0 +1,170 @@
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8'?>
|
2
|
+
<v2:TrackReply xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:v2='http://fedex.com/ws/track/v2'>
|
3
|
+
<v2:HighestSeverity>SUCCESS</v2:HighestSeverity>
|
4
|
+
<v2:Notifications>
|
5
|
+
<v2:Severity>SUCCESS</v2:Severity>
|
6
|
+
<v2:Source>trck</v2:Source>
|
7
|
+
<v2:Code>0</v2:Code>
|
8
|
+
<v2:Message>Request was successfully processed.</v2:Message>
|
9
|
+
<v2:LocalizedMessage>Request was successfully processed.</v2:LocalizedMessage>
|
10
|
+
</v2:Notifications>
|
11
|
+
<ns:TransactionDetail xmlns:ns='http://fedex.com/ws/track/v2'>
|
12
|
+
<ns:CustomerTransactionId>20090102-111321</ns:CustomerTransactionId>
|
13
|
+
</ns:TransactionDetail>
|
14
|
+
<ns:Version xmlns:ns='http://fedex.com/ws/track/v2'>
|
15
|
+
<ns:ServiceId>trck</ns:ServiceId>
|
16
|
+
<ns:Major>2</ns:Major>
|
17
|
+
<ns:Intermediate>0</ns:Intermediate>
|
18
|
+
<ns:Minor>0</ns:Minor>
|
19
|
+
</ns:Version>
|
20
|
+
<v2:DuplicateWaybill>false</v2:DuplicateWaybill>
|
21
|
+
<v2:MoreData>false</v2:MoreData>
|
22
|
+
<v2:TrackDetails>
|
23
|
+
<v2:TrackingNumber>9611018034267800045212</v2:TrackingNumber>
|
24
|
+
<v2:TrackingNumberUniqueIdentifier>120081227094248461000~034267800045212</v2:TrackingNumberUniqueIdentifier>
|
25
|
+
<v2:StatusCode>OD</v2:StatusCode>
|
26
|
+
<v2:StatusDescription>On FedEx vehicle for delivery</v2:StatusDescription>
|
27
|
+
<v2:CarrierCode>FDXG</v2:CarrierCode>
|
28
|
+
<v2:ServiceInfo>Ground-Package Returns Program-Domestic</v2:ServiceInfo>
|
29
|
+
<v2:PackageWeight>
|
30
|
+
<v2:Units>LB</v2:Units>
|
31
|
+
<v2:Value>2.6</v2:Value>
|
32
|
+
</v2:PackageWeight>
|
33
|
+
<v2:Packaging>Package</v2:Packaging>
|
34
|
+
<v2:PackageSequenceNumber>1</v2:PackageSequenceNumber>
|
35
|
+
<v2:PackageCount>1</v2:PackageCount>
|
36
|
+
<v2:OriginLocationAddress>
|
37
|
+
<v2:City>SANFORD</v2:City>
|
38
|
+
<v2:StateOrProvinceCode>FL</v2:StateOrProvinceCode>
|
39
|
+
<v2:CountryCode>US</v2:CountryCode>
|
40
|
+
<v2:Residential>false</v2:Residential>
|
41
|
+
</v2:OriginLocationAddress>
|
42
|
+
<v2:ShipTimestamp>2008-12-29T00:00:00</v2:ShipTimestamp>
|
43
|
+
<v2:EstimatedDeliveryTimestamp>2009-01-02T00:00:00</v2:EstimatedDeliveryTimestamp>
|
44
|
+
<v2:SignatureProofOfDeliveryAvailable>false</v2:SignatureProofOfDeliveryAvailable>
|
45
|
+
<v2:ProofOfDeliveryNotificationsAvailable>true</v2:ProofOfDeliveryNotificationsAvailable>
|
46
|
+
<v2:ExceptionNotificationsAvailable>true</v2:ExceptionNotificationsAvailable>
|
47
|
+
<v2:Events>
|
48
|
+
<v2:Timestamp>2009-01-02T06:00:00</v2:Timestamp>
|
49
|
+
<v2:EventType>OD</v2:EventType>
|
50
|
+
<v2:EventDescription>On FedEx vehicle for delivery</v2:EventDescription>
|
51
|
+
<v2:Address>
|
52
|
+
<v2:City>WICHITA</v2:City>
|
53
|
+
<v2:StateOrProvinceCode>KS</v2:StateOrProvinceCode>
|
54
|
+
<v2:PostalCode>67226</v2:PostalCode>
|
55
|
+
<v2:CountryCode>US</v2:CountryCode>
|
56
|
+
<v2:Residential>false</v2:Residential>
|
57
|
+
</v2:Address>
|
58
|
+
</v2:Events>
|
59
|
+
<v2:Events>
|
60
|
+
<v2:Timestamp>2009-01-02T01:17:32</v2:Timestamp>
|
61
|
+
<v2:EventType>AR</v2:EventType>
|
62
|
+
<v2:EventDescription>At local FedEx facility</v2:EventDescription>
|
63
|
+
<v2:Address>
|
64
|
+
<v2:City>WICHITA</v2:City>
|
65
|
+
<v2:StateOrProvinceCode>KS</v2:StateOrProvinceCode>
|
66
|
+
<v2:PostalCode>67226</v2:PostalCode>
|
67
|
+
<v2:CountryCode>US</v2:CountryCode>
|
68
|
+
<v2:Residential>false</v2:Residential>
|
69
|
+
</v2:Address>
|
70
|
+
</v2:Events>
|
71
|
+
<v2:Events>
|
72
|
+
<v2:Timestamp>2009-01-01T21:49:49</v2:Timestamp>
|
73
|
+
<v2:EventType>DP</v2:EventType>
|
74
|
+
<v2:EventDescription>Departed FedEx location</v2:EventDescription>
|
75
|
+
<v2:Address>
|
76
|
+
<v2:City>LENEXA</v2:City>
|
77
|
+
<v2:StateOrProvinceCode>KS</v2:StateOrProvinceCode>
|
78
|
+
<v2:PostalCode>66227</v2:PostalCode>
|
79
|
+
<v2:CountryCode>US</v2:CountryCode>
|
80
|
+
<v2:Residential>false</v2:Residential>
|
81
|
+
</v2:Address>
|
82
|
+
</v2:Events>
|
83
|
+
<v2:Events>
|
84
|
+
<v2:Timestamp>2008-12-31T16:19:00</v2:Timestamp>
|
85
|
+
<v2:EventType>AR</v2:EventType>
|
86
|
+
<v2:EventDescription>Arrived at FedEx location</v2:EventDescription>
|
87
|
+
<v2:Address>
|
88
|
+
<v2:City>LENEXA</v2:City>
|
89
|
+
<v2:StateOrProvinceCode>KS</v2:StateOrProvinceCode>
|
90
|
+
<v2:PostalCode>66227</v2:PostalCode>
|
91
|
+
<v2:CountryCode>US</v2:CountryCode>
|
92
|
+
<v2:Residential>false</v2:Residential>
|
93
|
+
</v2:Address>
|
94
|
+
</v2:Events>
|
95
|
+
<v2:Events>
|
96
|
+
<v2:Timestamp>2008-12-30T11:01:23</v2:Timestamp>
|
97
|
+
<v2:EventType>DP</v2:EventType>
|
98
|
+
<v2:EventDescription>Departed FedEx location</v2:EventDescription>
|
99
|
+
<v2:Address>
|
100
|
+
<v2:City>ORLANDO</v2:City>
|
101
|
+
<v2:StateOrProvinceCode>FL</v2:StateOrProvinceCode>
|
102
|
+
<v2:PostalCode>32809</v2:PostalCode>
|
103
|
+
<v2:CountryCode>US</v2:CountryCode>
|
104
|
+
<v2:Residential>false</v2:Residential>
|
105
|
+
</v2:Address>
|
106
|
+
</v2:Events>
|
107
|
+
<v2:Events>
|
108
|
+
<v2:Timestamp>2008-12-30T05:00:00</v2:Timestamp>
|
109
|
+
<v2:EventType>AR</v2:EventType>
|
110
|
+
<v2:EventDescription>Arrived at FedEx location</v2:EventDescription>
|
111
|
+
<v2:Address>
|
112
|
+
<v2:City>ORLANDO</v2:City>
|
113
|
+
<v2:StateOrProvinceCode>FL</v2:StateOrProvinceCode>
|
114
|
+
<v2:PostalCode>32809</v2:PostalCode>
|
115
|
+
<v2:CountryCode>US</v2:CountryCode>
|
116
|
+
<v2:Residential>false</v2:Residential>
|
117
|
+
</v2:Address>
|
118
|
+
</v2:Events>
|
119
|
+
<v2:Events>
|
120
|
+
<v2:Timestamp>2008-12-30T03:16:33</v2:Timestamp>
|
121
|
+
<v2:EventType>DP</v2:EventType>
|
122
|
+
<v2:EventDescription>Left FedEx origin facility</v2:EventDescription>
|
123
|
+
<v2:Address>
|
124
|
+
<v2:City>SANFORD</v2:City>
|
125
|
+
<v2:StateOrProvinceCode>FL</v2:StateOrProvinceCode>
|
126
|
+
<v2:PostalCode>32771</v2:PostalCode>
|
127
|
+
<v2:CountryCode>US</v2:CountryCode>
|
128
|
+
<v2:Residential>false</v2:Residential>
|
129
|
+
</v2:Address>
|
130
|
+
</v2:Events>
|
131
|
+
<v2:Events>
|
132
|
+
<v2:Timestamp>2008-12-29T22:46:00</v2:Timestamp>
|
133
|
+
<v2:EventType>AR</v2:EventType>
|
134
|
+
<v2:EventDescription>Arrived at FedEx location</v2:EventDescription>
|
135
|
+
<v2:Address>
|
136
|
+
<v2:City>SANFORD</v2:City>
|
137
|
+
<v2:StateOrProvinceCode>FL</v2:StateOrProvinceCode>
|
138
|
+
<v2:PostalCode>32771</v2:PostalCode>
|
139
|
+
<v2:CountryCode>US</v2:CountryCode>
|
140
|
+
<v2:Residential>false</v2:Residential>
|
141
|
+
</v2:Address>
|
142
|
+
</v2:Events>
|
143
|
+
<v2:Events>
|
144
|
+
<v2:Timestamp>2008-12-29T17:12:00</v2:Timestamp>
|
145
|
+
<v2:EventType>PU</v2:EventType>
|
146
|
+
<v2:EventDescription>Picked up</v2:EventDescription>
|
147
|
+
<v2:Address>
|
148
|
+
<v2:City>SANFORD</v2:City>
|
149
|
+
<v2:StateOrProvinceCode>FL</v2:StateOrProvinceCode>
|
150
|
+
<v2:PostalCode>32771</v2:PostalCode>
|
151
|
+
<v2:CountryCode>US</v2:CountryCode>
|
152
|
+
<v2:Residential>false</v2:Residential>
|
153
|
+
</v2:Address>
|
154
|
+
</v2:Events>
|
155
|
+
<v2:Events>
|
156
|
+
<v2:Timestamp>2008-12-27T09:40:00</v2:Timestamp>
|
157
|
+
<v2:EventType>IP</v2:EventType>
|
158
|
+
<v2:EventDescription>In FedEx possession</v2:EventDescription>
|
159
|
+
<v2:StatusExceptionCode>084</v2:StatusExceptionCode>
|
160
|
+
<v2:StatusExceptionDescription>Tendered at FedEx location</v2:StatusExceptionDescription>
|
161
|
+
<v2:Address>
|
162
|
+
<v2:City>LONGWOOD</v2:City>
|
163
|
+
<v2:StateOrProvinceCode>FL</v2:StateOrProvinceCode>
|
164
|
+
<v2:PostalCode>327506398</v2:PostalCode>
|
165
|
+
<v2:CountryCode>US</v2:CountryCode>
|
166
|
+
<v2:Residential>false</v2:Residential>
|
167
|
+
</v2:Address>
|
168
|
+
</v2:Events>
|
169
|
+
</v2:TrackDetails>
|
170
|
+
</v2:TrackReply>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<products xmlns:juju="http://bigco.com">
|
2
|
+
<product>
|
3
|
+
<title>A Title</title>
|
4
|
+
<features_bullets>
|
5
|
+
<feature>This is feature text 1</feature>
|
6
|
+
<feature>This is feature text 2</feature>
|
7
|
+
<bug>This is a bug</bug>
|
8
|
+
</features_bullets>
|
9
|
+
</product>
|
10
|
+
</products>
|
@@ -4,70 +4,91 @@ describe HappyMapper::Item do
|
|
4
4
|
|
5
5
|
describe "new instance" do
|
6
6
|
before do
|
7
|
-
@
|
7
|
+
@item = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
|
8
8
|
end
|
9
9
|
|
10
10
|
it "should accept a name" do
|
11
|
-
@
|
11
|
+
@item.name.should == 'foo'
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'should accept a type' do
|
15
|
-
@
|
15
|
+
@item.type.should == String
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'should accept :tag as an option' do
|
19
|
-
@
|
19
|
+
@item.tag.should == 'foobar'
|
20
20
|
end
|
21
21
|
|
22
|
-
it
|
23
|
-
@
|
22
|
+
it "should have a method_name" do
|
23
|
+
@item.method_name.should == 'foo'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#method_name" do
|
28
|
+
it "should convert dashes to underscores" do
|
29
|
+
item = HappyMapper::Item.new(:'foo-bar', String, :tag => 'foobar')
|
30
|
+
item.method_name.should == 'foo_bar'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#xpath" do
|
35
|
+
it "should default to tag" do
|
36
|
+
item = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
|
37
|
+
item.xpath.should == 'foobar'
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should prepend with .// if options[:deep] true" do
|
41
|
+
item = HappyMapper::Item.new(:foo, String, :tag => 'foobar', :deep => true)
|
42
|
+
item.xpath.should == './/foobar'
|
24
43
|
end
|
25
44
|
|
26
|
-
it
|
27
|
-
|
45
|
+
it "should prepend namespace if namespace exists" do
|
46
|
+
item = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
|
47
|
+
item.namespace = 'v2:'
|
48
|
+
item.xpath.should == 'v2:foobar'
|
28
49
|
end
|
29
50
|
end
|
30
51
|
|
31
52
|
describe "typecasting" do
|
32
53
|
it "should work with Strings" do
|
33
|
-
|
54
|
+
item = HappyMapper::Item.new(:foo, String)
|
34
55
|
[21, '21'].each do |a|
|
35
|
-
|
56
|
+
item.typecast(a).should == '21'
|
36
57
|
end
|
37
58
|
end
|
38
59
|
|
39
60
|
it "should work with Integers" do
|
40
|
-
|
61
|
+
item = HappyMapper::Item.new(:foo, Integer)
|
41
62
|
[21, 21.0, '21'].each do |a|
|
42
|
-
|
63
|
+
item.typecast(a).should == 21
|
43
64
|
end
|
44
65
|
end
|
45
66
|
|
46
67
|
it "should work with Floats" do
|
47
|
-
|
68
|
+
item = HappyMapper::Item.new(:foo, Float)
|
48
69
|
[21, 21.0, '21'].each do |a|
|
49
|
-
|
70
|
+
item.typecast(a).should == 21.0
|
50
71
|
end
|
51
72
|
end
|
52
73
|
|
53
74
|
it "should work with Times" do
|
54
|
-
|
55
|
-
|
75
|
+
item = HappyMapper::Item.new(:foo, Time)
|
76
|
+
item.typecast('2000-01-01 01:01:01.123456').should == Time.local(2000, 1, 1, 1, 1, 1, 123456)
|
56
77
|
end
|
57
78
|
|
58
79
|
it "should work with Dates" do
|
59
|
-
|
60
|
-
|
80
|
+
item = HappyMapper::Item.new(:foo, Date)
|
81
|
+
item.typecast('2000-01-01').should == Date.new(2000, 1, 1)
|
61
82
|
end
|
62
83
|
|
63
84
|
it "should work with DateTimes" do
|
64
|
-
|
65
|
-
|
85
|
+
item = HappyMapper::Item.new(:foo, DateTime)
|
86
|
+
item.typecast('2000-01-01 00:00:00').should == DateTime.new(2000, 1, 1, 0, 0, 0)
|
66
87
|
end
|
67
88
|
|
68
89
|
it "should work with Boolean" do
|
69
|
-
|
70
|
-
|
90
|
+
item = HappyMapper::Item.new(:foo, Boolean)
|
91
|
+
item.typecast('false').should == false
|
71
92
|
end
|
72
93
|
end
|
73
94
|
end
|
data/spec/happymapper_spec.rb
CHANGED
@@ -1,8 +1,127 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require 'pp'
|
2
3
|
|
3
|
-
class
|
4
|
+
class Feature
|
5
|
+
include HappyMapper
|
6
|
+
element :name, String, :tag => '.|.//text()'
|
7
|
+
end
|
8
|
+
|
9
|
+
class FeatureBullet
|
4
10
|
include HappyMapper
|
5
11
|
|
12
|
+
tag 'features_bullets'
|
13
|
+
has_many :features, Feature
|
14
|
+
element :bug, String
|
15
|
+
end
|
16
|
+
|
17
|
+
class Product
|
18
|
+
include HappyMapper
|
19
|
+
|
20
|
+
element :title, String
|
21
|
+
has_one :feature_bullets, FeatureBullet
|
22
|
+
end
|
23
|
+
|
24
|
+
module FamilySearch
|
25
|
+
class Person
|
26
|
+
include HappyMapper
|
27
|
+
|
28
|
+
attribute :version, String
|
29
|
+
attribute :modified, Time
|
30
|
+
attribute :id, String
|
31
|
+
end
|
32
|
+
|
33
|
+
class Persons
|
34
|
+
include HappyMapper
|
35
|
+
has_many :person, Person
|
36
|
+
end
|
37
|
+
|
38
|
+
class FamilyTree
|
39
|
+
include HappyMapper
|
40
|
+
|
41
|
+
tag 'familytree', :root => true
|
42
|
+
attribute :version, String
|
43
|
+
attribute :status_message, String, :tag => 'statusMessage'
|
44
|
+
attribute :status_code, String, :tag => 'statusCode'
|
45
|
+
has_one :persons, Persons
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module FedEx
|
50
|
+
class Address
|
51
|
+
include HappyMapper
|
52
|
+
|
53
|
+
tag 'Address'
|
54
|
+
element :city, String, :tag => 'City'
|
55
|
+
element :state, String, :tag => 'StateOrProvinceCode'
|
56
|
+
element :zip, String, :tag => 'PostalCode'
|
57
|
+
element :countrycode, String, :tag => 'CountryCode'
|
58
|
+
element :residential, Boolean, :tag => 'Residential'
|
59
|
+
end
|
60
|
+
|
61
|
+
class Event
|
62
|
+
include HappyMapper
|
63
|
+
|
64
|
+
tag 'Events'
|
65
|
+
element :timestamp, String, :tag => 'Timestamp'
|
66
|
+
element :eventtype, String, :tag => 'EventType'
|
67
|
+
element :eventdescription, String, :tag => 'EventDescription'
|
68
|
+
has_one :address, Address
|
69
|
+
end
|
70
|
+
|
71
|
+
class PackageWeight
|
72
|
+
include HappyMapper
|
73
|
+
|
74
|
+
tag 'PackageWeight'
|
75
|
+
element :units, String, :tag => 'Units'
|
76
|
+
element :value, Integer, :tag => 'Value'
|
77
|
+
end
|
78
|
+
|
79
|
+
class TrackDetails
|
80
|
+
include HappyMapper
|
81
|
+
|
82
|
+
tag 'TrackDetails'
|
83
|
+
element :tracking_number, String, :tag => 'TrackingNumber'
|
84
|
+
element :status_code, String, :tag => 'StatusCode'
|
85
|
+
element :status_desc, String, :tag => 'StatusDescription'
|
86
|
+
element :carrier_code, String, :tag => 'CarrierCode'
|
87
|
+
element :service_info, String, :tag => 'ServiceInfo'
|
88
|
+
has_one :weight, PackageWeight, :tag => 'PackageWeight'
|
89
|
+
element :est_delivery, String, :tag => 'EstimatedDeliveryTimestamp'
|
90
|
+
has_many :events, Event
|
91
|
+
end
|
92
|
+
|
93
|
+
class Notification
|
94
|
+
include HappyMapper
|
95
|
+
|
96
|
+
tag 'Notifications'
|
97
|
+
element :severity, String, :tag => 'Severity'
|
98
|
+
element :source, String, :tag => 'Source'
|
99
|
+
element :code, Integer, :tag => 'Code'
|
100
|
+
element :message, String, :tag => 'Message'
|
101
|
+
element :localized_message, String, :tag => 'LocalizedMessage'
|
102
|
+
end
|
103
|
+
|
104
|
+
class TransactionDetail
|
105
|
+
include HappyMapper
|
106
|
+
|
107
|
+
tag 'TransactionDetail'
|
108
|
+
element :cust_tran_id, String, :tag => 'CustomerTransactionId'
|
109
|
+
end
|
110
|
+
|
111
|
+
class TrackReply
|
112
|
+
include HappyMapper
|
113
|
+
|
114
|
+
tag 'TrackReply', :root => true
|
115
|
+
element :highest_severity, String, :tag => 'HighestSeverity'
|
116
|
+
element :more_data, Boolean, :tag => 'MoreData'
|
117
|
+
has_many :notifications, Notification, :tag => 'Notifications'
|
118
|
+
has_many :trackdetails, TrackDetails, :tag => 'TrackDetails'
|
119
|
+
has_one :tran_detail, TransactionDetail, :tab => 'TransactionDetail'
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class Place
|
124
|
+
include HappyMapper
|
6
125
|
element :name, String
|
7
126
|
end
|
8
127
|
|
@@ -53,15 +172,17 @@ end
|
|
53
172
|
|
54
173
|
class CurrentWeather
|
55
174
|
include HappyMapper
|
56
|
-
|
57
|
-
|
58
|
-
element :
|
59
|
-
element :
|
175
|
+
|
176
|
+
tag 'ob'
|
177
|
+
element :temperature, Integer, :tag => 'temp'
|
178
|
+
element :feels_like, Integer, :tag => 'feels-like'
|
179
|
+
element :current_condition, String, :tag => 'current-condition', :attributes => {:icon => String}
|
60
180
|
end
|
61
181
|
|
62
182
|
class Address
|
63
183
|
include HappyMapper
|
64
184
|
|
185
|
+
tag 'address', :root => true
|
65
186
|
element :street, String
|
66
187
|
element :postcode, String
|
67
188
|
element :housenumber, String
|
@@ -93,8 +214,7 @@ module GitHub
|
|
93
214
|
class Commit
|
94
215
|
include HappyMapper
|
95
216
|
|
96
|
-
tag "commit"
|
97
|
-
|
217
|
+
tag "commit", :root => true
|
98
218
|
element :url, String
|
99
219
|
element :tree, String
|
100
220
|
element :message, String
|
@@ -171,10 +291,15 @@ describe HappyMapper do
|
|
171
291
|
element.options[:single] = false
|
172
292
|
end
|
173
293
|
|
174
|
-
it "should default tag name to class" do
|
294
|
+
it "should default tag name to lowercase class" do
|
175
295
|
Foo.get_tag_name.should == 'foo'
|
176
296
|
end
|
177
297
|
|
298
|
+
it "should default tag name of class in modules to the last constant lowercase" do
|
299
|
+
module Bar; class Baz; include HappyMapper; end; end
|
300
|
+
Bar::Baz.get_tag_name.should == 'baz'
|
301
|
+
end
|
302
|
+
|
178
303
|
it "should allow setting tag name" do
|
179
304
|
Foo.tag('FooBar')
|
180
305
|
Foo.get_tag_name.should == 'FooBar'
|
@@ -199,139 +324,172 @@ describe HappyMapper do
|
|
199
324
|
end
|
200
325
|
end
|
201
326
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
first.href.should == 'http://roxml.rubyforge.org/'
|
214
|
-
first.hash.should == '19bba2ab667be03a19f67fb67dc56917'
|
215
|
-
first.description.should == 'ROXML - Ruby Object to XML Mapping Library'
|
216
|
-
first.tag.should == 'ruby xml gems mapping'
|
217
|
-
first.time.should == Time.utc(2008, 8, 9, 5, 24, 20)
|
218
|
-
first.others.should == 56
|
219
|
-
first.extended.should == 'ROXML is a Ruby library designed to make it easier for Ruby developers to work with XML. Using simple annotations, it enables Ruby classes to be custom-mapped to XML. ROXML takes care of the marshalling and unmarshalling of mapped attributes so that developers can focus on building first-class Ruby classes.'
|
220
|
-
end
|
327
|
+
it "should parse xml attributes into ruby objects" do
|
328
|
+
posts = Post.parse(fixture_file('posts.xml'))
|
329
|
+
posts.size.should == 20
|
330
|
+
first = posts.first
|
331
|
+
first.href.should == 'http://roxml.rubyforge.org/'
|
332
|
+
first.hash.should == '19bba2ab667be03a19f67fb67dc56917'
|
333
|
+
first.description.should == 'ROXML - Ruby Object to XML Mapping Library'
|
334
|
+
first.tag.should == 'ruby xml gems mapping'
|
335
|
+
first.time.should == Time.utc(2008, 8, 9, 5, 24, 20)
|
336
|
+
first.others.should == 56
|
337
|
+
first.extended.should == 'ROXML is a Ruby library designed to make it easier for Ruby developers to work with XML. Using simple annotations, it enables Ruby classes to be custom-mapped to XML. ROXML takes care of the marshalling and unmarshalling of mapped attributes so that developers can focus on building first-class Ruby classes.'
|
221
338
|
end
|
222
339
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
first.user.screen_name.should == 'jnunemaker'
|
244
|
-
first.user.location.should == 'Mishawaka, IN, US'
|
245
|
-
first.user.description.should == 'Loves his wife, ruby, notre dame football and iu basketball'
|
246
|
-
first.user.profile_image_url.should == 'http://s3.amazonaws.com/twitter_production/profile_images/53781608/Photo_75_normal.jpg'
|
247
|
-
first.user.url.should == 'http://addictedtonew.com'
|
248
|
-
first.user.protected.should be_false
|
249
|
-
first.user.followers_count.should == 486
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
describe "#parse (with xml containing the desired element as root node)" do
|
254
|
-
before do
|
255
|
-
file_contents = File.read(File.dirname(__FILE__) + '/fixtures/address.xml')
|
256
|
-
@address = Address.parse(file_contents, :single => true)
|
257
|
-
end
|
258
|
-
|
259
|
-
it "should properly create objects" do
|
260
|
-
@address.street.should == 'Milchstrasse'
|
261
|
-
@address.postcode.should == '26131'
|
262
|
-
@address.housenumber.should == '23'
|
263
|
-
@address.city.should == 'Oldenburg'
|
264
|
-
@address.country.should == 'Germany'
|
265
|
-
end
|
340
|
+
it "should parse xml elements to ruby objcts" do
|
341
|
+
statuses = Status.parse(fixture_file('statuses.xml'))
|
342
|
+
statuses.size.should == 20
|
343
|
+
first = statuses.first
|
344
|
+
first.id.should == 882281424
|
345
|
+
first.created_at.should == Time.utc(2008, 8, 9, 5, 38, 12)
|
346
|
+
first.source.should == 'web'
|
347
|
+
first.truncated.should be_false
|
348
|
+
first.in_reply_to_status_id.should == 1234
|
349
|
+
first.in_reply_to_user_id.should == 12345
|
350
|
+
first.favorited.should be_false
|
351
|
+
first.user.id.should == 4243
|
352
|
+
first.user.name.should == 'John Nunemaker'
|
353
|
+
first.user.screen_name.should == 'jnunemaker'
|
354
|
+
first.user.location.should == 'Mishawaka, IN, US'
|
355
|
+
first.user.description.should == 'Loves his wife, ruby, notre dame football and iu basketball'
|
356
|
+
first.user.profile_image_url.should == 'http://s3.amazonaws.com/twitter_production/profile_images/53781608/Photo_75_normal.jpg'
|
357
|
+
first.user.url.should == 'http://addictedtonew.com'
|
358
|
+
first.user.protected.should be_false
|
359
|
+
first.user.followers_count.should == 486
|
266
360
|
end
|
267
361
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
it "should properly create objects" do
|
276
|
-
@items.total_results.should == 22
|
277
|
-
@items.total_pages.should == 3
|
278
|
-
first = @items.items[0]
|
279
|
-
second = @items.items[1]
|
280
|
-
first.asin.should == '0321480791'
|
281
|
-
first.detail_page_url.should == 'http://www.amazon.com/gp/redirect.html%3FASIN=0321480791%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0321480791%253FSubscriptionId=dontbeaswoosh'
|
282
|
-
first.manufacturer.should == 'Addison-Wesley Professional'
|
283
|
-
second.asin.should == '047022388X'
|
284
|
-
second.manufacturer.should == 'Wrox'
|
285
|
-
end
|
362
|
+
it "should parse xml containing the desired element as root node" do
|
363
|
+
address = Address.parse(fixture_file('address.xml'), :single => true)
|
364
|
+
address.street.should == 'Milchstrasse'
|
365
|
+
address.postcode.should == '26131'
|
366
|
+
address.housenumber.should == '23'
|
367
|
+
address.city.should == 'Oldenburg'
|
368
|
+
address.country.should == 'Germany'
|
286
369
|
end
|
287
370
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
end
|
371
|
+
it "should parse xml with default namespace (amazon)" do
|
372
|
+
file_contents = fixture_file('pita.xml')
|
373
|
+
items = PITA::Items.parse(file_contents, :single => true)
|
374
|
+
items.total_results.should == 22
|
375
|
+
items.total_pages.should == 3
|
376
|
+
first = items.items[0]
|
377
|
+
second = items.items[1]
|
378
|
+
first.asin.should == '0321480791'
|
379
|
+
first.detail_page_url.should == 'http://www.amazon.com/gp/redirect.html%3FASIN=0321480791%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0321480791%253FSubscriptionId=dontbeaswoosh'
|
380
|
+
first.manufacturer.should == 'Addison-Wesley Professional'
|
381
|
+
second.asin.should == '047022388X'
|
382
|
+
second.manufacturer.should == 'Wrox'
|
301
383
|
end
|
302
384
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
@first = @radars[0]
|
311
|
-
@first.places.size.should == 1
|
312
|
-
@first.places[0].name.should == 'Store'
|
313
|
-
@second = @radars[1]
|
314
|
-
@second.places.size.should == 0
|
315
|
-
@third = @radars[2]
|
316
|
-
@third.places.size.should == 2
|
317
|
-
@third.places[0].name.should == 'Work'
|
318
|
-
@third.places[1].name.should == 'Home'
|
319
|
-
end
|
385
|
+
it "should parse xml that has attributes of elements" do
|
386
|
+
items = CurrentWeather.parse(fixture_file('current_weather.xml'))
|
387
|
+
first = items[0]
|
388
|
+
first.temperature.should == 51
|
389
|
+
first.feels_like.should == 51
|
390
|
+
first.current_condition.should == 'Sunny'
|
391
|
+
first.current_condition.icon.should == 'http://deskwx.weatherbug.com/images/Forecast/icons/cond007.gif'
|
320
392
|
end
|
321
393
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
394
|
+
it "should parse xml with nested elements" do
|
395
|
+
radars = Radar.parse(fixture_file('radar.xml'))
|
396
|
+
first = radars[0]
|
397
|
+
first.places.size.should == 1
|
398
|
+
first.places[0].name.should == 'Store'
|
399
|
+
second = radars[1]
|
400
|
+
second.places.size.should == 0
|
401
|
+
third = radars[2]
|
402
|
+
third.places.size.should == 2
|
403
|
+
third.places[0].name.should == 'Work'
|
404
|
+
third.places[1].name.should == 'Home'
|
405
|
+
end
|
406
|
+
|
407
|
+
it "should parse xml that has elements with dashes" do
|
408
|
+
commit = GitHub::Commit.parse(fixture_file('commit.xml'))
|
409
|
+
commit.message.should == "move commands.rb and helpers.rb into commands/ dir"
|
410
|
+
commit.url.should == "http://github.com/defunkt/github-gem/commit/c26d4ce9807ecf57d3f9eefe19ae64e75bcaaa8b"
|
411
|
+
commit.id.should == "c26d4ce9807ecf57d3f9eefe19ae64e75bcaaa8b"
|
412
|
+
commit.committed_date.should == Date.parse("2008-03-02T16:45:41-08:00")
|
413
|
+
commit.tree.should == "28a1a1ca3e663d35ba8bf07d3f1781af71359b76"
|
414
|
+
end
|
415
|
+
|
416
|
+
it "should parse xml with no namespace" do
|
417
|
+
product = Product.parse(fixture_file('product_no_namespace.xml'), :single => true)
|
418
|
+
product.title.should == "A Title"
|
419
|
+
product.feature_bullets.bug.should == 'This is a bug'
|
420
|
+
product.feature_bullets.features.size.should == 2
|
421
|
+
product.feature_bullets.features[0].name.should == 'This is feature text 1'
|
422
|
+
product.feature_bullets.features[1].name.should == 'This is feature text 2'
|
423
|
+
end
|
424
|
+
|
425
|
+
it "should parse xml with default namespace" do
|
426
|
+
product = Product.parse(fixture_file('product_default_namespace.xml'), :single => true)
|
427
|
+
product.title.should == "A Title"
|
428
|
+
product.feature_bullets.bug.should == 'This is a bug'
|
429
|
+
product.feature_bullets.features.size.should == 2
|
430
|
+
product.feature_bullets.features[0].name.should == 'This is feature text 1'
|
431
|
+
product.feature_bullets.features[1].name.should == 'This is feature text 2'
|
432
|
+
end
|
433
|
+
|
434
|
+
it "should parse xml with single namespace" do
|
435
|
+
product = Product.parse(fixture_file('product_single_namespace.xml'), :single => true)
|
436
|
+
product.title.should == "A Title"
|
437
|
+
product.feature_bullets.bug.should == 'This is a bug'
|
438
|
+
product.feature_bullets.features.size.should == 2
|
439
|
+
product.feature_bullets.features[0].name.should == 'This is feature text 1'
|
440
|
+
product.feature_bullets.features[1].name.should == 'This is feature text 2'
|
441
|
+
end
|
442
|
+
|
443
|
+
it "should parse xml with multiple namespaces" do
|
444
|
+
track = FedEx::TrackReply.parse(fixture_file('multiple_namespaces.xml'))
|
445
|
+
track.highest_severity.should == 'SUCCESS'
|
446
|
+
track.more_data.should be_false
|
447
|
+
notification = track.notifications.first
|
448
|
+
notification.code.should == 0
|
449
|
+
notification.localized_message.should == 'Request was successfully processed.'
|
450
|
+
notification.message.should == 'Request was successfully processed.'
|
451
|
+
notification.severity.should == 'SUCCESS'
|
452
|
+
notification.source.should == 'trck'
|
453
|
+
detail = track.trackdetails.first
|
454
|
+
detail.carrier_code.should == 'FDXG'
|
455
|
+
detail.est_delivery.should == '2009-01-02T00:00:00'
|
456
|
+
detail.service_info.should == 'Ground-Package Returns Program-Domestic'
|
457
|
+
detail.status_code.should == 'OD'
|
458
|
+
detail.status_desc.should == 'On FedEx vehicle for delivery'
|
459
|
+
detail.tracking_number.should == '9611018034267800045212'
|
460
|
+
detail.weight.units.should == 'LB'
|
461
|
+
detail.weight.value.should == 2
|
462
|
+
events = detail.events
|
463
|
+
events.size.should == 10
|
464
|
+
first_event = events[0]
|
465
|
+
first_event.eventdescription.should == 'On FedEx vehicle for delivery'
|
466
|
+
first_event.eventtype.should == 'OD'
|
467
|
+
first_event.timestamp.should == '2009-01-02T06:00:00'
|
468
|
+
first_event.address.city.should == 'WICHITA'
|
469
|
+
first_event.address.countrycode.should == 'US'
|
470
|
+
first_event.address.residential.should be_false
|
471
|
+
first_event.address.state.should == 'KS'
|
472
|
+
first_event.address.zip.should == '67226'
|
473
|
+
last_event = events[-1]
|
474
|
+
last_event.eventdescription.should == 'In FedEx possession'
|
475
|
+
last_event.eventtype.should == 'IP'
|
476
|
+
last_event.timestamp.should == '2008-12-27T09:40:00'
|
477
|
+
last_event.address.city.should == 'LONGWOOD'
|
478
|
+
last_event.address.countrycode.should == 'US'
|
479
|
+
last_event.address.residential.should be_false
|
480
|
+
last_event.address.state.should == 'FL'
|
481
|
+
last_event.address.zip.should == '327506398'
|
482
|
+
track.tran_detail.cust_tran_id.should == '20090102-111321'
|
483
|
+
end
|
484
|
+
|
485
|
+
xit "should parse family search xml" do
|
486
|
+
tree = FamilySearch::FamilyTree.parse(fixture_file('family_tree.xml'))
|
487
|
+
tree.version.should == '1.0.20071213.942'
|
488
|
+
tree.status_message.should == 'OK'
|
489
|
+
tree.status_code.should == '200'
|
490
|
+
# tree.people.size.should == 1
|
491
|
+
# tree.people.first.version.should == '1199378491000'
|
492
|
+
# tree.people.first.modified.should == Time.utc(2008, 1, 3, 16, 41, 31) # 2008-01-03T09:41:31-07:00
|
493
|
+
# tree.people.first.id.should == 'KWQS-BBQ'
|
336
494
|
end
|
337
495
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jnunemaker-happymapper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-01-
|
12
|
+
date: 2009-01-29 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -48,6 +48,7 @@ extra_rdoc_files:
|
|
48
48
|
files:
|
49
49
|
- examples/amazon.rb
|
50
50
|
- examples/current_weather.rb
|
51
|
+
- examples/dashed_elements.rb
|
51
52
|
- examples/post.rb
|
52
53
|
- examples/twitter.rb
|
53
54
|
- happymapper.gemspec
|
@@ -65,8 +66,13 @@ files:
|
|
65
66
|
- spec/fixtures/address.xml
|
66
67
|
- spec/fixtures/commit.xml
|
67
68
|
- spec/fixtures/current_weather.xml
|
69
|
+
- spec/fixtures/family_tree.xml
|
70
|
+
- spec/fixtures/multiple_namespaces.xml
|
68
71
|
- spec/fixtures/pita.xml
|
69
72
|
- spec/fixtures/posts.xml
|
73
|
+
- spec/fixtures/product_default_namespace.xml
|
74
|
+
- spec/fixtures/product_no_namespace.xml
|
75
|
+
- spec/fixtures/product_single_namespace.xml
|
70
76
|
- spec/fixtures/radar.xml
|
71
77
|
- spec/fixtures/statuses.xml
|
72
78
|
- spec/happymapper_attribute_spec.rb
|