xml-mapping 0.8.1 → 0.9.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.
Files changed (71) hide show
  1. data/ChangeLog +64 -3
  2. data/README +871 -173
  3. data/README_XPATH +40 -13
  4. data/Rakefile +37 -26
  5. data/TODO.txt +39 -8
  6. data/examples/README +5 -0
  7. data/examples/company_usage.intout +34 -22
  8. data/examples/documents_folders.rb +31 -0
  9. data/examples/documents_folders.xml +16 -0
  10. data/examples/documents_folders_usage.intin.rb +18 -0
  11. data/examples/documents_folders_usage.intout +46 -0
  12. data/examples/order_signature_enhanced_usage.intout +21 -11
  13. data/examples/order_usage.intin.rb +52 -5
  14. data/examples/order_usage.intout +154 -80
  15. data/examples/person.intin.rb +44 -0
  16. data/examples/person.intout +27 -0
  17. data/examples/person_mm.intin.rb +119 -0
  18. data/examples/person_mm.intout +114 -0
  19. data/examples/publication.intin.rb +44 -0
  20. data/examples/publication.intout +20 -0
  21. data/examples/reader.intin.rb +33 -0
  22. data/examples/reader.intout +19 -0
  23. data/examples/stringarray.rb +5 -0
  24. data/examples/stringarray.xml +10 -0
  25. data/examples/stringarray_usage.intin.rb +11 -0
  26. data/examples/stringarray_usage.intout +31 -0
  27. data/examples/time_augm.intout +19 -7
  28. data/examples/time_augm_loading.intin.rb +44 -0
  29. data/examples/time_augm_loading.intout +12 -0
  30. data/examples/time_node.intin.rb +79 -0
  31. data/examples/time_node.rb +3 -2
  32. data/examples/time_node_w_marshallers.intin.rb +48 -0
  33. data/examples/time_node_w_marshallers.intout +25 -0
  34. data/examples/time_node_w_marshallers.xml +9 -0
  35. data/examples/xpath_create_new.intout +132 -114
  36. data/examples/xpath_ensure_created.intout +86 -65
  37. data/examples/xpath_pathological.intout +16 -16
  38. data/examples/xpath_usage.intout +1 -1
  39. data/install.rb +1 -0
  40. data/lib/xml/mapping.rb +3 -1
  41. data/lib/xml/mapping/base.rb +442 -272
  42. data/lib/xml/mapping/core_classes_mapping.rb +32 -0
  43. data/lib/xml/mapping/standard_nodes.rb +176 -86
  44. data/lib/xml/mapping/version.rb +2 -2
  45. data/lib/xml/rexml_ext.rb +186 -0
  46. data/lib/xml/xxpath.rb +28 -265
  47. data/lib/xml/xxpath/steps.rb +345 -0
  48. data/lib/xml/xxpath_methods.rb +96 -0
  49. data/test/all_tests.rb +4 -1
  50. data/test/benchmark_fixtures.rb +14 -0
  51. data/test/{multiple_mappings.rb → bookmarks.rb} +0 -0
  52. data/test/company.rb +47 -0
  53. data/test/documents_folders.rb +11 -1
  54. data/test/examples_test.rb +29 -0
  55. data/test/fixtures/benchmark.xml +77 -0
  56. data/test/fixtures/company1.xml +9 -0
  57. data/test/fixtures/documents_folders.xml +0 -8
  58. data/test/fixtures/documents_folders2.xml +13 -19
  59. data/test/fixtures/triangle_m1.xml +17 -0
  60. data/test/fixtures/triangle_m2.xml +19 -0
  61. data/test/inheritance_test.rb +50 -0
  62. data/test/multiple_mappings_test.rb +155 -0
  63. data/test/rexml_xpath_benchmark.rb +29 -0
  64. data/test/triangle_mm.rb +57 -0
  65. data/test/xml_mapping_adv_test.rb +36 -1
  66. data/test/xml_mapping_test.rb +136 -7
  67. data/test/xpath_test.rb +154 -0
  68. data/test/xxpath_benchmark.rb +36 -0
  69. data/test/xxpath_benchmark.result1.txt +17 -0
  70. data/test/xxpath_methods_test.rb +61 -0
  71. metadata +139 -90
