jimmyz-happymapper 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/History ADDED
@@ -0,0 +1,62 @@
1
+ == 0.3.1
2
+ * 1 minor tweak
3
+ * Added to_xml and namespace_url code to the family_tree.rb example
4
+
5
+ == 0.3.0
6
+ * 5 major enhancements, 2 minor enhancements, 1 bug fixes
7
+ * to_xml methods attach namespaces to the root element and add the namespaces to the elements (if a namespace_url is specified)
8
+ * Modified namespace method to accept a hash {'prefix' => 'http://example.com/v1'} OR 'prefix'
9
+ * Added namespace_url method to set and read a full namespace URL. This adds much better stability in case prefixes change.
10
+ * Added to_xml method to HappyMapper Module to serialize objects to string
11
+ * Added to_xml_node method to HappyMapper Module to serialize objects to LibXML::XML::Node
12
+ * Full support for creating happymapper elements from scratch. has_many instance vars default to []
13
+ * Support for has_many with a primitive type (String, Boolean, etc.)
14
+ * fix for issue #8: http://jnunemaker.lighthouseapp.com/projects/20014-happy-mapper/tickets/8
15
+
16
+ == 0.2.2
17
+ * 2 minor tweaks
18
+ * removed GC.start (libxml recommended this) as setting nodes to nil should be enough, specs run 3-4x faster (Brandon Keepers)
19
+ * renamed get_tag_name to tag_name (Brandon Keepers)
20
+ * removed libxml helpers as they are no longer needed
21
+
22
+ == 0.2.1
23
+ * 1 minor fix, 3 major enhancements
24
+ * fixed warnings about using XML::Parser (mojodna)
25
+ * Improved namespace support, now handles multiple namespaces and allows namespaces to be set item wide or on a per element basis (mojodna)
26
+ * Auto detect root nodes (mojodna)
27
+ * Type coercion (mojodna)
28
+
29
+ == 0.2.0
30
+ * 1 major enhancement, 2 minor ehancements
31
+ * Automatic handling of namespaces (part by Robert Lowrey and rest by John Nunemaker)
32
+ * 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
33
+ * Now defaulting tag names for classes in modules to last constant downcased
34
+
35
+ == 0.1.7 2009-01-29
36
+ * 1 minor enhancement
37
+ * Support dashes in elements (Josh Nichols)
38
+
39
+ == 0.1.6 2009-01-17
40
+ * 1 minor enhancement:
41
+ * added support for nested collection elements (Justin Marney)
42
+
43
+ == 0.1.5 2009-01-05
44
+ * 1 major enhancement:
45
+ * Updated to latest version of libxml-ruby (lightningdb)
46
+
47
+ == 0.1.4 2009-01-05
48
+ * 1 major enhancement:
49
+ * Fixed parsing when the object is the root node. (Garret Alfert)
50
+
51
+ == 0.1.3 2008-12-31
52
+ * 1 major enhancement:
53
+ * Added parsing of attributes of elements that are also mapped, see current_weather.rb for example (jeremyf)
54
+
55
+ == 0.1.2 2008-12-12
56
+ * 1 major enhancement:
57
+ * Fixed that :deep only worked for first item (dvrensk)
58
+
59
+ == 0.1.0 2008-11-16
60
+
61
+ * 1 major enhancement:
62
+ * Initial release
data/License ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 John Nunemaker
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,40 @@
1
+ examples/amazon.rb
2
+ examples/current_weather.rb
3
+ examples/dashed_elements.rb
4
+ examples/family_tree.rb
5
+ examples/post.rb
6
+ examples/twitter.rb
7
+ happymapper.gemspec
8
+ History
9
+ lib/happymapper/attribute.rb
10
+ lib/happymapper/element.rb
11
+ lib/happymapper/item.rb
12
+ lib/happymapper/version.rb
13
+ lib/happymapper.rb
14
+ License
15
+ Manifest
16
+ Rakefile
17
+ README
18
+ spec/fixtures/address.xml
19
+ spec/fixtures/commit.xml
20
+ spec/fixtures/current_weather.xml
21
+ spec/fixtures/family_tree.xml
22
+ spec/fixtures/multiple_namespaces.xml
23
+ spec/fixtures/partial_posts.xml
24
+ spec/fixtures/pita.xml
25
+ spec/fixtures/posts.xml
26
+ spec/fixtures/product_default_namespace.xml
27
+ spec/fixtures/product_no_namespace.xml
28
+ spec/fixtures/product_single_namespace.xml
29
+ spec/fixtures/radar.xml
30
+ spec/fixtures/statuses.xml
31
+ spec/happymapper_attribute_spec.rb
32
+ spec/happymapper_element_spec.rb
33
+ spec/happymapper_item_spec.rb
34
+ spec/happymapper_spec.rb
35
+ spec/spec.opts
36
+ spec/spec_helper.rb
37
+ spec/xml_helper.rb
38
+ TODO
39
+ website/css/common.css
40
+ website/index.html
data/README ADDED
@@ -0,0 +1,26 @@
1
+ = happymapper
2
+
3
+ == DESCRIPTION:
4
+
5
+ Object to xml mapping library. I have included examples to help get you going. The specs
6
+ should also point you in the right direction.
7
+
8
+ == FEATURES:
9
+
10
+ * Easy to define xml attributes and elements for an object
11
+ * Fast because it uses libxml-ruby under the hood
12
+ * Automatic conversion of xml to defined objects
13
+
14
+ == SYNOPSIS:
15
+
16
+ See examples directory in the gem to get a feel for how it works.
17
+
18
+ == INSTALL:
19
+
20
+ * add github to your sources if you haven't gem sources -a http://gems.github.com
21
+ * sudo gem install jnunemaker-happymapper
22
+ * sudo gem install happymapper (when rubyforge approves and i release there)
23
+
24
+ == TICKETS:
25
+
26
+ http://jnunemaker.lighthouseapp.com/projects/20014-happy-mapper/overview
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ ProjectName = 'happymapper'
2
+ WebsitePath = "jnunemaker@rubyforge.org:/var/www/gforge-projects/#{ProjectName}"
3
+
4
+ require 'rubygems'
5
+ require 'rake'
6
+ require 'echoe'
7
+ require 'spec/rake/spectask'
8
+ require "lib/#{ProjectName}/version"
9
+
10
+ Echoe.new(ProjectName, HappyMapper::Version) do |p|
11
+ p.description = "object to xml mapping library"
12
+ p.install_message = "May you have many happy mappings!"
13
+ p.url = "http://#{ProjectName}.rubyforge.org"
14
+ p.author = "John Nunemaker"
15
+ p.email = "nunemaker@gmail.com"
16
+ p.extra_deps = [['libxml-ruby', '= 0.9.8']]
17
+ p.need_tar_gz = false
18
+ p.docs_host = WebsitePath
19
+ end
20
+
21
+ desc 'Upload website files to rubyforge'
22
+ task :website do
23
+ sh %{rsync -av website/ #{WebsitePath}}
24
+ Rake::Task['website_docs'].invoke
25
+ end
26
+
27
+ task :website_docs do
28
+ Rake::Task['redocs'].invoke
29
+ sh %{rsync -av doc/ #{WebsitePath}/docs}
30
+ end
31
+
32
+ desc 'Preps the gem for a new release'
33
+ task :prepare do
34
+ %w[manifest build_gemspec].each do |task|
35
+ Rake::Task[task].invoke
36
+ end
37
+ end
38
+
39
+ Rake::Task[:default].prerequisites.clear
40
+ task :default => :spec
41
+ Spec::Rake::SpecTask.new do |t|
42
+ t.spec_files = FileList["spec/**/*_spec.rb"]
43
+ end
data/TODO ADDED
File without changes
@@ -0,0 +1,34 @@
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/pita.xml')
5
+
6
+ # The document `pita.xml` contains both a default namespace and the 'georss'
7
+ # namespace (for the 'point' element).
8
+ module PITA
9
+ class Item
10
+ include HappyMapper
11
+
12
+ tag 'Item' # if you put class in module you need tag
13
+ element :asin, String, :tag => 'ASIN'
14
+ element :detail_page_url, String, :tag => 'DetailPageURL'
15
+ element :manufacturer, String, :tag => 'Manufacturer', :deep => true
16
+ # this is the only element that exists in a different namespace, so it
17
+ # must be explicitly specified
18
+ element :point, String, :tag => 'point', :namespace => 'georss'
19
+ end
20
+
21
+ class Items
22
+ include HappyMapper
23
+
24
+ tag 'Items' # if you put class in module you need tag
25
+ element :total_results, Integer, :tag => 'TotalResults'
26
+ element :total_pages, Integer, :tag => 'TotalPages'
27
+ has_many :items, Item
28
+ end
29
+ end
30
+
31
+ item = PITA::Items.parse(file_contents, :single => true)
32
+ item.items.each do |i|
33
+ puts i.asin, i.detail_page_url, i.manufacturer, ''
34
+ end
@@ -0,0 +1,21 @@
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/current_weather.xml')
5
+
6
+ class CurrentWeather
7
+ include HappyMapper
8
+
9
+ tag 'ob'
10
+ namespace 'aws'
11
+ element :temperature, Integer, :tag => 'temp'
12
+ element :feels_like, Integer, :tag => 'feels-like'
13
+ element :current_condition, String, :tag => 'current-condition', :attributes => {:icon => String}
14
+ end
15
+
16
+ CurrentWeather.parse(file_contents).each do |current_weather|
17
+ puts "temperature: #{current_weather.temperature}"
18
+ puts "feels_like: #{current_weather.feels_like}"
19
+ puts "current_condition: #{current_weather.current_condition}"
20
+ puts "current_condition.icon: #{current_weather.current_condition.icon}"
21
+ end
@@ -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"
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
@@ -0,0 +1,55 @@
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/family_tree.xml')
5
+
6
+ module FamilySearch
7
+ class AlternateIds
8
+ include HappyMapper
9
+
10
+ tag 'alternateIds'
11
+ namespace 'fsapi-v1' => 'http://api.familysearch.org/v1'
12
+ has_many :ids, String, :tag => 'id'
13
+ end
14
+
15
+ class Information
16
+ include HappyMapper
17
+
18
+ namespace 'fsapi-v1' => 'http://api.familysearch.org/v1'
19
+ has_one :alternateIds, AlternateIds
20
+ end
21
+
22
+ class Person
23
+ include HappyMapper
24
+
25
+ namespace_url 'http://api.familysearch.org/familytree/v1'
26
+ attribute :version, String
27
+ attribute :modified, Time
28
+ attribute :id, String
29
+ has_one :information, Information
30
+ end
31
+
32
+ class Persons
33
+ include HappyMapper
34
+
35
+ namespace_url 'http://api.familysearch.org/familytree/v1'
36
+ has_many :person, Person
37
+ end
38
+
39
+ class FamilyTree
40
+ include HappyMapper
41
+
42
+ tag 'familytree'
43
+ namespace_url 'http://api.familysearch.org/familytree/v1'
44
+ attribute :version, String
45
+ attribute :status_message, String, :tag => 'statusMessage'
46
+ attribute :status_code, String, :tag => 'statusCode'
47
+ has_one :persons, Persons
48
+ end
49
+ end
50
+
51
+ familytree = FamilySearch::FamilyTree.parse(file_contents)
52
+ familytree.persons.person.each do |p|
53
+ puts p.id, p.information.alternateIds.ids, ''
54
+ end
55
+ puts familytree.to_xml
data/examples/post.rb ADDED
@@ -0,0 +1,19 @@
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/posts.xml')
5
+
6
+ class Post
7
+ include HappyMapper
8
+
9
+ attribute :href, String
10
+ attribute :hash, String
11
+ attribute :description, String
12
+ attribute :tag, String
13
+ attribute :time, DateTime
14
+ attribute :others, Integer
15
+ attribute :extended, String
16
+ end
17
+
18
+ posts = Post.parse(file_contents)
19
+ posts.each { |post| puts post.description, post.href, post.extended, '' }
@@ -0,0 +1,37 @@
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/statuses.xml')
5
+
6
+ class User
7
+ include HappyMapper
8
+
9
+ element :id, Integer
10
+ element :name, String
11
+ element :screen_name, String
12
+ element :location, String
13
+ element :description, String
14
+ element :profile_image_url, String
15
+ element :url, String
16
+ element :protected, Boolean
17
+ element :followers_count, Integer
18
+ end
19
+
20
+ class Status
21
+ include HappyMapper
22
+
23
+ element :id, Integer
24
+ element :text, String
25
+ element :created_at, Time
26
+ element :source, String
27
+ element :truncated, Boolean
28
+ element :in_reply_to_status_id, Integer
29
+ element :in_reply_to_user_id, Integer
30
+ element :favorited, Boolean
31
+ has_one :user, User
32
+ end
33
+
34
+ statuses = Status.parse(file_contents)
35
+ statuses.each do |status|
36
+ puts status.user.name, status.user.screen_name, status.text, status.source, ''
37
+ end
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{happymapper}
5
+ s.version = "0.3.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["John Nunemaker"]
9
+ s.date = %q{2009-02-19}
10
+ s.description = %q{object to xml mapping library}
11
+ s.email = %q{nunemaker@gmail.com}
12
+ s.extra_rdoc_files = ["lib/happymapper/attribute.rb", "lib/happymapper/element.rb", "lib/happymapper/item.rb", "lib/happymapper/version.rb", "lib/happymapper.rb", "README", "TODO"]
13
+ s.files = ["examples/amazon.rb", "examples/current_weather.rb", "examples/dashed_elements.rb", "examples/family_tree.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", "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/partial_posts.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", "spec/xml_helper.rb", "TODO", "website/css/common.css", "website/index.html"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://happymapper.rubyforge.org}
16
+ s.post_install_message = %q{May you have many happy mappings!}
17
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Happymapper", "--main", "README"]
18
+ s.require_paths = ["lib"]
19
+ s.rubyforge_project = %q{happymapper}
20
+ s.rubygems_version = %q{1.3.1}
21
+ s.summary = %q{object to xml mapping library}
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 2
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ s.add_runtime_dependency(%q<libxml-ruby>, ["= 0.9.8"])
29
+ else
30
+ s.add_dependency(%q<libxml-ruby>, ["= 0.9.8"])
31
+ end
32
+ else
33
+ s.add_dependency(%q<libxml-ruby>, ["= 0.9.8"])
34
+ end
35
+ end
@@ -0,0 +1,191 @@
1
+ dir = File.dirname(__FILE__)
2
+ $:.unshift(dir) unless $:.include?(dir) || $:.include?(File.expand_path(dir))
3
+
4
+ require 'date'
5
+ require 'time'
6
+ require 'rubygems'
7
+ gem 'libxml-ruby', '= 0.9.8'
8
+ require 'xml'
9
+
10
+ class Boolean; end
11
+
12
+ module HappyMapper
13
+
14
+ DEFAULT_NS = "happymapper"
15
+
16
+ def self.included(base)
17
+ base.instance_variable_set("@attributes", {})
18
+ base.instance_variable_set("@elements", {})
19
+ base.extend ClassMethods
20
+ end
21
+
22
+ def to_xml
23
+ node = to_xml_node
24
+ node.to_s
25
+ end
26
+
27
+ def to_xml_node(root_node = nil)
28
+ node = XML::Node.new(self.class.tag_name)
29
+ root_node ||= node
30
+ if self.class.namespace_url
31
+ if root_node
32
+ namespace_object = root_node.namespaces.find_by_href(self.class.namespace_url)
33
+ namespace_object ||= XML::Namespace.new root_node, self.class.namespace, self.class.namespace_url
34
+ node.namespaces.namespace = namespace_object
35
+ end
36
+ else
37
+ nil
38
+ end
39
+ self.class.elements.each do |e|
40
+ if e.options[:single] == false
41
+ self.send("#{e.method_name}").each do |array_element|
42
+ node << e.to_xml_node(array_element,root_node)
43
+ end
44
+ else
45
+ node << e.to_xml_node(self.send("#{e.method_name}"),root_node)
46
+ end
47
+ end
48
+ self.class.attributes.each do |a|
49
+ attribute_value = self.send("#{a.method_name}")
50
+ node.attributes[a.tag] = attribute_value.to_s unless attribute_value.nil?
51
+ end
52
+ node
53
+ end
54
+
55
+ module ClassMethods
56
+ def attribute(name, type, options={})
57
+ attribute = Attribute.new(name, type, options)
58
+ @attributes[to_s] ||= []
59
+ @attributes[to_s] << attribute
60
+ attr_accessor attribute.method_name.intern
61
+ end
62
+
63
+ def attributes
64
+ @attributes[to_s] || []
65
+ end
66
+
67
+ def element(name, type, options={})
68
+ options = {:namespace => @namespace}.merge(options)
69
+ element = Element.new(name, type, options)
70
+ @elements[to_s] ||= []
71
+ @elements[to_s] << element
72
+ attr_accessor element.method_name.intern
73
+
74
+ # set the default value of a collection instance variable to [] instead of nil
75
+ if options[:single] == false
76
+ module_eval <<-eof
77
+ def #{element.method_name}
78
+ @#{element.method_name} ||= []
79
+ end
80
+ eof
81
+ end
82
+ end
83
+
84
+ def elements
85
+ @elements[to_s] || []
86
+ end
87
+
88
+ def has_one(name, type, options={})
89
+ element name, type, {:single => true}.merge(options)
90
+ end
91
+
92
+ def has_many(name, type, options={})
93
+ element name, type, {:single => false}.merge(options)
94
+ end
95
+
96
+ # Specify a namespace if a node and all its children are all namespaced
97
+ # elements. This is simpler than passing the :namespace option to each
98
+ # defined element.
99
+ #
100
+ # namespace can either be a string for the prefix or a hash with 'prefix' => 'url'
101
+ def namespace(namespace = nil)
102
+ if namespace
103
+ if namespace.is_a? Hash
104
+ namespace.each_pair do |k,v|
105
+ @namespace = k.to_s
106
+ @namespace_url = v
107
+ end
108
+ else
109
+ @namespace = namespace
110
+ end
111
+ end
112
+ @namespace
113
+ end
114
+
115
+ def namespace_url(url = nil)
116
+ @namespace_url = url if url
117
+ @namespace_url
118
+ end
119
+
120
+ def tag(new_tag_name)
121
+ @tag_name = new_tag_name.to_s
122
+ end
123
+
124
+ def tag_name
125
+ @tag_name ||= to_s.split('::')[-1].downcase
126
+ end
127
+
128
+ def parse(xml, options = {})
129
+ # locally scoped copy of namespace for this parse run
130
+ namespace = @namespace
131
+
132
+ if xml.is_a?(XML::Node)
133
+ node = xml
134
+ else
135
+ if xml.is_a?(XML::Document)
136
+ node = xml.root
137
+ else
138
+ node = XML::Parser.string(xml).parse.root
139
+ end
140
+
141
+ root = node.name == tag_name
142
+ end
143
+
144
+ # This is the entry point into the parsing pipeline, so the default
145
+ # namespace prefix registered here will propagate down
146
+ namespaces = node.namespaces
147
+ if @namespace_url && namespaces.default.href != @namespace_url
148
+ namespace = namespaces.find_by_href(@namespace_url).prefix
149
+ elsif namespaces && namespaces.default
150
+ # don't assign the default_prefix if it has already been assigned
151
+ namespaces.default_prefix = DEFAULT_NS unless namespaces.find_by_prefix(DEFAULT_NS)
152
+ namespace ||= DEFAULT_NS
153
+ end
154
+
155
+ xpath = root ? '/' : './/'
156
+ xpath += "#{namespace}:" if namespace
157
+ xpath += tag_name
158
+ # puts "parse: #{xpath}"
159
+
160
+ nodes = node.find(xpath)
161
+ collection = nodes.collect do |n|
162
+ obj = new
163
+
164
+ attributes.each do |attr|
165
+ obj.send("#{attr.method_name}=",
166
+ attr.from_xml_node(n, namespace))
167
+ end
168
+
169
+ elements.each do |elem|
170
+ obj.send("#{elem.method_name}=",
171
+ elem.from_xml_node(n, namespace))
172
+ end
173
+
174
+ obj
175
+ end
176
+
177
+ # per http://libxml.rubyforge.org/rdoc/classes/LibXML/XML/Document.html#M000354
178
+ nodes = nil
179
+
180
+ if options[:single] || root
181
+ collection.first
182
+ else
183
+ collection
184
+ end
185
+ end
186
+ end
187
+ end
188
+
189
+ require 'happymapper/item'
190
+ require 'happymapper/attribute'
191
+ require 'happymapper/element'