xapper 0.1.0

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