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 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|
@@ -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.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/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!}
@@ -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: jnunemaker-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 -08:00
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