@@ -8,12 +8,18 @@ uses xml-xxpath extensively for implementing its node types -- see the
8
8
  README file and the reference documentation (and the source code) for
9
9
  details. xml-xxpath, however, does not depend on xml-mapping at all,
10
10
  and is useful in its own right -- maybe I'll later distribute it as a
11
- seperate library instead of bundling it. xml-xxpath's XPath support is
12
- vastly incomplete (see below), but, in addition to the normal
13
- reading/matching functionality found in other XPath implementations
14
- (i.e. "find all elements in a given XML document matching a given
15
- XPath expression"), xml-xxpath supports <i>write access</i>. For
16
- example, when writing the XPath expression
11
+ seperate library instead of bundling it. For the time being, if you
12
+ want to use this XPath implementation stand-alone, you can just rip
13
+ the files <tt>lib/xml/xxpath.rb</tt>,
14
+ <tt>lib/xml/xxpath/steps.rb</tt>, and
15
+ <tt>lib/xml/xxpath_methods.rb</tt> out of the xml-mapping distribution
16
+ and use them on their own (they do not depend on anything else).
17
+
18
+ xml-xxpath's XPath support is vastly incomplete (see below), but, in
19
+ addition to the normal reading/matching functionality found in other
20
+ XPath implementations (i.e. "find all elements in a given XML document
21
+ matching a given XPath expression"), xml-xxpath supports <i>write
22
+ access</i>. For example, when writing the XPath expression
17
23
  "/foo/bar[3]/baz[@key='hiho']" to the XML document
18
24
 
19
25
  <foo>
@@ -40,14 +46,13 @@ of the existing XPath implementations, e.g. the one that comes with
40
46
  REXML. Also, the whole xml-xxpath implementation is just 300 lines of
41
47
  Ruby code, it is quite fast (paths are precompiled), and xml-xxpath
42
48
  returns matched elements in the order they appeared in the source
43
- document -- I've heard REXML::XXPath doesn't do that :)
49
+ document -- I've heard REXML::XPath doesn't do that :)
44
50
 
