jimmyz-happymapper 0.3.1
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 +62 -0
- data/License +20 -0
- data/Manifest +40 -0
- data/README +26 -0
- data/Rakefile +43 -0
- data/TODO +0 -0
- data/examples/amazon.rb +34 -0
- data/examples/current_weather.rb +21 -0
- data/examples/dashed_elements.rb +20 -0
- data/examples/family_tree.rb +55 -0
- data/examples/post.rb +19 -0
- data/examples/twitter.rb +37 -0
- data/happymapper.gemspec +35 -0
- data/lib/happymapper.rb +191 -0
- data/lib/happymapper/attribute.rb +3 -0
- data/lib/happymapper/element.rb +3 -0
- data/lib/happymapper/item.rb +175 -0
- data/lib/happymapper/version.rb +3 -0
- data/spec/fixtures/address.xml +8 -0
- data/spec/fixtures/commit.xml +52 -0
- data/spec/fixtures/current_weather.xml +89 -0
- data/spec/fixtures/family_tree.xml +21 -0
- data/spec/fixtures/multiple_namespaces.xml +170 -0
- data/spec/fixtures/partial_posts.xml +5 -0
- data/spec/fixtures/pita.xml +133 -0
- data/spec/fixtures/posts.xml +23 -0
- data/spec/fixtures/product_default_namespace.xml +17 -0
- data/spec/fixtures/product_no_namespace.xml +10 -0
- data/spec/fixtures/product_single_namespace.xml +10 -0
- data/spec/fixtures/radar.xml +21 -0
- data/spec/fixtures/statuses.xml +422 -0
- data/spec/happymapper_attribute_spec.rb +17 -0
- data/spec/happymapper_element_spec.rb +17 -0
- data/spec/happymapper_item_spec.rb +94 -0
- data/spec/happymapper_spec.rb +666 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/xml_helper.rb +115 -0
- data/website/css/common.css +47 -0
- data/website/index.html +98 -0
- metadata +112 -0
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
|
data/examples/amazon.rb
ADDED
@@ -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, '' }
|
data/examples/twitter.rb
ADDED
@@ -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
|
data/happymapper.gemspec
ADDED
@@ -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
|
data/lib/happymapper.rb
ADDED
@@ -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'
|