happymapper 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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, :use_default_namespace => 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
@@ -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 'aws:ob'
9
- element :temperature, Integer, :tag => 'aws:temp'
10
- element :feels_like, Integer, :tag => 'aws:feels-like'
11
- element :current_condition, String, :tag => 'aws:current-condition', :attributes => {:icon => String}
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|
@@ -7,8 +7,7 @@ module GitHub
7
7
  class Commit
8
8
  include HappyMapper
9
9
 
10
- tag "commit"
11
-
10
+ tag "commit", :root => true
12
11
  element :url, String
13
12
  element :tree, String
14
13
  element :message, String
@@ -17,7 +16,5 @@ module GitHub
17
16
  end
18
17
  end
19
18
 
20
- commits = GitHub::Commit.parse(file_contents)
21
- commits.each do |commit|
22
- puts commit.committed_date, commit.url, commit.id
23
- end
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.1.7"
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-28}
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/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/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", "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", "happymapper.gemspec"]
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!}
@@ -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, self.type, self.tag = name, type, o.delete(:tag) || name.to_s
14
- self.options = {:single => false, :deep => false}.merge(o)
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, namespace = nil)
23
+ def from_xml_node(node)
23
24
  if primitive?
24
- value_from_xml_node(node, namespace) do |value_before_type_cast|
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
- use_default_namespace = !namespace.nil?
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, namespace=nil)
77
- node.register_default_namespace(namespace.chop) if namespace
78
-
88
+ private
89
+ def value_from_xml_node(node)
79
90
  if element?
80
- depth = options[:deep] ? './/' : ''
81
- result = node.find_first("#{depth}#{namespace}#{tag}")
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)
@@ -1,3 +1,3 @@
1
1
  module HappyMapper
2
- Version = '0.1.7'
2
+ Version = '0.2.0'
3
3
  end
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
- create_accessor(attribute.name)
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
- create_accessor(element.name)
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
- def tag(new_tag_name)
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 ||= to_s.downcase
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
- :single => false,
65
- :use_default_namespace => false,
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
- namespace = "default_ns:" if options[:use_default_namespace]
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
- nodes = if namespace
72
- node = doc.respond_to?(:root) ? doc.root : doc
73
- node.register_default_namespace(namespace.chop)
74
- node.find("#{namespace}#{get_tag_name}")
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
- nodes = if namespace
80
- node = doc.respond_to?(:root) ? doc.root : doc
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
- # per http://libxml.rubyforge.org/rdoc/classes/LibXML/XML/Document.html#M000354
92
- nodes = nil
93
- GC.start
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
- options[:single] ? collection.first : collection
96
- end
97
-
98
- private
99
- def create_collection(nodes, namespace=nil)
100
- nodes.inject([]) do |acc, el|
101
- obj = new
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
- create_getter(name)
132
- create_setter(name)
133
- end
112
+ # per http://libxml.rubyforge.org/rdoc/classes/LibXML/XML/Document.html#M000354
113
+ nodes = nil
114
+ GC.start
134
115
 
135
- def normalize_name(name)
136
- name.gsub('-', '_')
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
- # Also:
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="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>
@@ -0,0 +1,10 @@
1
+ <products>
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>
@@ -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
- @attr = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
7
+ @item = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
8
8
  end
9
9
 
10
10
  it "should accept a name" do
11
- @attr.name.should == 'foo'
11
+ @item.name.should == 'foo'
12
12
  end
13
13
 
14
14
  it 'should accept a type' do
15
- @attr.type.should == String
15
+ @item.type.should == String
16
16
  end
17
17
 
18
18
  it 'should accept :tag as an option' do
19
- @attr.tag.should == 'foobar'
19
+ @item.tag.should == 'foobar'
20
20
  end
21
21
 
22
- it 'should provide #name' do
23
- @attr.should respond_to(:name)
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 'should provide #type' do
27
- @attr.should respond_to(:type)
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
- attribute = HappyMapper::Item.new(:foo, String)
54
+ item = HappyMapper::Item.new(:foo, String)
34
55
  [21, '21'].each do |a|
35
- attribute.typecast(a).should == '21'
56
+ item.typecast(a).should == '21'
36
57
  end
37
58
  end
38
59
 
39
60
  it "should work with Integers" do
40
- attribute = HappyMapper::Item.new(:foo, Integer)
61
+ item = HappyMapper::Item.new(:foo, Integer)
41
62
  [21, 21.0, '21'].each do |a|
42
- attribute.typecast(a).should == 21
63
+ item.typecast(a).should == 21
43
64
  end
44
65
  end
45
66
 
46
67
  it "should work with Floats" do
47
- attribute = HappyMapper::Item.new(:foo, Float)
68
+ item = HappyMapper::Item.new(:foo, Float)
48
69
  [21, 21.0, '21'].each do |a|
49
- attribute.typecast(a).should == 21.0
70
+ item.typecast(a).should == 21.0
50
71
  end
51
72
  end
52
73
 
53
74
  it "should work with Times" do
54
- attribute = HappyMapper::Item.new(:foo, Time)
55
- attribute.typecast('2000-01-01 01:01:01.123456').should == Time.local(2000, 1, 1, 1, 1, 1, 123456)
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
- attribute = HappyMapper::Item.new(:foo, Date)
60
- attribute.typecast('2000-01-01').should == Date.new(2000, 1, 1)
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
- attribute = HappyMapper::Item.new(:foo, DateTime)
65
- attribute.typecast('2000-01-01 00:00:00').should == DateTime.new(2000, 1, 1, 0, 0, 0)
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
- attribute = HappyMapper::Item.new(:foo, Boolean)
70
- attribute.typecast('false').should == false
90
+ item = HappyMapper::Item.new(:foo, Boolean)
91
+ item.typecast('false').should == false
71
92
  end
