xapper 0.1.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/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+
4
+ gem "nokogiri"
5
+ gem "rspec"
6
+ gem "rspec-mocks"
7
+ gem "jeweler"
@@ -0,0 +1,28 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.3)
5
+ git (1.2.5)
6
+ jeweler (1.6.4)
7
+ bundler (~> 1.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ nokogiri (1.5.0)
11
+ rake (0.9.2)
12
+ rspec (2.7.0)
13
+ rspec-core (~> 2.7.0)
14
+ rspec-expectations (~> 2.7.0)
15
+ rspec-mocks (~> 2.7.0)
16
+ rspec-core (2.7.0)
17
+ rspec-expectations (2.7.0)
18
+ diff-lcs (~> 1.1.2)
19
+ rspec-mocks (2.7.0)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ jeweler
26
+ nokogiri
27
+ rspec
28
+ rspec-mocks
@@ -0,0 +1,144 @@
1
+ # Xapper - another XML mapper for Ruby
2
+
3
+ A simple mapper that takes a mapping and converts your ugly-XML to a lovely-ruby hash.
4
+
5
+ ## Install
6
+
7
+
8
+ Use [Bundler](http://gembundler.com/) + git, add this to your `Gemfile`
9
+
10
+ gem "xapper"
11
+
12
+ In your code
13
+
14
+ require "xapper"
15
+
16
+ ## Usage
17
+
18
+ ### Setup
19
+
20
+ Get a mapper object
21
+
22
+ mapper = Xapper::Mapper.new
23
+
24
+ And some hopefully less contrived XML
25
+
26
+ <response type="book_list">
27
+ <title>A list of books on my shelf</title>
28
+ <books>
29
+ <book author="Boris Akunin">
30
+ <title>The Winter Queen</title>
31
+ </book>
32
+ <book author="Neil Gaiman">
33
+ <title>Neverwhere</title>
34
+ </book>
35
+ <book author="Hermann Hesse">
36
+ <title>Steppenwolf</title>
37
+ </book>
38
+ </books>
39
+ </response>
40
+
41
+ ### Mapping text values and attributes using xpath
42
+
43
+ mapper.mappings = {
44
+ :type => "/response/@type",
45
+ :title => "/response/title"
46
+ }
47
+
48
+ data = mapper.map(xml)
49
+
50
+ data[:type] #=> "book_list"
51
+ data[:title] #=> "A list of books on my shelf"
52
+
53
+ ### Mapping lists of XML nodes into arrays of hashes
54
+
55
+ mapper.mappings = {
56
+ :books => ["/response/books/book", {
57
+ :title => "title",
58
+ :author => "@author"
59
+ }]
60
+ }
61
+
62
+ data = mapper.map(xml)
63
+
64
+ data[:books].size #=> 3
65
+ data[:books][0][:title] #=> "The Winter Queen"
66
+ data[:books][0][:author] #=> "Boris Akunin"
67
+
68
+
69
+ ### Using a lambda to convert values
70
+
71
+ You can map via a lambda, the current [Nokogiri](http://nokogiri.org/) node is passed as an argument
72
+
73
+ mapper.mappings = {
74
+ :books => ["/response/books/book", {
75
+ :title => "title",
76
+ :author => {
77
+ :name => "@author",
78
+ :wiki => lambda do |node|
79
+ "http://en.wikipedia.org/wiki/" + node.attribute('author').value.gsub(" ", "_")
80
+ end
81
+ }
82
+ }]
83
+ }
84
+
85
+ data = mapper.map(xml)
86
+ data[:books][0][:author][:wiki] #=> "http://en.wikipedia.org/wiki/Boris_Akunin"
87
+
88
+ ### Using namespaces
89
+
90
+ You can map XML namespaces, the mapper just needs to know about them first
91
+
92
+ xml = %q{
93
+ <root xmlns:h="http://www.w3.org/TR/html4/" xmlns:f="http://www.w3schools.com/furniture">
94
+ <h:table>
95
+ <h:tr>
96
+ <h:td>Apples</h:td>
97
+ <h:td>Bananas</h:td>
98
+ </h:tr>
99
+ </h:table>
100
+ <f:table>
101
+ <f:name>African Coffee Table</f:name>
102
+ <f:width>80</f:width>
103
+ <f:length>120</f:length>
104
+ </f:table>
105
+ </root>
106
+ }
107
+
108
+ mapper.mappings = {
109
+ :html_table_rows => ["/root/h:table/h:tr/h:td", { :text => "." }],
110
+ :an_actual_table => {
111
+ :name => "/root/f:table/f:name",
112
+ :width => "/root/f:table/f:width",
113
+ :length => "/root/f:table/f:length"
114
+ }
115
+ }
116
+
117
+ mapper.namespaces = {
118
+ "h" => "http://www.w3.org/TR/html4/",
119
+ "f" => "http://www.w3schools.com/furniture"
120
+ }
121
+
122
+ data = mapper.map(xml)
123
+
124
+ data[:html_table_rows].size # => 2
125
+ data[:html_table_rows][0][:text] # => "Apples"
126
+
127
+ data[:an_actual_table][:name] # => "African Coffee Table"
128
+ data[:an_actual_table][:width] # => "80"
129
+
130
+ ## Examples
131
+
132
+ See `examples/*.rb`, run with `rake examples`
133
+
134
+ ## Development
135
+
136
+ Please send new code in the form of a pull requests with tests. Run the current test suite with ...
137
+
138
+ rake spec # Runs spec/*_spec.rb
139
+
140
+ ## Contributors:
141
+
142
+ * [Matt Haynes](https://github.com/matth)
143
+ * [Anuj Dutta](https://github.com/andhapp/)
144
+ * [Jim Lynn](https://github.com/JimLynn)
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ gem.name = "xapper"
15
+ gem.homepage = "http://github.com/bbcsnippets/xapper"
16
+ gem.license = "MIT"
17
+ gem.summary = %Q{A simple mapper that takes an XML and maps it into a ruby hash}
18
+ gem.description = %Q{Give it an xml and define a mapping for it as a ruby hash and there you have it. A ruby hash for your XML}
19
+ gem.email = "dutta.anuj@gmail.com"
20
+ gem.authors = ["andhapp", "matth"]
21
+ end
22
+ Jeweler::RubygemsDotOrgTasks.new
23
+
24
+ require 'rspec/core'
25
+ require 'rspec/core/rake_task'
26
+ RSpec::Core::RakeTask.new(:spec) do |spec|
27
+ spec.pattern = FileList['spec/**/*_spec.rb']
28
+ end
29
+
30
+ task :examples do
31
+ $: << "."
32
+ require "examples/book_list"
33
+ end
34
+
35
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,41 @@
1
+ require "xapper"
2
+ require "pp"
3
+
4
+ xml = %q{
5
+ <response type="book_list">
6
+ <title>A list of books on my shelf</title>
7
+ <books>
8
+ <book author="Boris Akunin">
9
+ <title>The Winter Queen</title>
10
+ </book>
11
+ <book author="Neil Gaiman">
12
+ <title>Neverwhere</title>
13
+ </book>
14
+ <book author="Hermann Hesse">
15
+ <title>Steppenwolf</title>
16
+ </book>
17
+ </books>
18
+ </response>
19
+ }
20
+
21
+ puts "Mapping: \n #{xml}"
22
+
23
+ mapper = Xapper::Mapper.new
24
+
25
+ mapper.mappings = {
26
+ :type => "/response/@type",
27
+ :title => "/response/title",
28
+ :books => ["/response/books/book", {
29
+ :title => "title",
30
+ :author => {
31
+ :name => "@author",
32
+ :wiki => lambda do |node|
33
+ "http://en.wikipedia.org/wiki/" + node.attribute('author').value.gsub(" ", "_")
34
+ end
35
+ }
36
+ }]
37
+ }
38
+
39
+ puts "\nTo:\n\n"
40
+
41
+ pp mapper.map(xml)
@@ -0,0 +1,72 @@
1
+ require "nokogiri"
2
+
3
+ module Xapper
4
+
5
+ @@mappings = {}
6
+
7
+ def mappings(hash, namespaces = nil)
8
+ mapper = Mapper.new
9
+ mapper.mappings = hash[hash.keys[0]]
10
+ mapper.namespaces = namespaces
11
+ @@mappings[hash.keys[0]] = mapper
12
+ end
13
+
14
+ def map(mapping, xml)
15
+ @@mappings[mapping].map(xml)
16
+ end
17
+
18
+ class Mapper
19
+
20
+ attr_accessor :mappings
21
+ attr_accessor :namespaces
22
+
23
+ def map(xml_str)
24
+ xml = Nokogiri::XML(xml_str)
25
+ map_xml_data(xml, @mappings, {})
26
+ end
27
+
28
+ protected
29
+
30
+ def map_xml_data(xml, mappings, data)
31
+
32
+ mappings.each_pair do |key, val|
33
+
34
+ # We have a proc object
35
+ if val.is_a? Proc
36
+ data[key] = val.call xml
37
+ next
38
+ end
39
+
40
+ # Recurse on hash
41
+ if val.is_a? Hash
42
+ data[key] = map_xml_data(xml, val, {})
43
+ next
44
+ end
45
+
46
+ if val.is_a? Array
47
+ xpath = val[0]
48
+ submap = val[1]
49
+ items = xml.xpath(xpath, @namespaces)
50
+ data[key] = items.map {|node| map_xml_data(node, submap, {})}
51
+ next
52
+ end
53
+
54
+ # We have an xpath to lookup
55
+ node = xml.xpath(val,@namespaces)[0]
56
+
57
+ if node.class == Nokogiri::XML::Element
58
+ data[key] = node.content
59
+ elsif node.class == Nokogiri::XML::Attr
60
+ data[key] = node.value
61
+ else
62
+ data[key] = nil
63
+ end
64
+
65
+ end
66
+
67
+ data
68
+
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,175 @@
1
+ require 'spec_helper'
2
+
3
+ describe Xapper::Mapper do
4
+
5
+ before do
6
+ @mapper = Xapper::Mapper.new
7
+ end
8
+
9
+ describe '#map' do
10
+
11
+ it 'should map xml node values' do
12
+ xml = %q{
13
+ <?xml version="1.0" encoding="UTF-8"?>
14
+ <foo>
15
+ <bar>Hello world</bar>
16
+ <baz>Goodbye world</baz>
17
+ </foo>
18
+ }
19
+ @mapper.mappings = {
20
+ :bar => '/foo/bar',
21
+ :baz => '/foo/baz'
22
+ }
23
+ data = @mapper.map xml
24
+ data[:bar].should == 'Hello world'
25
+ data[:baz].should == 'Goodbye world'
26
+ end
27
+
28
+ it 'should map xml attribute values' do
29
+ xml = %q{
30
+ <?xml version="1.0" encoding="UTF-8"?>
31
+ <foo>
32
+ <bar text="Hello world" />
33
+ <baz text="Goodbye world" />
34
+ </foo>
35
+ }
36
+ @mapper.mappings = {
37
+ :bar => '/foo/bar/@text',
38
+ :baz => '/foo/baz/@text'
39
+ }
40
+ data = @mapper.map xml
41
+ data[:bar].should == 'Hello world'
42
+ data[:baz].should == 'Goodbye world'
43
+ end
44
+
45
+ it 'should map the result of a proc object if given, by passing currnent xml context as value' do
46
+ xml = %q{
47
+ <?xml version="1.0" encoding="UTF-8"?>
48
+ <foo>
49
+ <bar attr="BAR1">Text value</bar>
50
+ <bar attr="BAR2">Text value</bar>
51
+ </foo>
52
+ }
53
+ @mapper.mappings = {
54
+ :bar => {
55
+ :attributes => lambda do |xml|
56
+ xml.xpath('//bar').map { |node| node.attribute('attr') }.join(',')
57
+ end
58
+ }
59
+ }
60
+ data = @mapper.map xml
61
+ data[:bar][:attributes].should == 'BAR1,BAR2'
62
+
63
+ end
64
+
65
+ it 'should map a node to nil if it does not exist' do
66
+ xml = %q{
67
+ <?xml version="1.0" encoding="UTF-8"?>
68
+ <foo><bar /></foo>
69
+ }
70
+ @mapper.mappings = {
71
+ :bar => "/foo/bar/@attr",
72
+ :baz => "/foo/baz"
73
+ }
74
+ data = @mapper.map xml
75
+ data[:bar].should == nil
76
+ data[:baz].should == nil
77
+ end
78
+
79
+ it 'should map an multiple xml elements to an array' do
80
+ xml = %q{
81
+ <?xml version="1.0" encoding="UTF-8"?>
82
+ <foo>
83
+ <bar>
84
+ <baz attr="Attr value 1">Text value 1</bar>
85
+ </bar>
86
+ <bar>
87
+ <baz attr="Attr value 2">Text value 2</bar>
88
+ </bar>
89
+ </foo>
90
+ }
91
+ @mapper.mappings = {
92
+ :bar => ['/foo/bar', {
93
+ :baz => {
94
+ :attr => 'baz/@attr',
95
+ :text => 'baz'
96
+ }
97
+ }]
98
+ }
99
+ data = @mapper.map xml
100
+ data[:bar].size.should == 2
101
+ data[:bar][0][:baz][:attr].should == "Attr value 1"
102
+ data[:bar][0][:baz][:text].should == "Text value 1"
103
+ data[:bar][1][:baz][:attr].should == "Attr value 2"
104
+ data[:bar][1][:baz][:text].should == "Text value 2"
105
+ end
106
+
107
+ it 'should map into a nested hash' do
108
+ xml = %q{
109
+ <?xml version="1.0" encoding="UTF-8"?>
110
+ <foo>
111
+ <bar attr="Attr value">Text value</bar>
112
+ <baz attr="Attr value">Text value</bar>
113
+ </foo>
114
+ }
115
+ @mapper.mappings = {
116
+ :bar => {
117
+ :attr => '/foo/bar/@attr',
118
+ :text => '/foo/bar'
119
+ },
120
+ :baz => {
121
+ :attr => '/foo/baz/@attr',
122
+ :text => '/foo/baz'
123
+ }
124
+ }
125
+ data = @mapper.map xml
126
+ data[:bar][:attr].should == 'Attr value'
127
+ data[:bar][:text].should == 'Text value'
128
+ data[:baz][:attr].should == 'Attr value'
129
+ data[:baz][:text].should == 'Text value'
130
+ end
131
+
132
+ end
133
+
134
+ it "should map namespaces correctly" do
135
+
136
+ xml = %q{
137
+ <root xmlns:h="http://www.w3.org/TR/html4/" xmlns:f="http://www.w3schools.com/furniture">
138
+ <h:table>
139
+ <h:tr>
140
+ <h:td>Apples</h:td>
141
+ <h:td>Bananas</h:td>
142
+ </h:tr>
143
+ </h:table>
144
+ <f:table>
145
+ <f:name>African Coffee Table</f:name>
146
+ <f:width>80</f:width>
147
+ <f:length>120</f:length>
148
+ </f:table>
149
+ </root>
150
+ }
151
+
152
+ @mapper.mappings = {
153
+ :html_table_rows => ["/root/h:table/h:tr/h:td", { :text => "." }],
154
+ :an_actual_table => {
155
+ :name => "/root/f:table/f:name",
156
+ :width => "/root/f:table/f:width",
157
+ :length => "/root/f:table/f:length"
158
+ }
159
+ }
160
+
161
+ @mapper.namespaces = {
162
+ "h" => "http://www.w3.org/TR/html4/",
163
+ "f" => "http://www.w3schools.com/furniture"
164
+ }
165
+
166
+ data = @mapper.map(xml)
167
+
168
+ data[:html_table_rows].size.should == 2
169
+ data[:html_table_rows][0][:text].should == "Apples"
170
+
171
+ data[:an_actual_table][:name].should == "African Coffee Table"
172
+ data[:an_actual_table][:width].should == "80"
173
+ end
174
+
175
+ end
@@ -0,0 +1,8 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+
3
+ require 'bundler'
4
+ Bundler.require
5
+ require 'xapper'
6
+
7
+ RSpec.configure do |config|
8
+ end
@@ -0,0 +1,58 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{xapper}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["andhapp", "matth"]
12
+ s.date = %q{2011-10-21}
13
+ s.description = %q{Give it an xml and define a mapping for it as a ruby hash and there you have it. A ruby hash for your XML}
14
+ s.email = %q{dutta.anuj@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".rspec",
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "README.md",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "examples/book_list.rb",
26
+ "lib/xapper.rb",
27
+ "spec/lib/xapper_spec.rb",
28
+ "spec/spec_helper.rb",
29
+ "xapper.gemspec"
30
+ ]
31
+ s.homepage = %q{http://github.com/bbcsnippets/xapper}
32
+ s.licenses = ["MIT"]
33
+ s.require_paths = ["lib"]
34
+ s.rubygems_version = %q{1.6.2}
35
+ s.summary = %q{A simple mapper that takes an XML and maps it into a ruby hash}
36
+
37
+ if s.respond_to? :specification_version then
38
+ s.specification_version = 3
39
+
40
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
41
+ s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
42
+ s.add_runtime_dependency(%q<rspec>, [">= 0"])
43
+ s.add_runtime_dependency(%q<rspec-mocks>, [">= 0"])
44
+ s.add_runtime_dependency(%q<jeweler>, [">= 0"])
45
+ else
46
+ s.add_dependency(%q<nokogiri>, [">= 0"])
47
+ s.add_dependency(%q<rspec>, [">= 0"])
48
+ s.add_dependency(%q<rspec-mocks>, [">= 0"])
49
+ s.add_dependency(%q<jeweler>, [">= 0"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<nokogiri>, [">= 0"])
53
+ s.add_dependency(%q<rspec>, [">= 0"])
54
+ s.add_dependency(%q<rspec-mocks>, [">= 0"])
55
+ s.add_dependency(%q<jeweler>, [">= 0"])
56
+ end
57
+ end
58
+
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xapper
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - andhapp
9
+ - matth
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2011-10-21 00:00:00 +01:00
15
+ default_executable:
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: nokogiri
19
+ prerelease: false
20
+ requirement: &id001 !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: "0"
26
+ type: :runtime
27
+ version_requirements: *id001
28
+ - !ruby/object:Gem::Dependency
29
+ name: rspec
30
+ prerelease: false
31
+ requirement: &id002 !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: "0"
37
+ type: :runtime
38
+ version_requirements: *id002
39
+ - !ruby/object:Gem::Dependency
40
+ name: rspec-mocks
41
+ prerelease: false
42
+ requirement: &id003 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ type: :runtime
49
+ version_requirements: *id003
50
+ - !ruby/object:Gem::Dependency
51
+ name: jeweler
52
+ prerelease: false
53
+ requirement: &id004 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ type: :runtime
60
+ version_requirements: *id004
61
+ description: Give it an xml and define a mapping for it as a ruby hash and there you have it. A ruby hash for your XML
62
+ email: dutta.anuj@gmail.com
63
+ executables: []
64
+
65
+ extensions: []
66
+
67
+ extra_rdoc_files:
68
+ - README.md
69
+ files:
70
+ - .rspec
71
+ - Gemfile
72
+ - Gemfile.lock
73
+ - README.md
74
+ - Rakefile
75
+ - VERSION
76
+ - examples/book_list.rb
77
+ - lib/xapper.rb
78
+ - spec/lib/xapper_spec.rb
79
+ - spec/spec_helper.rb
80
+ - xapper.gemspec
81
+ has_rdoc: true
82
+ homepage: http://github.com/bbcsnippets/xapper
83
+ licenses:
84
+ - MIT
85
+ post_install_message:
86
+ rdoc_options: []
87
+
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: "0"
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: "0"
102
+ requirements: []
103
+
104
+ rubyforge_project:
105
+ rubygems_version: 1.6.2
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: A simple mapper that takes an XML and maps it into a ruby hash
109
+ test_files: []
110
+