xml-mapping 0.8.1 → 0.9.1

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