dam5s-happymapper 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/History ADDED
@@ -0,0 +1,59 @@
1
+ == 0.2.5
2
+ * 1 minor tweak
3
+ * Classes can now be strings instead of constants so you don't have to worry about class definition order (this was all for technicalpickles, enjoy!)
4
+
5
+ == 0.2.4
6
+ * 1 minor tweak
7
+ * Added a patch that allows even crazy namespaces to work
8
+
9
+ == 0.2.3
10
+ * 1 minor tweak
11
+ * bumped the version of libxml-ruby to 1.1.3
12
+
13
+ == 0.2.2
14
+ * 2 minor tweaks
15
+ * removed GC.start (libxml recommended this) as setting nodes to nil should be enough, specs run 3-4x faster (Brandon Keepers)
16
+ * renamed get_tag_name to tag_name (Brandon Keepers)
17
+ * removed libxml helpers as they are no longer needed
18
+
19
+ == 0.2.1
20
+ * 1 minor fix, 3 major enhancements
21
+ * fixed warnings about using XML::Parser (mojodna)
22
+ * Improved namespace support, now handles multiple namespaces and allows namespaces to be set item wide or on a per element basis (mojodna)
23
+ * Auto detect root nodes (mojodna)
24
+ * Type coercion (mojodna)
25
+
26
+ == 0.2.0
27
+ * 1 major enhancement, 2 minor ehancements
28
+ * Automatic handling of namespaces (part by Robert Lowrey and rest by John Nunemaker)
29
+ * 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
30
+ * Now defaulting tag names for classes in modules to last constant downcased
31
+
32
+ == 0.1.7 2009-01-29
33
+ * 1 minor enhancement
34
+ * Support dashes in elements (Josh Nichols)
35
+
36
+ == 0.1.6 2009-01-17
37
+ * 1 minor enhancement:
38
+ * added support for nested collection elements (Justin Marney)
39
+
40
+ == 0.1.5 2009-01-05
41
+ * 1 major enhancement:
42
+ * Updated to latest version of libxml-ruby (lightningdb)
43
+
44
+ == 0.1.4 2009-01-05
45
+ * 1 major enhancement:
46
+ * Fixed parsing when the object is the root node. (Garret Alfert)
47
+
48
+ == 0.1.3 2008-12-31
49
+ * 1 major enhancement:
50
+ * Added parsing of attributes of elements that are also mapped, see current_weather.rb for example (jeremyf)
51
+
52
+ == 0.1.2 2008-12-12
53
+ * 1 major enhancement:
54
+ * Fixed that :deep only worked for first item (dvrensk)
55
+
56
+ == 0.1.0 2008-11-16
57
+
58
+ * 1 major enhancement:
59
+ * 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,38 @@
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/analytics.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/pita.xml
24
+ spec/fixtures/posts.xml
25
+ spec/fixtures/product_default_namespace.xml
26
+ spec/fixtures/product_no_namespace.xml
27
+ spec/fixtures/product_single_namespace.xml
28
+ spec/fixtures/radar.xml
29
+ spec/fixtures/statuses.xml
30
+ spec/happymapper_attribute_spec.rb
31
+ spec/happymapper_element_spec.rb
32
+ spec/happymapper_item_spec.rb
33
+ spec/happymapper_spec.rb
34
+ spec/spec.opts
35
+ spec/spec_helper.rb
36
+ TODO
37
+ website/css/common.css
38
+ website/index.html
data/README ADDED
@@ -0,0 +1,63 @@
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
+ == EXAMPLES:
17
+
18
+ Here is a simple example that maps Twitter statuses and users.
19
+
20
+ class User
21
+ include HappyMapper
22
+
23
+ element :id, Integer
24
+ element :name, String
25
+ element :screen_name, String
26
+ element :location, String
27
+ element :description, String
28
+ element :profile_image_url, String
29
+ element :url, String
30
+ element :protected, Boolean
31
+ element :followers_count, Integer
32
+ end
33
+
34
+ class Status
35
+ include HappyMapper
36
+
37
+ element :id, Integer
38
+ element :text, String
39
+ element :created_at, Time
40
+ element :source, String
41
+ element :truncated, Boolean
42
+ element :in_reply_to_status_id, Integer
43
+ element :in_reply_to_user_id, Integer
44
+ element :favorited, Boolean
45
+ has_one :user, User
46
+ end
47
+
48
+ See examples directory in the gem for more examples.
49
+
50
+ http://github.com/jnunemaker/happymapper/tree/master/examples/
51
+
52
+ == INSTALL:
53
+
54
+ * sudo gem install jnunemaker-happymapper -s http://gems.github.com
55
+ * sudo gem install happymapper (when rubyforge approves and i release there)
56
+
57
+ == TICKETS:
58
+
59
+ http://github.com/jnunemaker/happymapper/issues/
60
+
61
+ == DOCS:
62
+
63
+ http://rdoc.info/projects/jnunemaker/happymapper
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', '= 1.1.3']]
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.2"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["John Nunemaker", "Dave Bolton", "Damien Le Berrigaud"]
9
+ s.date = %q{2009-08-28}
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>, ["= 1.1.3"])
29
+ s.add_development_dependency(%q<echoe>, [">= 0"])
30
+ else
31
+ s.add_dependency(%q<libxml-ruby>, ["= 1.1.3"])
32
+ s.add_dependency(%q<echoe>, [">= 0"])
33
+ end
34
+ else
35
+ s.add_dependency(%q<libxml-ruby>, ["= 1.1.3"])
36
+ s.add_dependency(%q<echoe>, [">= 0"])
37
+ end
38
+ end
@@ -0,0 +1,145 @@
1
+ dir = File.dirname(__FILE__)
2
+
3
+ require 'date'
4
+ require 'time'
5
+ require 'rubygems'
6
+ gem 'libxml-ruby', '= 1.1.3'
7
+ require 'xml'
8
+
9
+ class Boolean; end
10
+
11
+ module HappyMapper
12
+
13
+ DEFAULT_NS = "happymapper"
14
+
15
+ def self.included(base)
16
+ base.instance_variable_set("@attributes", {})
17
+ base.instance_variable_set("@elements", {})
18
+ base.extend ClassMethods
19
+ end
20
+
21
+ module ClassMethods
22
+ def attribute(name, type, options={})
23
+ attribute = Attribute.new(name, type, options)
24
+ @attributes[to_s] ||= []
25
+ @attributes[to_s] << attribute
26
+ attr_accessor attribute.method_name.intern
27
+ end
28
+
29
+ def attributes
30
+ @attributes[to_s] || []
31
+ end
32
+
33
+ def element(name, type, options={})
34
+ element = Element.new(name, type, options)
35
+ @elements[to_s] ||= []
36
+ @elements[to_s] << element
37
+ attr_accessor element.method_name.intern
38
+ end
39
+
40
+ def elements
41
+ @elements[to_s] || []
42
+ end
43
+
44
+ def text_node(name, type, options={})
45
+ @text_node = TextNode.new(name, type, options)
46
+ attr_accessor @text_node.method_name.intern
47
+ end
48
+
49
+ def has_one(name, type, options={})
50
+ element name, type, {:single => true}.merge(options)
51
+ end
52
+
53
+ def has_many(name, type, options={})
54
+ element name, type, {:single => false}.merge(options)
55
+ end
56
+
57
+ # Specify a namespace if a node and all its children are all namespaced
58
+ # elements. This is simpler than passing the :namespace option to each
59
+ # defined element.
60
+ def namespace(namespace = nil)
61
+ @namespace = namespace if namespace
62
+ @namespace
63
+ end
64
+
65
+ def tag(new_tag_name)
66
+ @tag_name = new_tag_name.to_s unless new_tag_name.nil? || new_tag_name.to_s.empty?
67
+ end
68
+
69
+ def tag_name
70
+ @tag_name ||= to_s.split('::')[-1].downcase
71
+ end
72
+
73
+ def parse(xml, options = {})
74
+ # locally scoped copy of namespace for this parse run
75
+ namespace = @namespace
76
+
77
+ if xml.is_a?(XML::Node)
78
+ node = xml
79
+ else
80
+ if xml.is_a?(XML::Document)
81
+ node = xml.root
82
+ else
83
+ node = XML::Parser.string(xml).parse.root
84
+ end
85
+
86
+ root = node.name == tag_name
87
+ end
88
+
89
+ # This is the entry point into the parsing pipeline, so the default
90
+ # namespace prefix registered here will propagate down
91
+ namespaces = node.namespaces
92
+ if namespaces && namespaces.default
93
+ # don't assign the default_prefix if it has already been assigned
94
+ namespaces.default_prefix = DEFAULT_NS unless namespaces.find_by_prefix(DEFAULT_NS)
95
+ namespace ||= DEFAULT_NS
96
+ end
97
+
98
+ xpath = root ? '/' : './/'
99
+ xpath += "#{namespace}:" if namespace
100
+ #puts "parse: #{xpath}"
101
+
102
+ nodes = []
103
+ # when finding nodes, do it in this order:
104
+ # 1. specified tag
105
+ # 2. name of element
106
+ # 3. tag_name (derived from class name by default)
107
+ [options[:tag], options[:name], tag_name].compact.each do |xpath_ext|
108
+ nodes = node.find(xpath + xpath_ext.to_s)
109
+ break if nodes && nodes.size > 0
110
+ end
111
+ collection = nodes.collect do |n|
112
+ obj = new
113
+
114
+ attributes.each do |attr|
115
+ obj.send("#{attr.method_name}=",
116
+ attr.from_xml_node(n, namespace))
117
+ end
118
+
119
+ elements.each do |elem|
120
+ obj.send("#{elem.method_name}=",
121
+ elem.from_xml_node(n, namespace))
122
+ end
123
+
124
+ obj.send("#{@text_node.method_name}=",
125
+ @text_node.from_xml_node(n, namespace)) if @text_node
126
+
127
+ obj
128
+ end
129
+
130
+ # per http://libxml.rubyforge.org/rdoc/classes/LibXML/XML/Document.html#M000354
131
+ nodes = nil
132
+
133
+ if options[:single] || root
134
+ collection.first
135
+ else
136
+ collection
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ require File.join(dir, 'happymapper/item')
143
+ require File.join(dir, 'happymapper/attribute')
144
+ require File.join(dir, 'happymapper/element')
145
+ require File.join(dir, 'happymapper/text_node')