cobravsmongoose 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,2 @@
1
+ == 0.0.1 (2006-04-16)
2
+ * Initial release.
data/COPYING ADDED
@@ -0,0 +1,21 @@
1
+ == Licence (MIT)
2
+
3
+ Copyright (c) 2006 Paul Battley <pbattley@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README ADDED
@@ -0,0 +1,14 @@
1
+ == Cobra vs Mongoose
2
+
3
+ Cobra vs Mongoose translates XML to and from Ruby Hash objects, following the BadgerFish convention (http://badgerfish.ning.com/).
4
+
5
+ == Licence
6
+
7
+ This code is free to use under the terms of the MIT licence. If you'd like to
8
+ negotiate a different licence for a specific use, just contact me -- I'll
9
+ almost certainly permit it.
10
+
11
+ == Contact
12
+
13
+ Comments are welcome. Send an email to pbattley@gmail.com.
14
+
@@ -0,0 +1,153 @@
1
+ require 'rexml/document'
2
+ require 'cgi'
3
+
4
+ #
5
+ # CobraVsMongoose translates between XML documents and Ruby hashes according to the
6
+ # rules of the BadgerFish convention (see http://badgerfish.ning.com/).
7
+ #
8
+ class CobraVsMongoose
9
+
10
+ class ParseError < RuntimeError
11
+ end
12
+
13
+ class << self
14
+
15
+ #
16
+ # Returns a Hash corresponding to the data structure of the given XML string.
17
+ #
18
+ # E.g.
19
+ # xml = '<alice><bob>charlie</bob><bob>david</bob></alice>'
20
+ # CobraVsMongoose.xml_to_hash(xml)
21
+ # # => { "alice" => { "bob" => [{ "$" => "charlie" }, { "$" => "david" }] } }
22
+ #
23
+ def xml_to_hash(xml)
24
+ doc = REXML::Document.new(xml)
25
+ return xml_node_to_hash(doc.root_node)
26
+ end
27
+
28
+ #
29
+ # Returns an XML string corresponding to the data structure of the given Hash.
30
+ #
31
+ # E.g.
32
+ # hash = { "alice" => { "$" => "bob", "@charlie" => "david" } }
33
+ # CobraVsMongoose.hash_to_xml(hash)
34
+ # # => "<alice charlie='david'>bob</alice>"
35
+ #
36
+ # Note that, due to the fact that Ruby's hashes do not preserve ordering, the
37
+ # order of XML elements is undefined. For a predictable order, see the
38
+ # sort_keys class attribute.
39
+ #
40
+ def hash_to_xml(hash)
41
+ return nested_data_to_xml(hash.keys.first, hash.values.first)
42
+ end
43
+
44
+ #
45
+ # The sort_keys class attribute is useful for testing, when a predictable order
46
+ # is required in the generated XML. By setting CobraVsMongoose.sort_keys to true,
47
+ # hash-derived elements within a scope will be sorted by their element name, whilst
48
+ # attributes on an element will be sorted according to their name.
49
+ #
50
+ attr_accessor :sort_keys
51
+
52
+ private
53
+
54
+ def xml_node_to_hash(node, parent_namespaces={}) #:nodoc
55
+ this_node = {}
56
+ namespaces = parent_namespaces.dup
57
+ node.attributes.each do |name, value|
58
+ case name
59
+ when 'xmlns'
60
+ (namespaces['@xmlns'] ||= {})['$'] = value
61
+ when /^xmlns:(.*)/
62
+ (namespaces['@xmlns'] ||= {})[$1] = value
63
+ else
64
+ this_node["@#{name}"] = value
65
+ end
66
+ end
67
+ node.each_child do |child|
68
+ case child.node_type
69
+ when :element
70
+ key, value = child.expanded_name, xml_node_to_hash(child, namespaces)
71
+ when :text
72
+ key, value = '$', unescape(child.to_s).strip
73
+ next if value.empty?
74
+ end
75
+ current = this_node[key]
76
+ case current
77
+ when Array
78
+ this_node[key] << value
79
+ when nil
80
+ this_node[key] = value
81
+ else
82
+ this_node[key] = [current.dup, value]
83
+ end
84
+ end
85
+ return this_node.merge(namespaces)
86
+ end
87
+
88
+ def nested_data_to_xml(name, item, known_namespaces=[]) #:nodoc:
89
+ case item
90
+ when Hash
91
+ attributes = {}
92
+ children = {}
93
+ namespaces = known_namespaces.dup
94
+ opt_order(item).each do |key, value|
95
+ value = item[key]
96
+ case key
97
+ when '@xmlns'
98
+ value.each do |ns_name, ns_value|
99
+ full_ns_name = 'xmlns' << ((ns_name == '$') ? '' : ':' << ns_name)
100
+ unless known_namespaces.include?(full_ns_name)
101
+ namespaces << full_ns_name
102
+ attributes[full_ns_name] = ns_value
103
+ end
104
+ end
105
+ when /^@(.*)/
106
+ attributes[$1] = value
107
+ else
108
+ children[key] = value
109
+ end
110
+ end
111
+ return make_tag(name, attributes) do
112
+ opt_order(children).map { |tag, value|
113
+ case tag
114
+ when '$'
115
+ escape(value)
116
+ else
117
+ nested_data_to_xml(tag, value, namespaces)
118
+ end
119
+ }.join
120
+ end
121
+ when Array
122
+ return item.map{ |subitem| nested_data_to_xml(name, subitem, known_namespaces) }.join
123
+ else
124
+ raise ParseError, "unparseable type: #{item.class}"
125
+ end
126
+ end
127
+
128
+ def make_tag(name, attributes={}) #:nodoc:
129
+ attr_string = ' ' << opt_order(attributes).map{ |k, v| "#{k}='#{escape(v)}'" }.join(' ')
130
+ attr_string = '' if attr_string == ' '
131
+ body = yield
132
+ if body && !body.empty?
133
+ return "<#{name}#{attr_string}>" << body << "</#{name}>"
134
+ else
135
+ return "<#{name}#{attr_string} />"
136
+ end
137
+ return result
138
+ end
139
+
140
+ def escape(str) #:nodoc:
141
+ return CGI.escapeHTML(str)
142
+ end
143
+
144
+ def unescape(str) #:nodoc:
145
+ return CGI.unescapeHTML(str)
146
+ end
147
+
148
+ def opt_order(hash)
149
+ return sort_keys ? hash.sort_by{ |kv| kv.first } : hash
150
+ end
151
+
152
+ end
153
+ end
data/test/all.rb ADDED
@@ -0,0 +1,3 @@
1
+ Dir[File.dirname(__FILE__)+'/*_test.rb'].each do |test|
2
+ require test
3
+ end
@@ -0,0 +1,38 @@
1
+ $: << File.dirname(__FILE__) + '/../lib/'
2
+ require 'cobravsmongoose'
3
+ require 'test/unit'
4
+ begin
5
+ require 'test/unit/xml'
6
+ rescue
7
+ puts 'You need to install Test::Unit::XML (http://testunitxml.rubyforge.org/) for this test.'
8
+ end
9
+ require File.dirname(__FILE__) + '/test_data'
10
+
11
+ class HashToXMLTest < Test::Unit::TestCase
12
+
13
+ COBRA_VS_MONGOOSE_TEST_DATA.each do |name, data|
14
+ method = ("test_data_" << name.to_s << "_hash_to_xml").to_sym
15
+ define_method(method) do
16
+ assert_hash_to_xml(name)
17
+ end
18
+ end
19
+
20
+ def test_data_loaded
21
+ assert !COBRA_VS_MONGOOSE_TEST_DATA.empty?
22
+ assert methods.grep(/^test_/).length > COBRA_VS_MONGOOSE_TEST_DATA.keys.length
23
+ end
24
+
25
+ def assert_hash_to_xml(name)
26
+ CobraVsMongoose.sort_keys = true
27
+ source, expected = COBRA_VS_MONGOOSE_TEST_DATA[name]
28
+ assert_not_nil(expected)
29
+ assert_not_nil(source)
30
+ actual = CobraVsMongoose.hash_to_xml(source)
31
+ if (expected == actual)
32
+ assert true
33
+ else
34
+ assert_xml_equal(expected, actual)
35
+ end
36
+ end
37
+
38
+ end
data/test/test_data.rb ADDED
@@ -0,0 +1,100 @@
1
+ COBRA_VS_MONGOOSE_TEST_DATA = {
2
+ :text_content => [
3
+ { 'alice' => { '$' => 'bob' } },
4
+ '<alice>bob</alice>'
5
+ ],
6
+ :nested_elements => [
7
+ {
8
+ "alice" => {
9
+ "bob" => { "$" => "charlie" },
10
+ "david" => { "$" => "edgar"}
11
+ }
12
+ },
13
+ '<alice><bob>charlie</bob><david>edgar</david></alice>'
14
+ ],
15
+ :multiple_elements => [
16
+ {
17
+ "alice" => {
18
+ "bob" => [
19
+ { "$" => "charlie" },
20
+ { "$" => "david" }
21
+ ]
22
+ }
23
+ },
24
+ '<alice><bob>charlie</bob><bob>david</bob></alice>'
25
+ ],
26
+ :attributes => [
27
+ {
28
+ "alice" => {
29
+ "$" => "bob",
30
+ "@charlie" => "david"
31
+ }
32
+ },
33
+ '<alice charlie="david">bob</alice>'
34
+ ],
35
+ :namespace_uri => [
36
+ {
37
+ "alice" => {
38
+ "$" => "bob",
39
+ "@xmlns" => {
40
+ "$" => "http://some-namespace"
41
+ }
42
+ }
43
+ },
44
+ '<alice xmlns="http://some-namespace">bob</alice>'
45
+ ],
46
+ :other_namespaces => [
47
+ {
48
+ "alice" => {
49
+ "$" => "bob",
50
+ "@xmlns" => {
51
+ "$" => "http://some-namespace",
52
+ "charlie" => "http://some-other-namespace"
53
+ }
54
+ }
55
+ },
56
+ '<alice xmlns="http://some-namespace" xmlns:charlie="http://some-other-namespace">bob</alice>'
57
+ ],
58
+ :namespace_prefixes => [
59
+ {
60
+ "alice" => {
61
+ "bob" => {
62
+ "$" => "david" ,
63
+ "@xmlns" => {
64
+ "charlie" => "http://some-other-namespace" ,
65
+ "$" => "http://some-namespace"
66
+ }
67
+ },
68
+ "charlie:edgar" => {
69
+ "$" => "frank",
70
+ "@xmlns" => {
71
+ "charlie" => "http://some-other-namespace",
72
+ "$" => "http://some-namespace"
73
+ }
74
+ },
75
+ "@xmlns" => {
76
+ "charlie" => "http://some-other-namespace",
77
+ "$" => "http:\/\/some-namespace"
78
+ }
79
+ }
80
+ },
81
+ '<alice xmlns="http://some-namespace" xmlns:charlie="http://some-other-namespace">
82
+ <bob>david</bob> <charlie:edgar>frank</charlie:edgar> </alice>'
83
+ ],
84
+ :attribute_escaping => [
85
+ {"a" => {"@attribute" => "foo & bar", "$" => "string"}},
86
+ "<a attribute='foo &amp; bar'>string</a>"
87
+ ],
88
+ :string_escaping => [
89
+ {"a" => {"$" => "foo & bar"}},
90
+ "<a>foo &amp; bar</a>"
91
+ ],
92
+ :empty_element => [
93
+ {"a" => {}},
94
+ "<a />"
95
+ ],
96
+ :empty_element_with_attributes => [
97
+ {"a" => {"@foo" => "bar"}},
98
+ "<a foo='bar' />"
99
+ ]
100
+ }
@@ -0,0 +1,27 @@
1
+ $: << File.dirname(__FILE__) + '/../lib/'
2
+ require 'cobravsmongoose'
3
+ require 'test/unit'
4
+ require File.dirname(__FILE__) + '/test_data'
5
+
6
+ class XMLToHashTest < Test::Unit::TestCase
7
+
8
+ COBRA_VS_MONGOOSE_TEST_DATA.each do |name, data|
9
+ method = ("test_data_" << name.to_s << "_xml_to_hash").to_sym
10
+ define_method(method) do
11
+ assert_xml_to_hash(name)
12
+ end
13
+ end
14
+
15
+ def test_data_loaded
16
+ assert !COBRA_VS_MONGOOSE_TEST_DATA.empty?
17
+ assert methods.grep(/^test_/).length > COBRA_VS_MONGOOSE_TEST_DATA.keys.length
18
+ end
19
+
20
+ def assert_xml_to_hash(name)
21
+ expected, source = COBRA_VS_MONGOOSE_TEST_DATA[name]
22
+ assert_not_nil(expected)
23
+ assert_not_nil(source)
24
+ assert_equal(expected, CobraVsMongoose.xml_to_hash(source))
25
+ end
26
+
27
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: cobravsmongoose
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.1
7
+ date: 2006-04-16 00:00:00 +01:00
8
+ summary: Translates XML to and from Ruby Hash objects, following the BadgerFish convention.
9
+ require_paths:
10
+ - lib
11
+ email: pbattley@gmail.com
12
+ homepage:
13
+ rubyforge_project:
14
+ description:
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - Paul Battley
30
+ files:
31
+ - lib/cobravsmongoose.rb
32
+ - test/all.rb
33
+ - test/hash_to_xml_test.rb
34
+ - test/test_data.rb
35
+ - test/xml_to_hash_test.rb
36
+ - README
37
+ - CHANGES
38
+ - COPYING
39
+ test_files:
40
+ - test/all.rb
41
+ rdoc_options: []
42
+
43
+ extra_rdoc_files:
44
+ - README
45
+ - CHANGES
46
+ - COPYING
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ requirements: []
52
+
53
+ dependencies: []
54
+