lightningdb-happymapper 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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>