lightningdb-happymapper 0.3.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 ADDED
@@ -0,0 +1,47 @@
1
+ == 0.2.2
2
+ * 2 minor tweaks
3
+ * removed GC.start (libxml recommended this) as setting nodes to nil should be enough, specs run 3-4x faster (Brandon Keepers)
4
+ * renamed get_tag_name to tag_name (Brandon Keepers)
5
+ * removed libxml helpers as they are no longer needed
6
+
7
+ == 0.2.1
8
+ * 1 minor fix, 3 major enhancements
9
+ * fixed warnings about using XML::Parser (mojodna)
10
+ * Improved namespace support, now handles multiple namespaces and allows namespaces to be set item wide or on a per element basis (mojodna)
11
+ * Auto detect root nodes (mojodna)
12
+ * Type coercion (mojodna)
13
+
14
+ == 0.2.0
15
+ * 1 major enhancement, 2 minor ehancements
16
+ * Automatic handling of namespaces (part by Robert Lowrey and rest by John Nunemaker)
17
+ * 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
18
+ * Now defaulting tag names for classes in modules to last constant downcased
19
+
20
+ == 0.1.7 2009-01-29
21
+ * 1 minor enhancement
22
+ * Support dashes in elements (Josh Nichols)
23
+
24
+ == 0.1.6 2009-01-17
25
+ * 1 minor enhancement:
26
+ * added support for nested collection elements (Justin Marney)
27
+
28
+ == 0.1.5 2009-01-05
29
+ * 1 major enhancement:
30
+ * Updated to latest version of libxml-ruby (lightningdb)
31
+
32
+ == 0.1.4 2009-01-05
33
+ * 1 major enhancement:
34
+ * Fixed parsing when the object is the root node. (Garret Alfert)
35
+
36
+ == 0.1.3 2008-12-31
37
+ * 1 major enhancement:
38
+ * Added parsing of attributes of elements that are also mapped, see current_weather.rb for example (jeremyf)
39
+
40
+ == 0.1.2 2008-12-12
41
+ * 1 major enhancement:
42
+ * Fixed that :deep only worked for first item (dvrensk)
43
+
44
+ == 0.1.0 2008-11-16
45
+
46
+ * 1 major enhancement:
47
+ * 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,37 @@
1
+ examples/amazon.rb
2
+ examples/current_weather.rb
3
+ examples/dashed_elements.rb
4
+ examples/post.rb
5
+ examples/twitter.rb
6
+ happymapper.gemspec
7
+ History
8
+ lib/happymapper/attribute.rb
9
+ lib/happymapper/element.rb
10
+ lib/happymapper/item.rb
11
+ lib/happymapper/version.rb
12
+ lib/happymapper.rb
13
+ License
14
+ Manifest
15
+ Rakefile
16
+ README
17
+ spec/fixtures/address.xml
18
+ spec/fixtures/commit.xml
19
+ spec/fixtures/current_weather.xml
20
+ spec/fixtures/family_tree.xml
21
+ spec/fixtures/multiple_namespaces.xml
22
+ spec/fixtures/pita.xml
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
27
+ spec/fixtures/radar.xml
28
+ spec/fixtures/statuses.xml
29
+ spec/happymapper_attribute_spec.rb
30
+ spec/happymapper_element_spec.rb
31
+ spec/happymapper_item_spec.rb
32
+ spec/happymapper_spec.rb
33
+ spec/spec.opts
34
+ spec/spec_helper.rb
35
+ TODO
36
+ website/css/common.css
37
+ website/index.html
data/README ADDED
@@ -0,0 +1,28 @@
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
+ * Reusable classes via a node finding mechanism that searches by 1. specified tag,
14
+ 2. name of element, 3. class name. (gemspec was upgraded to 0.3.0 for this change)
15
+
16
+ == SYNOPSIS:
17
+
18
+ See examples directory in the gem to get a feel for how it works.
19
+
20
+ == INSTALL:
21
+
22
+ * add github to your sources if you haven't gem sources -a http://gems.github.com
23
+ * sudo gem install jnunemaker-happymapper
24
+ * sudo gem install happymapper (when rubyforge approves and i release there)
25
+
26
+ == TICKETS:
27
+
28
+ 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
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,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{happymapper}
5
+ s.version = "0.3.0"
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-01-29}
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/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/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/quarters.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
+ 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
+ s.add_development_dependency(%q<echoe>, [">= 0"])
30
+ else
31
+ s.add_dependency(%q<libxml-ruby>, ["= 0.9.8"])
32
+ s.add_dependency(%q<echoe>, [">= 0"])
33
+ end
34
+ else
35
+ s.add_dependency(%q<libxml-ruby>, ["= 0.9.8"])
36
+ s.add_dependency(%q<echoe>, [">= 0"])
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module HappyMapper
2
+ class Attribute < Item; end
3
+ end
@@ -0,0 +1,3 @@
1
+ module HappyMapper
2
+ class Element < Item; end
3
+ end
@@ -0,0 +1,148 @@
1
+ module HappyMapper
2
+ class Item
3
+ attr_accessor :name, :type, :tag, :options, :namespace
4
+
5
+ Types = [String, Float, Time, Date, DateTime, Integer, Boolean]
6
+
7
+ # options:
8
+ # :deep => Boolean False to only parse element's children, True to include
9
+ # grandchildren and all others down the chain (// in xpath)
10
+ # :namespace => String Element's namespace if it's not the global or inherited
11
+ # default
12
+ # :parser => Symbol Class method to use for type coercion.
13
+ # :raw => Boolean Use raw node value (inc. tags) when parsing.
14
+ # :single => Boolean False if object should be collection, True for single object
15
+ # :tag => String Element name if it doesn't match the specified name.
16
+ def initialize(name, type, o={})
17
+ self.name = name.to_s
18
+ self.type = type
19
+ #self.tag = o.delete(:tag) || name.to_s
20
+ self.tag = o[:tag] || name.to_s
21
+ self.options = o.merge(:name => self.name)
22
+
23
+ @xml_type = self.class.to_s.split('::').last.downcase
24
+ end
25
+
26
+ def from_xml_node(node, namespace)
27
+ if primitive?
28
+ find(node, namespace) do |n|
29
+ if n.respond_to?(:content)
30
+ typecast(n.content)
31
+ else
32
+ typecast(n.to_s)
33
+ end
34
+ end
35
+ else
36
+ if options[:parser]
37
+ find(node, namespace) do |n|
38
+ if n.respond_to?(:content) && !options[:raw]
39
+ value = n.content
40
+ else
41
+ value = n.to_s
42
+ end
43
+
44
+ begin
45
+ type.send(options[:parser].to_sym, value)
46
+ rescue
47
+ nil
48
+ end
49
+ end
50
+ else
51
+ type.parse(node, options)
52
+ end
53
+ end
54
+ end
55
+
56
+ def xpath(namespace = self.namespace)
57
+ xpath = ''
58
+ xpath += './/' if options[:deep]
59
+ xpath += "#{namespace}:" if namespace
60
+ xpath += tag
61
+ # puts "xpath: #{xpath}"
62
+ xpath
63
+ end
64
+
65
+ def primitive?
66
+ Types.include?(type)
67
+ end
68
+
69
+ def element?
70
+ @xml_type == 'element'
71
+ end
72
+
73
+ def attribute?
74
+ !element?
75
+ end
76
+
77
+ def method_name
78
+ @method_name ||= name.tr('-', '_')
79
+ end
80
+
81
+ def typecast(value)
82
+ return value if value.kind_of?(type) || value.nil?
83
+ begin
84
+ if type == String then value.to_s
85
+ elsif type == Float then value.to_f
86
+ elsif type == Time then Time.parse(value.to_s)
87
+ elsif type == Date then Date.parse(value.to_s)
88
+ elsif type == DateTime then DateTime.parse(value.to_s)
89
+ elsif type == Boolean then ['true', 't', '1'].include?(value.to_s.downcase)
90
+ elsif type == Integer
91
+ # ganked from datamapper
92
+ value_to_i = value.to_i
93
+ if value_to_i == 0 && value != '0'
94
+ value_to_s = value.to_s
95
+ begin
96
+ Integer(value_to_s =~ /^(\d+)/ ? $1 : value_to_s)
97
+ rescue ArgumentError
98
+ nil
99
+ end
100
+ else
101
+ value_to_i
102
+ end
103
+ else
104
+ value
105
+ end
106
+ rescue
107
+ value
108
+ end
109
+ end
110
+
111
+ private
112
+ def find(node, namespace, &block)
113
+ # this node has a custom namespace (that is present in the doc)
114
+ if self.namespace && node.namespaces.find_by_prefix(self.namespace)
115
+ # from the class definition
116
+ namespace = self.namespace
117
+ elsif options[:namespace] && node.namespaces.find_by_prefix(options[:namespace])
118
+ # from an element definition
119
+ namespace = options[:namespace]
120
+ end
121
+
122
+ if element?
123
+ result = node.find_first(xpath(namespace))
124
+ # puts "vfxn: #{xpath} #{result.inspect}"
125
+ if result
126
+ value = yield(result)
127
+ if options[:attributes].is_a?(Hash)
128
+ result.attributes.each do |xml_attribute|
129
+ if attribute_options = options[:attributes][xml_attribute.name.to_sym]
130
+ attribute_value = Attribute.new(xml_attribute.name.to_sym, *attribute_options).from_xml_node(result, namespace)
131
+ result.instance_eval <<-EOV
132
+ def value.#{xml_attribute.name}
133
+ #{attribute_value.inspect}
134
+ end
135
+ EOV
136
+ end
137
+ end
138
+ end
139
+ value
140
+ else
141
+ nil
142
+ end
143
+ else
144
+ yield(node[tag])
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,3 @@
1
+ module HappyMapper
2
+ Version = '0.2.2'
3
+ end
@@ -0,0 +1,137 @@
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
+ module ClassMethods
23
+ def attribute(name, type, options={})
24
+ attribute = Attribute.new(name, type, options)
25
+ @attributes[to_s] ||= []
26
+ @attributes[to_s] << attribute
27
+ attr_accessor attribute.method_name.intern
28
+ end
29
+
30
+ def attributes
31
+ @attributes[to_s] || []
32
+ end
33
+
34
+ def element(name, type, options={})
35
+ element = Element.new(name, type, options)
36
+ @elements[to_s] ||= []
37
+ @elements[to_s] << element
38
+ attr_accessor element.method_name.intern
39
+ end
40
+
41
+ def elements
42
+ @elements[to_s] || []
43
+ end
44
+
45
+ def has_one(name, type, options={})
46
+ element name, type, {:single => true}.merge(options)
47
+ end
48
+
49
+ def has_many(name, type, options={})
50
+ element name, type, {:single => false}.merge(options)
51
+ end
52
+
53
+ # Specify a namespace if a node and all its children are all namespaced
54
+ # elements. This is simpler than passing the :namespace option to each
55
+ # defined element.
56
+ def namespace(namespace = nil)
57
+ @namespace = namespace if namespace
58
+ @namespace
59
+ end
60
+
61
+ def tag(new_tag_name)
62
+ @tag_name = new_tag_name.to_s unless new_tag_name.nil? || new_tag_name.to_s.empty?
63
+ end
64
+
65
+ def tag_name
66
+ @tag_name ||= to_s.split('::')[-1].downcase
67
+ end
68
+
69
+ def parse(xml, options = {})
70
+ # locally scoped copy of namespace for this parse run
71
+ namespace = @namespace
72
+
73
+ if xml.is_a?(XML::Node)
74
+ node = xml
75
+ else
76
+ if xml.is_a?(XML::Document)
77
+ node = xml.root
78
+ else
79
+ node = XML::Parser.string(xml).parse.root
80
+ end
81
+
82
+ root = node.name == tag_name
83
+ end
84
+
85
+ # This is the entry point into the parsing pipeline, so the default
86
+ # namespace prefix registered here will propagate down
87
+ namespaces = node.namespaces
88
+ if namespaces && namespaces.default
89
+ # don't assign the default_prefix if it has already been assigned
90
+ namespaces.default_prefix = DEFAULT_NS unless namespaces.find_by_prefix(DEFAULT_NS)
91
+ namespace ||= DEFAULT_NS
92
+ end
93
+
94
+ xpath = root ? '/' : './/'
95
+ xpath += "#{namespace}:" if namespace
96
+ #puts "parse: #{xpath}"
97
+
98
+ nodes = []
99
+ # when finding nodes, do it in this order:
100
+ # 1. specified tag
101
+ # 2. name of element
102
+ # 3. tag_name (derived from class name by default)
103
+ [options[:tag], options[:name], tag_name].compact.each do |xpath_ext|
104
+ nodes = node.find(xpath + xpath_ext.to_s)
105
+ break if nodes && nodes.size > 0
106
+ end
107
+ collection = nodes.collect do |n|
108
+ obj = new
109
+
110
+ attributes.each do |attr|
111
+ obj.send("#{attr.method_name}=",
112
+ attr.from_xml_node(n, namespace))
113
+ end
114
+
115
+ elements.each do |elem|
116
+ obj.send("#{elem.method_name}=",
117
+ elem.from_xml_node(n, namespace))
118
+ end
119
+
120
+ obj
121
+ end
122
+
123
+ # per http://libxml.rubyforge.org/rdoc/classes/LibXML/XML/Document.html#M000354
124
+ nodes = nil
125
+
126
+ if options[:single] || root
127
+ collection.first
128
+ else
129
+ collection
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ require 'happymapper/item'
136
+ require 'happymapper/attribute'
137
+ require 'happymapper/element'
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <address>
3
+ <street>Milchstrasse</street>
4
+ <housenumber>23</housenumber>
5
+ <postcode>26131</postcode>
6
+ <city>Oldenburg</city>
7
+ <country code="de">Germany</country>
8
+ </address>