72
93
  end
73
94
  end
@@ -1,8 +1,127 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper.rb'
2
+ require 'pp'
2
3
 
3
- class Place
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
- tag 'aws:ob'
57
- element :temperature, Integer, :tag => 'aws:temp'
58
- element :feels_like, Integer, :tag => 'aws:feels-like'
59
- element :current_condition, String, :tag => 'aws:current-condition', :attributes => {:icon => String}
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
- describe "#parse (with xml attributes mapping to ruby attributes)" do
203
- before do
204
- @posts = Post.parse(File.read(File.dirname(__FILE__) + '/fixtures/posts.xml'))
205
- end
206
-
207
- it "should get the correct number of elements" do
208
- @posts.size.should == 20
209
- end
210
-
211
- it "should properly create objects" do
212
- first = @posts.first
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
- describe "#parse (with xml elements mapping to ruby attributes)" do
224
- before do
225
- @statuses = Status.parse(File.read(File.dirname(__FILE__) + '/fixtures/statuses.xml'))
226
- end
227
-
228
- it "should get the correct number of elements" do
229
- @statuses.size.should == 20
230
- end
231
-
232
- it "should properly create objects" do
233
- first = @statuses.first
234
- first.id.should == 882281424
235
- first.created_at.should == Time.utc(2008, 8, 9, 5, 38, 12)
236
- first.source.should == 'web'
237
- first.truncated.should be_false
238
- first.in_reply_to_status_id.should == 1234
239
- first.in_reply_to_user_id.should == 12345
240
- first.favorited.should be_false
241
- first.user.id.should == 4243
242
- first.user.name.should == 'John Nunemaker'
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
- # TODO: someone please get xml with namespaces working, kthxbai
269
- describe "#parse (with xml that has namespace)" do
270
- before do
271
- file_contents = File.read(File.dirname(__FILE__) + '/fixtures/pita.xml')
272
- @items = PITA::Items.parse(file_contents, :single => true, :use_default_namespace => true)
273
- end
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
- describe "#parse (with xml that has attributes of elements)" do
289
- before do
290
- file_contents = File.read(File.dirname(__FILE__) + '/fixtures/current_weather.xml')
291
- @items = CurrentWeather.parse(file_contents)
292
- end
293
-
294
- it "should properly create objects" do
295
- @first = @items[0]
296
- @first.temperature.should == 51
297
- @first.feels_like.should == 51
298
- @first.current_condition.should == 'Sunny'
299
- @first.current_condition.icon.should == 'http://deskwx.weatherbug.com/images/Forecast/icons/cond007.gif'
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
- describe "#parse (with xml that has nested elements)" do
304
- before do
305
- file_contents = File.read(File.dirname(__FILE__) + '/fixtures/radar.xml')
306
- @radars = Radar.parse(file_contents)
307
- end
308
-
309
- it "should properly create objects" do
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
- describe "#parse (with xml that has elements with dashes in them)" do
323
- before do
324
- file_contents = File.read(File.dirname(__FILE__) + '/fixtures/commit.xml')
325
- @commit = GitHub::Commit.parse(file_contents).first
326
- end
327
-
328
- it "should properly create objects" do
329
- @commit.message.should == "move commands.rb and helpers.rb into commands/ dir"
330
- @commit.url.should == "http://github.com/defunkt/github-gem/commit/c26d4ce9807ecf57d3f9eefe19ae64e75bcaaa8b"
331
- @commit.id.should == "c26d4ce9807ecf57d3f9eefe19ae64e75bcaaa8b"
332
- @commit.committed_date.should == Date.parse("2008-03-02T16:45:41-08:00")
333
- @commit.tree.should == "28a1a1ca3e663d35ba8bf07d3f1781af71359b76"
334
- end
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
@@ -7,3 +7,7 @@ rescue LoadError
7
7
  end
8
8
 
9
9
  require File.join(File.dirname(__FILE__), '..', 'lib', 'happymapper')
10
+
11
+ def fixture_file(filename)
12
+ File.read(File.dirname(__FILE__) + "/fixtures/#{filename}")
13
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: happymapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
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-28 00:00:00 -05:00
12
+ date: 2009-01-29 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -53,7 +53,6 @@ files:
53
53
  - examples/dashed_elements.rb
54
54
  - examples/post.rb
55
55
  - examples/twitter.rb
56
- - happymapper.gemspec
57
56
  - History
58
57
  - lib/happymapper/attribute.rb
59
58
  - lib/happymapper/element.rb
@@ -68,8 +67,13 @@ files:
68
67
  - spec/fixtures/address.xml
69
68
  - spec/fixtures/commit.xml
70
69
  - spec/fixtures/current_weather.xml
70
+ - spec/fixtures/family_tree.xml
71
+ - spec/fixtures/multiple_namespaces.xml
71
72
  - spec/fixtures/pita.xml
72
73
  - spec/fixtures/posts.xml
74
+ - spec/fixtures/product_default_namespace.xml
75
+ - spec/fixtures/product_no_namespace.xml
76
+ - spec/fixtures/product_single_namespace.xml
73
77
  - spec/fixtures/radar.xml
74
78
  - spec/fixtures/statuses.xml
75
79
  - spec/happymapper_attribute_spec.rb
@@ -81,6 +85,7 @@ files:
81
85
  - TODO
82
86
  - website/css/common.css
83
87
  - website/index.html
88
+ - happymapper.gemspec
84
89
  has_rdoc: true
85
90
  homepage: http://happymapper.rubyforge.org
86
91
  post_install_message: May you have many happy mappings!