45
- Some basic knowledge of XPath is helpful for reading this document (I
46
- don't know very much either).
51
+ Some basic knowledge of XPath is helpful for reading this document.
47
52
 
48
53
  At the moment, xml-xxpath understands XPath expressions of the form
49
- [<tt>/</tt>]_pathelement_<tt>/</tt>_pathelement_<tt>/</tt>..., where
50
- each _pathelement_ must be one of these:
54
+ [<tt>/</tt>]_pathelement_<tt>/[/]</tt>_pathelement_<tt>/[/]</tt>...,
55
+ where each _pathelement_ must be one of these:
51
56
 
52
57
  - a simple element name _name_, e.g. +signature+
53
58
 
@@ -60,6 +65,23 @@ each _pathelement_ must be one of these:
60
65
 
61
66
  - the "match-all" path element, <tt>*</tt>
62
67
 
68
+ - .
69
+
70
+ - name1|name2|...
71
+
72
+ - .[@key='xy'] / self::*[@key='xy']
73
+
74
+ - child::*[@key='xy']
75
+
76
+ - text()
77
+
78
+
79
+
80
+ Xml-xxpath only supports relative paths at this time, i.e. XPath
81
+ expressions beginning with "/" or "//" will still only find nodes
82
+ below the node the expression is applied to (as if you had written
83
+ "./" or ".//", respectively).
84
+
63
85
 
64
86
  == Usage
65
87
 
@@ -115,7 +137,7 @@ against the +firstelt+ element in the example *must not* start with
115
137
 
116
138
  === Write Access
117
139
 
118
- You may pass a <tt>:ensure_created=>true</tt> option argument to
140
+ You may pass an <tt>:ensure_created=>true</tt> option argument to
119
141
  _path_.first(_elt_)/_path_.all(_elt_) calls to make sure that _path_
120
142
  exists inside the passed XML element _elt_. If it existed before,
121
143
  nothing changes, and the call behaves just as it would without the
@@ -141,13 +163,18 @@ Examples:
141
163
  Alternatively, you may pass a <tt>:create_new=>true</tt> option
142
164
  argument or call <tt>create_new</tt> (_path_.create_new(_elt_) is
143
165
  equivalent to _path_.first(_elt_,:create_new=>true)). In that case, a
144
- new node in created in _elt_ for each path element of _path_ (or an
166
+ new node is created in _elt_ for each path element of _path_ (or an
145
167
  exception raised if that wasn't possible for any path element).
146
168
 
147
169
  Examples:
148
170
 
149
171
  :include: xpath_create_new.intout
150
172
 
173
+ This feature is used in xml-mapping by node types like
174
+ XML::Mapping::ArrayNode, which must create a new instance of the
175
+ "per-array element path" for each element of the array to be stored in
176
+ an XML tree.
177
+
151
178
 
152
179
  === Pathological Cases
153
180
 
data/Rakefile CHANGED
@@ -1,10 +1,12 @@
1
1
  # -*- ruby -*-
2
2
  # adapted from active_record's Rakefile
3
3
 
4
+ require 'build_lib/rdoc_ext'
5
+
4
6
  require 'rubygems'
5
7
  require 'rake'
6
8
  require 'rake/testtask'
7
- require 'rake/rdoctask'
9
+ require 'build_lib/my_rdoctask'
8
10
  require 'rake/packagetask'
9
11
  require 'rake/gempackagetask'
10
12
  #require 'rake/contrib/rubyforgepublisher'
@@ -12,13 +14,8 @@ require 'rake/contrib/sshpublisher'
12
14
 
13
15
  require File.dirname(__FILE__)+"/lib/xml/mapping/version"
14
16
 
15
-
16
17
  # yeah -- it's just stupid that these are private
17
18
 
18
- class Rake::RDocTask
19
- public :rdoc_target
20
- end
21
-
22
19
  class Rake::PackageTask
23
20
  public :tgz_file, :zip_file
24
21
  end
@@ -28,18 +25,33 @@ class Rake::GemPackageTask
28
25
  end
29
26
 
30
27
 
31
- FILES_RDOC_EXTRA=%w{README README_XPATH TODO.txt doc/xpath_impl_notes.txt}
28
+ FILES_RDOC_EXTRA=%w{README README_XPATH ChangeLog TODO.txt doc/xpath_impl_notes.txt}
32
29
  FILES_RDOC_INCLUDES=%w{examples/company.xml
33
30
  examples/company.rb
34
31
  examples/company_usage.intout
32
+ examples/order.xml
33
+ examples/order.rb
35
34
  examples/order_usage.intout
35
+ examples/stringarray_usage.intout
36
+ examples/stringarray.xml
37
+ examples/documents_folders_usage.intout
38
+ examples/documents_folders.xml
39
+ examples/time_node_w_marshallers.intout
40
+ examples/time_node_w_marshallers.xml
36
41
  examples/time_augm.intout
42
+ examples/time_augm_loading.intout
37
43
  examples/xpath_usage.intout
38
44
  examples/xpath_ensure_created.intout
39
45
  examples/xpath_create_new.intout
40
46
  examples/xpath_pathological.intout
41
47
  examples/xpath_docvsroot.intout
42
- examples/order_signature_enhanced_usage.intout}
48
+ examples/order_signature_enhanced_usage.intout
49
+ examples/order_signature_enhanced.xml
50
+ examples/person.intout
51
+ examples/publication.intout
52
+ examples/reader.intout
53
+ examples/person_mm.intout
54
+ }
43
55
 
44
56
 
45
57
  desc "Default Task"
@@ -48,6 +60,7 @@ task :default => [ :test ]
48
60
  Rake::TestTask.new(:test) { |t|
49
61
  t.test_files = ["test/all_tests.rb"]
50
62
  t.verbose = true
63
+ # t.loader = :testrb
51
64
  }
52
65
 
53
66
  # runs tests only if sources have changed since last succesful run of
@@ -59,10 +72,10 @@ end
59
72
 
60
73
 
61
74
 
62
- Rake::RDocTask.new { |rdoc|
75
+ MyRDocTask.new { |rdoc|
63
76
  rdoc.rdoc_dir = 'doc/api'
64
77
  rdoc.title = "XML::Mapping -- Simple, extensible Ruby-to-XML (and back) mapper"
65
- rdoc.options << '--line-numbers --inline-source --accessor cattr_accessor=object --include examples'
78
+ rdoc.options += %w{--line-numbers --inline-source --accessor cattr_accessor=object --include examples}
66
79
  rdoc.rdoc_files.include(*FILES_RDOC_EXTRA)
67
80
  rdoc.rdoc_files.include('lib/**/*.rb')
68
81
 
@@ -139,20 +152,17 @@ end
139
152
 
140
153
  # have to add additional prerequisites manually because it appears
141
154
  # that rules can only define a single prerequisite :-\
142
- for f in %w{examples/company_usage
143
- examples/order_usage
144
- examples/order_signature_enhanced_usage
145
- examples/time_augm
146
- examples/xpath_usage
147
- examples/xpath_ensure_created
148
- examples/xpath_create_new
149
- examples/xpath_pathological
150
- examples/xpath_docvsroot} do
151
- file "#{f}.intout" => ["#{f}.intin.rb", 'examples/company.xml']
152
- file "#{f}.intout" => FileList.new("lib/**/*.rb")
153
- file "#{f}.intout" => FileList.new("examples/**/*.rb")
155
+ FILES_RDOC_INCLUDES.select{|f|f=~/.intout$/}.each do |f|
156
+ file f => FileList.new("lib/**/*.rb")
157
+ file f => FileList.new("examples/**/*.rb")
154
158
  end
155
159
 
160
+ file 'examples/company_usage.intout' => ['examples/company.xml']
161
+ file 'examples/documents_folders_usage.intout' => ['examples/documents_folders.xml']
162
+ file 'examples/order_signature_enhanced_usage.intout' => ['examples/order_signature_enhanced.xml']
163
+ file 'examples/order_usage.intout' => ['examples/order.xml']
164
+ file 'examples/stringarray_usage.intout' => ['examples/stringarray.xml']
165
+
156
166
 
157
167
  spec = Gem::Specification.new do |s|
158
168
  s.name = 'xml-mapping'
@@ -168,12 +178,12 @@ spec = Gem::Specification.new do |s|
168
178
  s.files += Dir.glob("{lib,examples,test}/**/*").delete_if do |item|
169
179
  item.include?("CVS") || item =~ /~$/
170
180
  end
171
- s.files += %w{LICENSE Rakefile ChangeLog install.rb}
181
+ s.files += %w{LICENSE Rakefile install.rb}
172
182
  s.extra_rdoc_files = FILES_RDOC_EXTRA
173
183
  s.rdoc_options += %w{--include examples}
174
184
 
175
185
  s.require_path = 'lib'
176
- s.autorequire = 'xml/mapping'
186
+ # s.autorequire = 'xml/mapping'
177
187
 
178
188
  # s.add_dependency 'rexml'
179
189
 
@@ -182,8 +192,9 @@ spec = Gem::Specification.new do |s|
182
192
  s.test_file = 'test/all_tests.rb'
183
193
 
184
194
  s.author = 'Olaf Klischat'
185
- s.email = 'klischat@cs.tu-berlin.de'
195
+ s.email = 'olaf.klischat@sofd.de'
186
196
  s.homepage = "http://xml-mapping.rubyforge.org"
197
+ s.rubyforge_project = "xml-mapping"
187
198
  end
188
199
 
189
200
 
@@ -207,7 +218,7 @@ end
207
218
 
208
219
 
209
220
  task :rfpub_rdoc => [:rdoc] do
210
- p=Rake::SshDirPublisher.new('xml-mapping.rubyforge.org',
221
+ p=Rake::SshDirPublisher.new('rubyforge.org',
211
222
  '/var/www/gforge-projects/xml-mapping/',
212
223
  'doc/api')
213
224
  p.upload
data/TODO.txt CHANGED
@@ -1,18 +1,49 @@
1
- - XML::XXPath: generalize foo[@x='bar'] to foo[<any XPath
2
- expression>='bar'] (unless create/create_new implementation proves
3
- to be too difficult, but I don't think it will...)
1
+ - XML::XXPath: Write a real XPath parser eventually
2
+
3
+ - XML::XXPath: avoid duplicates in path.all(node) result arrays when
4
+ using the descendants ("//") axis
5
+
6
+ - invent an XPath-like language for Ruby object graphs (i.e. a
7
+ language that is to Ruby object graphs what XPath is to XML
8
+ trees). Use expressions in that language as a generalization of
9
+ "attribute names" (e.g. the 1st parameter to single attribute node
10
+ factory methods). The language could more or less be Ruby itself,
11
+ but the write support would need some extra work...
12
+
13
+ - XML::XXPath:
14
+
15
+ - implement .[@attrname] steps
16
+
17
+ - returns the context node iff it contains an attrname attribute
18
+
19
+ - doesn't work properly in REXML::XPath?
20
+
21
+ - implement *[@attrname] steps
22
+
23
+ - implement *[@attrname='attrvalue'] steps
24
+
25
+ - id/idref support (write support possible?)
26
+
27
+ - XML::Mapping: make SubObjectBaseNode a mixin instead of a subclass
28
+ of SingleAttributeNode ("mapping sub-objects" and "mapping to a
29
+ single attribute" are orthogonal concepts; inheritance is bad design
30
+ here)
4
31
 
5
32
  - documentation:
6
33
 
7
34
  - README:
8
35
 
9
- - multi-attribute nodes
36
+ - document/show usage of default_when_xpath_err outside node type
37
+ implementations
38
+
39
+ - README_XPATH:
40
+
41
+ - mention new step types, new axes, xml/xpath_methods
10
42
 
11
- - XML::Mapping: Move @options hash functionality from
12
- SingleAttributeNode to Node.
13
43
 
14
- - XML::Mapping/default attribute values: Update documentation, digest
15
- "nil" issues...
44
+ - XML::XXPath/XML::Mapping: support for XML namespaces in XML::XXPath
45
+ (match nodes with specific namespaces only) and XML::Mapping
46
+ (use_namespace etc.)
16
47
 
17
48
  - add streaming input/output to XML::Mapping, i.e. SAX-based input in
18
49
  addition to the current REXML/DOM - based one. Probably won't be
@@ -0,0 +1,5 @@
1
+ This directory contains the example code snippets from the main README
2
+ file (each time the documentation is regenerated, they're included in
3
+ the README, executed, and their output is checked for correctness and
4
+ included in the README as well). So, if you've read the README, you've
5
+ already seen all the examples from this directory.
@@ -1,11 +1,11 @@
1
1
  c = Company.load_from_file('company.xml')
2
- => #<Company:0x312dc8 @customers=[#<Customer:0x310bd8 @name="James Kirk", @id="jim">, #<Customer:0x310200 @name="Ernie", @id="ernie">, #<Customer:0x30f7f8 @name="Bert", @id="bert">], @address=#<Address:0x3123d8 @zip=10113, @city="Berlin">, @name="ACME inc.">
2
+ => #<Company:0x7f487635b068 @address=#<Address:0x7f487635a1e0 @zip=10113, @city="Berlin">, @name="ACME inc.", @customers=[#<Customer:0x7f48763584d0 @name="James Kirk", @id="jim">, #<Customer:0x7f4876356d88 @name="Ernie", @id="ernie">, #<Customer:0x7f4876355640 @name="Bert", @id="bert">]>
3
3
  c.name
4
4
  => "ACME inc."
5
5
  c.customers.size
6
6
  => 3
7
7
  c.customers[1]
8
- => #<Customer:0x310200 @name="Ernie", @id="ernie">
8
+ => #<Customer:0x7f4876356d88 @name="Ernie", @id="ernie">
9
9
  c.customers[1].name
10
10
  => "Ernie"
11
11
  c.customers[0].name
@@ -13,27 +13,39 @@ c.customers[0].name
13
13
  c.customers[0].name = 'James Tiberius Kirk'
14
14
  => "James Tiberius Kirk"
15
15
  c.customers << Customer.new('cm','Cookie Monster')
16
- => [#<Customer:0x310bd8 @name="James Tiberius Kirk", @id="jim">, #<Customer:0x310200 @name="Ernie", @id="ernie">, #<Customer:0x30f7f8 @name="Bert", @id="bert">, #<Customer:0x30e118 @name="Cookie Monster", @id="cm">]
16
+ => [#<Customer:0x7f48763584d0 @name="James Tiberius Kirk", @id="jim">, #<Customer:0x7f4876356d88 @name="Ernie", @id="ernie">, #<Customer:0x7f4876355640 @name="Bert", @id="bert">, #<Customer:0x7f4876352b48 @name="Cookie Monster", @id="cm">]
17
17
  xml2 = c.save_to_xml
18
18
  => <company name='ACME inc.'> ... </>
19
19
  xml2.write($stdout,2)
20
20
  <company name='ACME inc.'>
21
- <address>
22
- <city>Berlin</city>
23
- <zip>10113</zip>
24
- </address>
25
- <customers>
26
- <customer id='1607148'>
27
- <name>James Tiberius Kirk</name>
28
- </customer>
29
- <customer id='1605888'>
30
- <name>Ernie</name>
31
- </customer>
32
- <customer id='1604604'>
33
- <name>Bert</name>
34
- </customer>
35
- <customer id='1601676'>
36
- <name>Cookie Monster</name>
37
- </customer>
38
- </customers>
39
- </company>
21
+ <address>
22
+ <city>
23
+ Berlin
24
+ </city>
25
+ <zip>
26
+ 10113
27
+ </zip>
28
+ </address>
29
+ <customers>
30
+ <customer id='69974598795880'>
31
+ <name>
32
+ James Tiberius Kirk
33
+ </name>
34
+ </customer>
35
+ <customer id='69974598792900'>
36
+ <name>
37
+ Ernie
38
+ </name>
39
+ </customer>
40
+ <customer id='69974598789920'>
41
+ <name>
42
+ Bert
43
+ </name>
44
+ </customer>
45
+ <customer id='69974598784420'>
46
+ <name>
47
+ Cookie Monster
48
+ </name>
49
+ </customer>
50
+ </customers>
51
+ </company>
@@ -0,0 +1,31 @@
1
+ require 'xml/mapping'
2
+
3
+ class Entry
4
+ include XML::Mapping
5
+
6
+ text_node :name, "@name"
7
+ end
8
+
9
+
10
+ class Document <Entry
11
+ include XML::Mapping
12
+
13
+ text_node :contents, "contents"
14
+ end
15
+
16
+
17
+ class Folder <Entry
18
+ include XML::Mapping
19
+
20
+ array_node :entries, "document|folder", :default_value=>[]
21
+
22
+ def [](name)
23
+ entries.select{|e|e.name==name}[0]
24
+ end
25
+
26
+ def append(name,entry)
27
+ entries << entry
28
+ entry.name = name
29
+ entry
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="ISO-8859-1"?>
2
+
3
+ <folder name="home">
4
+ <document name="plan">
5
+ <contents> inhale, exhale</contents>
6
+ </document>
7
+
8
+ <folder name="work">
9
+ <folder name="xml-mapping">
10
+ <document name="README">
11
+ <contents>foo bar baz</contents>
12
+ </document>
13
+ </folder>
14
+ </folder>
15
+
16
+ </folder>
@@ -0,0 +1,18 @@
1
+ #:invisible:
2
+ $:.unshift "../lib"
3
+ require 'documents_folders' #<=
4
+ #:visible:
5
+
6
+ root = XML::Mapping.load_object_from_file "documents_folders.xml" #<=
7
+ root.name #<=
8
+ root.entries #<=
9
+
10
+ root.append "etc", Folder.new
11
+ root["etc"].append "passwd", Document.new
12
+ root["etc"]["passwd"].contents = "foo:x:2:2:/bin/sh"
13
+ root["etc"].append "hosts", Document.new
14
+ root["etc"]["hosts"].contents = "127.0.0.1 localhost"
15
+
16
+ xml = root.save_to_xml #<=
17
+ #:invisible_retval:
18
+ xml.write $stdout,2