pdf-labels 1.0.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.
Files changed (142) hide show
  1. data/History.txt +8 -0
  2. data/LICENCE +38 -0
  3. data/Manifest.txt +141 -0
  4. data/README.txt +72 -0
  5. data/Rakefile +30 -0
  6. data/lib/alias.rb +8 -0
  7. data/lib/glabel_template.rb +36 -0
  8. data/lib/label.rb +52 -0
  9. data/lib/layout.rb +13 -0
  10. data/lib/length_node.rb +47 -0
  11. data/lib/markup.rb +25 -0
  12. data/lib/pdf_label_page.rb +171 -0
  13. data/lib/pdf_labels.rb +6 -0
  14. data/lib/template.rb +37 -0
  15. data/templates/avery-iso-templates.xml +222 -0
  16. data/templates/avery-other-templates.xml +21 -0
  17. data/templates/avery-us-templates.xml +599 -0
  18. data/templates/glabels-2.0.dtd +329 -0
  19. data/templates/misc-iso-templates.xml +434 -0
  20. data/templates/misc-other-templates.xml +21 -0
  21. data/templates/misc-us-templates.xml +183 -0
  22. data/templates/paper-sizes.xml +37 -0
  23. data/templates/zweckform-iso-templates.xml +197 -0
  24. data/test/test_pdf_label_page.rb +91 -0
  25. data/vendor/color.rb +87 -0
  26. data/vendor/color/cmyk.rb +182 -0
  27. data/vendor/color/css.rb +27 -0
  28. data/vendor/color/grayscale.rb +135 -0
  29. data/vendor/color/hsl.rb +130 -0
  30. data/vendor/color/palette.rb +15 -0
  31. data/vendor/color/palette/gimp.rb +107 -0
  32. data/vendor/color/palette/monocontrast.rb +180 -0
  33. data/vendor/color/rgb-colors.rb +189 -0
  34. data/vendor/color/rgb.rb +311 -0
  35. data/vendor/color/rgb/metallic.rb +28 -0
  36. data/vendor/color/yiq.rb +78 -0
  37. data/vendor/pdf/charts.rb +13 -0
  38. data/vendor/pdf/charts/stddev.rb +433 -0
  39. data/vendor/pdf/grid.rb +135 -0
  40. data/vendor/pdf/math.rb +108 -0
  41. data/vendor/pdf/pagenumbers.rb +288 -0
  42. data/vendor/pdf/quickref.rb +331 -0
  43. data/vendor/pdf/simpletable.rb +947 -0
  44. data/vendor/pdf/techbook.rb +901 -0
  45. data/vendor/pdf/writer.rb +2801 -0
  46. data/vendor/pdf/writer/arc4.rb +63 -0
  47. data/vendor/pdf/writer/fontmetrics.rb +202 -0
  48. data/vendor/pdf/writer/fonts/Courier-Bold.afm +342 -0
  49. data/vendor/pdf/writer/fonts/Courier-BoldOblique.afm +342 -0
  50. data/vendor/pdf/writer/fonts/Courier-Oblique.afm +342 -0
  51. data/vendor/pdf/writer/fonts/Courier.afm +342 -0
  52. data/vendor/pdf/writer/fonts/Helvetica-Bold.afm +2827 -0
  53. data/vendor/pdf/writer/fonts/Helvetica-BoldOblique.afm +2827 -0
  54. data/vendor/pdf/writer/fonts/Helvetica-Oblique.afm +3051 -0
  55. data/vendor/pdf/writer/fonts/Helvetica.afm +3051 -0
  56. data/vendor/pdf/writer/fonts/Symbol.afm +213 -0
  57. data/vendor/pdf/writer/fonts/Times-Bold.afm +2588 -0
  58. data/vendor/pdf/writer/fonts/Times-BoldItalic.afm +2384 -0
  59. data/vendor/pdf/writer/fonts/Times-Italic.afm +2667 -0
  60. data/vendor/pdf/writer/fonts/Times-Roman.afm +2419 -0
  61. data/vendor/pdf/writer/fonts/ZapfDingbats.afm +225 -0
  62. data/vendor/pdf/writer/graphics.rb +813 -0
  63. data/vendor/pdf/writer/graphics/imageinfo.rb +365 -0
  64. data/vendor/pdf/writer/lang.rb +44 -0
  65. data/vendor/pdf/writer/lang/en.rb +104 -0
  66. data/vendor/pdf/writer/object.rb +23 -0
  67. data/vendor/pdf/writer/object/action.rb +40 -0
  68. data/vendor/pdf/writer/object/annotation.rb +42 -0
  69. data/vendor/pdf/writer/object/catalog.rb +39 -0
  70. data/vendor/pdf/writer/object/contents.rb +69 -0
  71. data/vendor/pdf/writer/object/destination.rb +40 -0
  72. data/vendor/pdf/writer/object/encryption.rb +53 -0
  73. data/vendor/pdf/writer/object/font.rb +68 -0
  74. data/vendor/pdf/writer/object/fontdescriptor.rb +34 -0
  75. data/vendor/pdf/writer/object/fontencoding.rb +40 -0
  76. data/vendor/pdf/writer/object/image.rb +308 -0
  77. data/vendor/pdf/writer/object/info.rb +79 -0
  78. data/vendor/pdf/writer/object/outline.rb +30 -0
  79. data/vendor/pdf/writer/object/outlines.rb +30 -0
  80. data/vendor/pdf/writer/object/page.rb +195 -0
  81. data/vendor/pdf/writer/object/pages.rb +115 -0
  82. data/vendor/pdf/writer/object/procset.rb +46 -0
  83. data/vendor/pdf/writer/object/viewerpreferences.rb +74 -0
  84. data/vendor/pdf/writer/ohash.rb +58 -0
  85. data/vendor/pdf/writer/oreader.rb +25 -0
  86. data/vendor/pdf/writer/state.rb +48 -0
  87. data/vendor/pdf/writer/strokestyle.rb +140 -0
  88. data/vendor/transaction/simple.rb +693 -0
  89. data/vendor/transaction/simple/group.rb +133 -0
  90. data/vendor/transaction/simple/threadsafe.rb +52 -0
  91. data/vendor/transaction/simple/threadsafe/group.rb +23 -0
  92. data/vendor/xml-mapping/ChangeLog +128 -0
  93. data/vendor/xml-mapping/LICENSE +56 -0
  94. data/vendor/xml-mapping/README +386 -0
  95. data/vendor/xml-mapping/README_XPATH +175 -0
  96. data/vendor/xml-mapping/Rakefile +214 -0
  97. data/vendor/xml-mapping/TODO.txt +32 -0
  98. data/vendor/xml-mapping/doc/xpath_impl_notes.txt +119 -0
  99. data/vendor/xml-mapping/examples/company.rb +34 -0
  100. data/vendor/xml-mapping/examples/company.xml +26 -0
  101. data/vendor/xml-mapping/examples/company_usage.intin.rb +19 -0
  102. data/vendor/xml-mapping/examples/company_usage.intout +39 -0
  103. data/vendor/xml-mapping/examples/order.rb +61 -0
  104. data/vendor/xml-mapping/examples/order.xml +54 -0
  105. data/vendor/xml-mapping/examples/order_signature_enhanced.rb +7 -0
  106. data/vendor/xml-mapping/examples/order_signature_enhanced.xml +9 -0
  107. data/vendor/xml-mapping/examples/order_signature_enhanced_usage.intin.rb +12 -0
  108. data/vendor/xml-mapping/examples/order_signature_enhanced_usage.intout +16 -0
  109. data/vendor/xml-mapping/examples/order_usage.intin.rb +73 -0
  110. data/vendor/xml-mapping/examples/order_usage.intout +147 -0
  111. data/vendor/xml-mapping/examples/time_augm.intin.rb +19 -0
  112. data/vendor/xml-mapping/examples/time_augm.intout +23 -0
  113. data/vendor/xml-mapping/examples/time_node.rb +27 -0
  114. data/vendor/xml-mapping/examples/xpath_create_new.intin.rb +85 -0
  115. data/vendor/xml-mapping/examples/xpath_create_new.intout +181 -0
  116. data/vendor/xml-mapping/examples/xpath_docvsroot.intin.rb +30 -0
  117. data/vendor/xml-mapping/examples/xpath_docvsroot.intout +34 -0
  118. data/vendor/xml-mapping/examples/xpath_ensure_created.intin.rb +62 -0
  119. data/vendor/xml-mapping/examples/xpath_ensure_created.intout +114 -0
  120. data/vendor/xml-mapping/examples/xpath_pathological.intin.rb +42 -0
  121. data/vendor/xml-mapping/examples/xpath_pathological.intout +56 -0
  122. data/vendor/xml-mapping/examples/xpath_usage.intin.rb +51 -0
  123. data/vendor/xml-mapping/examples/xpath_usage.intout +57 -0
  124. data/vendor/xml-mapping/install.rb +40 -0
  125. data/vendor/xml-mapping/lib/xml/mapping.rb +14 -0
  126. data/vendor/xml-mapping/lib/xml/mapping/base.rb +571 -0
  127. data/vendor/xml-mapping/lib/xml/mapping/standard_nodes.rb +343 -0
  128. data/vendor/xml-mapping/lib/xml/mapping/version.rb +8 -0
  129. data/vendor/xml-mapping/lib/xml/xxpath.rb +354 -0
  130. data/vendor/xml-mapping/test/all_tests.rb +6 -0
  131. data/vendor/xml-mapping/test/company.rb +56 -0
  132. data/vendor/xml-mapping/test/documents_folders.rb +33 -0
  133. data/vendor/xml-mapping/test/fixtures/bookmarks1.xml +24 -0
  134. data/vendor/xml-mapping/test/fixtures/company1.xml +85 -0
  135. data/vendor/xml-mapping/test/fixtures/documents_folders.xml +71 -0
  136. data/vendor/xml-mapping/test/fixtures/documents_folders2.xml +30 -0
  137. data/vendor/xml-mapping/test/multiple_mappings.rb +80 -0
  138. data/vendor/xml-mapping/test/tests_init.rb +2 -0
  139. data/vendor/xml-mapping/test/xml_mapping_adv_test.rb +84 -0
  140. data/vendor/xml-mapping/test/xml_mapping_test.rb +201 -0
  141. data/vendor/xml-mapping/test/xpath_test.rb +273 -0
  142. metadata +191 -0
@@ -0,0 +1,133 @@
1
+ require 'transaction/simple'
2
+
3
+ # A transaction group is an object wrapper that manages a group of objects
4
+ # as if they were a single object for the purpose of transaction
5
+ # management. All transactions for this group of objects should be
6
+ # performed against the transaction group object, not against individual
7
+ # objects in the group.
8
+ #
9
+ # == Transaction Group Usage
10
+ # require 'transaction/simple/group'
11
+ #
12
+ # x = "Hello, you."
13
+ # y = "And you, too."
14
+ #
15
+ # g = Transaction::Simple::Group.new(x, y)
16
+ # g.start_transaction(:first) # -> [ x, y ]
17
+ # g.transaction_open?(:first) # -> true
18
+ # x.transaction_open?(:first) # -> true
19
+ # y.transaction_open?(:first) # -> true
20
+ #
21
+ # x.gsub!(/you/, "world") # -> "Hello, world."
22
+ # y.gsub!(/you/, "me") # -> "And me, too."
23
+ #
24
+ # g.start_transaction(:second) # -> [ x, y ]
25
+ # x.gsub!(/world/, "HAL") # -> "Hello, HAL."
26
+ # y.gsub!(/me/, "Dave") # -> "And Dave, too."
27
+ # g.rewind_transaction(:second) # -> [ x, y ]
28
+ # x # -> "Hello, world."
29
+ # y # -> "And me, too."
30
+ #
31
+ # x.gsub!(/world/, "HAL") # -> "Hello, HAL."
32
+ # y.gsub!(/me/, "Dave") # -> "And Dave, too."
33
+ #
34
+ # g.commit_transaction(:second) # -> [ x, y ]
35
+ # x # -> "Hello, HAL."
36
+ # y # -> "And Dave, too."
37
+ #
38
+ # g.abort_transaction(:first) # -> [ x, y ]
39
+ # x = -> "Hello, you."
40
+ # y = -> "And you, too."
41
+ class Transaction::Simple::Group
42
+ # Creates a transaction group for the provided objects. If a block is
43
+ # provided, the transaction group object is yielded to the block; when
44
+ # the block is finished, the transaction group object will be cleared
45
+ # with #clear.
46
+ def initialize(*objects)
47
+ @objects = objects || []
48
+ @objects.freeze
49
+ @objects.each { |obj| obj.extend(Transaction::Simple) }
50
+
51
+ if block_given?
52
+ begin
53
+ yield self
54
+ ensure
55
+ self.clear
56
+ end
57
+ end
58
+ end
59
+
60
+ # Returns the objects that are covered by this transaction group.
61
+ attr_reader :objects
62
+
63
+ # Clears the object group. Removes references to the objects so that
64
+ # they can be garbage collected.
65
+ def clear
66
+ @objects = @objects.dup.clear
67
+ end
68
+
69
+ # Tests to see if all of the objects in the group have an open
70
+ # transaction. See Transaction::Simple#transaction_open? for more
71
+ # information.
72
+ def transaction_open?(name = nil)
73
+ @objects.inject(true) do |val, obj|
74
+ val = val and obj.transaction_open?(name)
75
+ end
76
+ end
77
+
78
+ # Returns the current name of the transaction for the group.
79
+ # Transactions not explicitly named are named +nil+.
80
+ def transaction_name
81
+ @objects[0].transaction_name
82
+ end
83
+
84
+ # Starts a transaction for the group. Stores the current object state.
85
+ # If a transaction name is specified, the transaction will be named.
86
+ # Transaction names must be unique. Transaction names of +nil+ will be
87
+ # treated as unnamed transactions.
88
+ def start_transaction(name = nil)
89
+ @objects.each { |obj| obj.start_transaction(name) }
90
+ end
91
+
92
+ # Rewinds the transaction. If +name+ is specified, then the intervening
93
+ # transactions will be aborted and the named transaction will be
94
+ # rewound. Otherwise, only the current transaction is rewound.
95
+ def rewind_transaction(name = nil)
96
+ @objects.each { |obj| obj.rewind_transaction(name) }
97
+ end
98
+
99
+ # Aborts the transaction. Resets the object state to what it was before
100
+ # the transaction was started and closes the transaction. If +name+ is
101
+ # specified, then the intervening transactions and the named transaction
102
+ # will be aborted. Otherwise, only the current transaction is aborted.
103
+ #
104
+ # If the current or named transaction has been started by a block
105
+ # (Transaction::Simple.start), then the execution of the block will be
106
+ # halted with +break+ +self+.
107
+ def abort_transaction(name = nil)
108
+ @objects.each { |obj| obj.abort_transaction(name) }
109
+ end
110
+
111
+ # If +name+ is +nil+ (default), the current transaction level is closed
112
+ # out and the changes are committed.
113
+ #
114
+ # If +name+ is specified and +name+ is in the list of named
115
+ # transactions, then all transactions are closed and committed until the
116
+ # named transaction is reached.
117
+ def commit_transaction(name = nil)
118
+ @objects.each { |obj| obj.commit_transaction(name) }
119
+ end
120
+
121
+ # Alternative method for calling the transaction methods. An optional
122
+ # name can be specified for named transaction support.
123
+ #
124
+ # #transaction(:start):: #start_transaction
125
+ # #transaction(:rewind):: #rewind_transaction
126
+ # #transaction(:abort):: #abort_transaction
127
+ # #transaction(:commit):: #commit_transaction
128
+ # #transaction(:name):: #transaction_name
129
+ # #transaction:: #transaction_open?
130
+ def transaction(action = nil, name = nil)
131
+ @objects.each { |obj| obj.transaction(action, name) }
132
+ end
133
+ end
@@ -0,0 +1,52 @@
1
+ require 'transaction/simple'
2
+ require 'thread'
3
+
4
+ class Transaction::TransactionThreadError < StandardError
5
+ end
6
+
7
+ # = Transaction::Simple::ThreadSafe
8
+ # Thread-safe simple object transaction support for Ruby.
9
+ # Transaction::Simple::ThreadSafe is used in the same way as
10
+ # Transaction::Simple. Transaction::Simple::ThreadSafe uses a Mutex object
11
+ # to ensure atomicity at the cost of performance in threaded applications.
12
+ #
13
+ # Transaction::Simple::ThreadSafe will not wait to obtain a lock; if the
14
+ # lock cannot be obtained immediately, a
15
+ # Transaction::TransactionThreadError will be raised.
16
+ #
17
+ # Thanks to Mauricio Fern�ndez for help with getting this part working.
18
+ #
19
+ # Threadsafe transactions can be used in any place that normal
20
+ # transactions would. The main difference would be in setup:
21
+ #
22
+ # require 'transaction/simple/threadsafe'
23
+ #
24
+ # x = "Hello, you."
25
+ # x.extend(Transaction::Simple::ThreadSafe) # Threadsafe
26
+ #
27
+ # y = "Hello, you."
28
+ # y.extend(Transaction::Simple) # Not threadsafe
29
+ module Transaction::Simple::ThreadSafe
30
+ include Transaction::Simple
31
+
32
+ SKIP_TRANSACTION_VARS = Transaction::Simple::SKIP_TRANSACTION_VARS.dup #:nodoc:
33
+ SKIP_TRANSACTION_VARS << "@__transaction_mutex__"
34
+
35
+ Transaction::Simple.instance_methods(false) do |meth|
36
+ next if meth == "transaction"
37
+ arg = "(name = nil)" unless meth == "transaction_name"
38
+ module_eval <<-EOS
39
+ def #{meth}#{arg}
40
+ if (@__transaction_mutex__ ||= Mutex.new).try_lock
41
+ result = super
42
+ @__transaction_mutex__.unlock
43
+ return result
44
+ else
45
+ raise TransactionThreadError, Messages[:cannot_obtain_transaction_lock] % meth
46
+ end
47
+ ensure
48
+ @__transaction_mutex__.unlock
49
+ end
50
+ EOS
51
+ end
52
+ end
@@ -0,0 +1,23 @@
1
+ require 'transaction/simple/threadsafe'
2
+
3
+ # A transaction group is an object wrapper that manages a group of objects
4
+ # as if they were a single object for the purpose of transaction
5
+ # management. All transactions for this group of objects should be
6
+ # performed against the transaction group object, not against individual
7
+ # objects in the group. This is the threadsafe version of a transaction
8
+ # group.
9
+ class Transaction::Simple::ThreadSafe::Group < Transaction::Simple::Group
10
+ def initialize(*objects)
11
+ @objects = objects || []
12
+ @objects.freeze
13
+ @objects.each { |obj| obj.extend(Transaction::Simple::ThreadSafe) }
14
+
15
+ if block_given?
16
+ begin
17
+ yield self
18
+ ensure
19
+ self.clear
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,128 @@
1
+ 2005-12-07 Olaf Klischat
2
+
3
+ * release 0.8.1
4
+
5
+ 2005-12-07 Olaf Klischat
6
+
7
+ * ChangeLog file
8
+
9
+ 2005/11/30 Olaf Klischat
10
+
11
+ * bugfix: clone default values to avoid external modifications
12
+
13
+ 2005/07/07 Olaf Klischat
14
+
15
+ * release 0.8
16
+
17
+ 2005/07/04 Olaf Klischat
18
+
19
+ * xml/xpath / XML::XPath -> xml/xxpath / XML::XXPath, license ->
20
+ Ruby's
21
+
22
+ 2005/06/29 Olaf Klischat
23
+
24
+ * when creating elt[@attr='value'] path elements, add a new
25
+ element if one with @attr='value' already existed
26
+
27
+ 2005/03/30 Olaf Klischat
28
+
29
+ * add_accessor: check for existing accessors.
30
+
31
+ 2005/03/05 Olaf Klischat
32
+
33
+ * better support for inheritance among mapping
34
+ classes
35
+
36
+ 2005/03/03 Olaf Klischat
37
+
38
+ * "polymorphic" nodes via root element
39
+ name. SubObjectBaseNode-based nodes es use node polymorphy when
40
+ no explicit node marshaller/unmarshaller has been sp ecified.
41
+
42
+ 2005/02/28 Olaf Klischat
43
+
44
+ * mapping root elt name => mapping class;
45
+ XML::Mapping::load_object_from_* implemented
46
+
47
+ 2005/02/13 Olaf Klischat
48
+
49
+ * IntNode renamed & generalized to NumericNode
50
+
51
+ 2005/02/12 Olaf Klischat
52
+
53
+ * renaming *_rexml => *_xml
54
+
55
+ 2005/01/27 Olaf Klischat
56
+
57
+ * special exception NoAttrValueSet for indicating absence of a
58
+ specific attribute in an XML source
59
+
60
+ 2005/01/23 Olaf Klischat
61
+
62
+ * some more documentation, Node.obj_initializing, setting node
63
+ values to defaults on initialization
64
+
65
+ 2005/01/10 Olaf Klischat
66
+
67
+ * root_element_name
68
+
69
+ 2005/01/07 Olaf Klischat
70
+
71
+ * refactoring:
72
+
73
+ Made node types (classes) dynamically addable via
74
+ XML::Mapping.add_node_class, xml/mapping.rb moved to
75
+ xml/mapping/base.rb, node types moved to
76
+ xml/mapping/standard_nodes.rb, xml/mapping.rb now requires base
77
+ and standard_nodes and adds all standard node types to
78
+ XML::Mapping.
79
+
80
+ * additional node class SingleAttributeNode < Node for nodes that
81
+ map to a single attribute in their class (that's true for all
82
+ nodes we have so far). Call to add_attribute moved from "core"
83
+ to SingleAttributeNode.initialize.
84
+
85
+ * XML::Mapping: @nodes renamed to @xml_mapping_nodes to minimize
86
+ chance of name clashes.
87
+
88
+ 2004/12/30 Olaf Klischat
89
+
90
+ * array node writing, hash node writing
91
+
92
+
93
+ 2004/12/30 Olaf Klischat
94
+
95
+ * xpath: create_new flag, + convenience method
96
+
97
+ 2004/12/21 Olaf Klischat
98
+
99
+ * node classes
100
+
101
+ 2004/12/20 Olaf Klischat
102
+
103
+ * hash_node
104
+
105
+ 2004/12/08 Olaf Klischat
106
+
107
+ * xpath: attribute nodes
108
+
109
+ * xml_mapping: retargeted from REXML::XPath to XML::XPath
110
+
111
+ 2004/12/02 Olaf Klischat
112
+
113
+ * xpath: write accessors
114
+
115
+ 2004/11/27 Olaf Klischat
116
+
117
+ * xpath: read access seems to work
118
+
119
+ 2004/11/25 Olaf Klischat
120
+
121
+ * array_node
122
+
123
+ stone age Olaf Klischat
124
+
125
+ * see http://rubygarden.org/ruby?XmlMapping
126
+
127
+
128
+
@@ -0,0 +1,56 @@
1
+ Xml-mapping is copyrighted free software by Olaf Klischat
2
+ <klischat@cs.tu-berlin.de>. You can redistribute it and/or modify it
3
+ under either the terms of the GPL, or the conditions below:
4
+
5
+ 1. You may make and give away verbatim copies of the source form of the
6
+ software without restriction, provided that you duplicate all of the
7
+ original copyright notices and associated disclaimers.
8
+
9
+ 2. You may modify your copy of the software in any way, provided that
10
+ you do at least ONE of the following:
11
+
12
+ a) place your modifications in the Public Domain or otherwise
13
+ make them Freely Available, such as by posting said
14
+ modifications to Usenet or an equivalent medium, or by allowing
15
+ the author to include your modifications in the software.
16
+
17
+ b) use the modified software only within your corporation or
18
+ organization.
19
+
20
+ c) give non-standard binaries non-standard names, with
21
+ instructions on where to get the original software distribution.
22
+
23
+ d) make other distribution arrangements with the author.
24
+
25
+ 3. You may distribute the software in object code or binary form,
26
+ provided that you do at least ONE of the following:
27
+
28
+ a) distribute the binaries and library files of the software,
29
+ together with instructions (in the manual page or equivalent)
30
+ on where to get the original distribution.
31
+
32
+ b) accompany the distribution with the machine-readable source of
33
+ the software.
34
+
35
+ c) give non-standard binaries non-standard names, with
36
+ instructions on where to get the original software distribution.
37
+
38
+ d) make other distribution arrangements with the author.
39
+
40
+ 4. You may modify and include the part of the software into any other
41
+ software (possibly commercial). But some files in the distribution
42
+ are not written by the author, so that they are not under these terms.
43
+
44
+ For the list of those files and their copying conditions, see the
45
+ file LEGAL.
46
+
47
+ 5. The scripts and library files supplied as input to or produced as
48
+ output from the software do not automatically fall under the
49
+ copyright of the software, but belong to whomever generated them,
50
+ and may be sold commercially, and may be aggregated with this
51
+ software.
52
+
53
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
54
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
55
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
56
+ PURPOSE.
@@ -0,0 +1,386 @@
1
+ = XML-MAPPING: XML-to-object (and back) mapper for Ruby, including XPath interpreter
2
+
3
+ Xml-mapping is an easy to use, extensible library that allows you to
4
+ semi-automatically map Ruby objects to XML trees and vice versa. It is
5
+ easy to use and has a modular design that allows for easy extension of
6
+ its functionality.
7
+
8
+ == Download
9
+
10
+ For downloading the latest version, CVS repository access etc. go to:
11
+
12
+ http://rubyforge.org/projects/xml-mapping/
13
+
14
+ == Example
15
+
16
+ (example document stolen + extended from
17
+ http://www.castor.org/xml-mapping.html)
18
+
19
+ === Input document:
20
+
21
+ :include: order.xml
22
+
23
+ === Mapping class declaration:
24
+
25
+ :include: order.rb
26
+
27
+ === Usage:
28
+
29
+ :include: order_usage.intout
30
+
31
+
32
+ == Description
33
+
34
+ As shown in the example, you have to include XML::Mapping into a class
35
+ to turn it into a "mapping class". There are no other restrictions
36
+ imposed on mapping classes; you can add attributes and methods to
37
+ them, include additional modules in them, derive them from other
38
+ classes, derive other classes from them etc.pp.
39
+
40
+ An instance of a mapping class can be created from/converted into an
41
+ XML node by means of instance methods like XML::Mapping.load_from_xml,
42
+ XML::Mapping#save_to_xml, XML::Mapping.load_from_file,
43
+ XML::Mapping#save_to_file. Special class methods like "text_node",
44
+ "array_node" etc., called "node factory methods", may be called from
45
+ the body of the class definition to define instance attributes that
46
+ are automatically and bidirectionally mapped to subtrees of the XML
47
+ element an instance of the class is mapped to. For example, in the
48
+ definition
49
+
50
+ class Address
51
+ include XML::Mapping
52
+
53
+ text_node :city, "City"
54
+ text_node :state, "State"
55
+ numeric_node :zip, "ZIP"
56
+ text_node :street, "Street"
57
+ end
58
+
59
+ the first call to #text_node creates an attribute named "city" which
60
+ is mapped to the text of the XML child element defined by the XPath
61
+ expression "City" (xml-mapping includes an XPath interpreter that can
62
+ also be used seperately; see below). When you create an instance of
63
+ +Address+ from an XML element (using Address.load_from_file(file_name)
64
+ or Address.load_from_xml(rexml_element)), that instance's "city"
65
+ attribute will be set to the text of the XML element's "City" child
66
+ element. When you convert an instance of Address into an XML element,
67
+ a sub-element "City" is added and it text is set to the current value
68
+ of the +city+ attribute. The other node types (numeric_node,
69
+ array_node etc.) work analogously. The node types +object_node+,
70
+ +array_node+, and +hash_node+ recursively map sub-trees to instances
71
+ of mapping classes (as opposed to simple types like String
72
+ etc.). For example, with the line
73
+
74
+ array_node :signatures, "Signed-By", "Signature", :class=>Signature, :default_value=>[]
75
+
76
+ , an attribute named "signatures" is added to the surrounding class
77
+ (here: Order); the attribute will be an array whose elements
78
+ correspond to the XML elements yielded by the XPath
79
+ "Signed-By/Signature". Each element will be of class +Signature+ (each
80
+ array element is created from the corresponding XML element by just
81
+ calling <tt>Signature.load_from_xml(the_xml_element)</tt>). The reason
82
+ why the path "Signed-By/Signature" is provieded in two arguments
83
+ instead of just one combined one becomes apparent when marshalling the
84
+ array (along with the surrounding object) back into a sequence of XML
85
+ elements. When that happens, "Signed-By" names the common base element
86
+ for all those elements, and "Signature" is the path that will be
87
+ duplicated for each element. The input document in the example above
88
+ shows how this ends up looking.
89
+
90
+ Hash nodes work similarly, but they define hash-valued attributes
91
+ instead of array-valued ones.
92
+
93
+ Refer to the reference documentation for details about the node types
94
+ that are included in the xml-mapping library.
95
+
96
+
97
+ === Default values
98
+
99
+ For each node you may define a _default value_ which will be set if
100
+ there was no value defined for the attribute in the XML source.
101
+
102
+ From the example:
103
+
104
+ class Signature
105
+ include XML::Mapping
106
+
107
+ text_node :position, "Position", :default_value=>"Some Employee"
108
+ end
109
+
110
+ The semantics of default values are as follows:
111
+
112
+ - when creating a new instance from scratch:
113
+
114
+ - attributes with default values are set to their default values
115
+
116
+ - attributes without default values are left unset
117
+
118
+ (when defining your own initializer, you'll have to call the
119
+ inherited _initialize_ method in order to get this behaviour)
120
+
121
+ - when loading:
122
+
123
+ - attributes without default values that are not represented in the
124
+ XML raise an error
125
+
126
+ - attributes with default values that are not represented in the XML
127
+ are set to their default values
128
+
129
+ - all other attributes are set to their respective values as present
130
+ in the XML
131
+
132
+
133
+ - when saving:
134
+
135
+ - unset attributes without default values raise an error
136
+
137
+ - attributes with default values that are set to their default
138
+ values are not saved
139
+
140
+ - all other attributes are saved
141
+
142
+
143
+ This implies that:
144
+
145
+ - attributes that are set to their respective default values are not
146
+ represented in the XML
147
+
148
+ - attributes without default values must be set explicitly before
149
+ saving
150
+
151
+
152
+
153
+ === Attribute handling details, augmenting existing classes
154
+
155
+ I'll shed some more light on how xml-mapping adds mapped attributes to
156
+ Ruby classes. An attribute declaration like
157
+
158
+ text_node :city, "City"
159
+
160
+ maps some portion of the XML tree (here: the "City" sub-element) to an
161
+ attribute (here: "city") of the class whose body the declaration
162
+ appears in. When writing (marshalling) instances of the surrounding
163
+ class into an XML document, xml-mapping will read the attribute value
164
+ from the instance using the function named +city+; when reading
165
+ (unmarshalling) an instance from an XML document, xml-mapping will use
166
+ the one-parameter function <tt>city=</tt> to set the attribute in the
167
+ instance to the value read from the XML document.
168
+
169
+ If these functions don't exist at the time the node declaration is
170
+ executed, xml-mapping adds default implementations that simply
171
+ read/write the attribute value to instance variables that have the
172
+ same name as the attribute. For example, the +city+ attribute
173
+ declaration in the +Address+ class in the example added functions
174
+ +city+ and <tt>city=</tt> that read/write from/to the instance
175
+ variable <tt>@city</tt>.
176
+
177
+ If, however, these functions already exist prior to defining the
178
+ attributes, xml-mapping will leave them untouched, so your precious
179
+ self-written accessor methods that do whatever complicated internal
180
+ processing of the data won't be overwritten.
181
+
182
+ This means that you can not only create new mapping classes from
183
+ scratch, you can also take existing classes that contain some
184
+ "business logic" and "augment" them with xml-mapping capabilities. As
185
+ a simple example, let's augment Ruby's "Time" class with node
186
+ declarations that declare XML mappings for the day, month etc. fields:
187
+
188
+ :include: time_augm.intout
189
+
190
+ Here XML mappings are defined for the existing fields +year+, +month+
191
+ etc. Xml-apping noticed that the getter methods for those attributes
192
+ existed, so it didn't overwrite them. When calling +save_to_xml+ on a
193
+ +Time+ object, these methods are called and return the object's values
194
+ for those fields, which then get written to the output XML. Of course
195
+ you could also derive a new class from a pre-existing one and
196
+ implement the XML::Mapping stuff there, or even derive several such
197
+ classes in order to define more than one XML mapping for one existing
198
+ class.
199
+
200
+ It should be mentioned that in the +Time+ example above, the setter
201
+ methods (<tt>year=</tt>, <tt>month=</tt> etc.) didn't exist in +Time+
202
+ (+Time+ objects are immutable), so xml-mapping defined its own setter
203
+ methods that just set <tt>@year</tt>, <tt>@month</tt> etc., which is
204
+ pretty useless for this case. So you can't really read +Time+ values
205
+ back from an XML representation in this example. For that to work,
206
+ you'd need functioning <tt>blah=(x)</tt> methods for each +blah+
207
+ attribute that you want to define an XML mapping for.
208
+
209
+
210
+ === Defining your own node types
211
+
212
+ It's easy to write additional node types and register them with the
213
+ xml-mapping library. Let's say we want to extend the +Signature+ class
214
+ from the example to include the time at which the signature was
215
+ created. We want the new XML representation of such a signature to
216
+ look like this:
217
+
218
+ :include: order_signature_enhanced.xml
219
+
220
+ (we only save year, month and day to make this example shorter), and
221
+ the mapping class declaration to look like this:
222
+
223
+ :include: order_signature_enhanced.rb
224
+
225
+ (i.e. a new "time_node" declaration was added).
226
+
227
+ We want this +signed_on+ call to define an attribute named +signed_on+
228
+ which holds the date value from the XML in an instance of class
229
+ +Time+.
230
+
231
+ This node type can be defined with this piece of code:
232
+
233
+ :include: time_node.rb
234
+
235
+ The last line registers the new node type with the xml-mapping
236
+ library. The name of the node factory method ("time_node") is
237
+ automatically derived from the class name of the node type
238
+ ("TimeNode").
239
+
240
+ There will be one instance of the node type per mapping class (not per
241
+ mapping class instance). That instance will be created by the node
242
+ factory method (+time_node+); there's no need to instantiate the node
243
+ type directly. Whenever an instance of the mapping class needs to be
244
+ marshalled/unmarshalled to/from XML, +set_attr_value+
245
+ resp. +extract_attr_value+ will be called on the node type instance
246
+ ("node" for short). The node factory method places the node into the
247
+ mapping class; the @owner attribute of the node is set to reference
248
+ the mapping class. The node factory method passes its arguments (in
249
+ the example, that would be <tt>:signed_on, "signed-on",
250
+ :default_value=>Time.now</tt>) to the node's initializer. TimeNode's
251
+ parent class XML::Mapping::SingleAttributeNode already handles the
252
+ <tt>:signed_on</tt> and <tt>:default_value=>Time.now</tt> arguments --
253
+ <tt>:signed_on</tt> is stored into <tt>@attrname</tt>, and the default
254
+ value declarations will be described in a moment. The remaining
255
+ argument <tt>"signed-on"</tt> gets passed to our +initialize_impl+
256
+ method as parameter _path_. We'll interpret it as an XPath expression
257
+ that locates the time value relative to the parent mapping object's
258
+ XML tree (in this case, this would be the XML tree rooted at the
259
+ +<Signature>+ element, i.e. the tree the +Signature+ instance was read
260
+ from). We'll later have to read/store the year, month, and day values
261
+ from <tt>path+"/year"</tt>, <tt>path+"/month"</tt>, and
262
+ <tt>path+"/day"</tt>, respectively, so we create (and precompile)
263
+ three corresponding XPath expressions using XML::XXPath.new and store
264
+ them into member variables of the node. XML::XXPath is an XPath
265
+ implementation that is bundled with xml-mapping. It is very
266
+ incomplete, but it supports writing (not just reading) of XML nodes,
267
+ which is needed to support writing data back to XML. The XML::XXPath
268
+ library is explained in more detail below.
269
+
270
+ The +extract_attr_value+ method is called whenever an instance of the
271
+ class the node belongs to (+Signature+ in the example) is being
272
+ created from an XML tree. The parameter _xml_ is that tree (again,
273
+ this is the tree rooted at the +<Signature>+ element in this
274
+ example). The method implementation is expected to extract the
275
+ attribute's value from _xml_ and return it, or raise
276
+ XML::Mapping::SingleAttributeNode::NoAttrValueSet if the attribute was
277
+ "unset" in the XML (so the default value should be put in place if it
278
+ was defined), or raise any other exception to signal an error and
279
+ abort the whole process. In our implementation, we apply the xpath
280
+ expressions created at initialization to _xml_
281
+ (e.g. <tt>@y_path.first(xml)</tt>). An expression
282
+ _xpath_expr_.first(_xml_) returns (as a REXML element) the first
283
+ sub-element of _xml_ that matches _xpath_expr_, or raises
284
+ XML::XXPathError if there was no such element. We apply REXML's _text_
285
+ method to the returned element to get out the element's text, convert
286
+ it to integer, and supply it to the constructor of the +Time+ object
287
+ to be returned. (as a side note, if an XPath expression matches XML
288
+ attributes, XML::XXPath methods like _first_ will return "Attribute"
289
+ nodes that behave similarly to REXML::Element nodes, including
290
+ messages like _name_ and _text_ (XML::XXPath extends REXML to support
291
+ this because REXML's Attribute class is too incompatible), so this
292
+ would've worked also if our XPath expressions named XML attributes,
293
+ not elements). The +default_when_xpath_err+ thing calls the supplied
294
+ block and returns its value, but maps the exception XML::XXPathError to
295
+ the mentioned XML::Mapping::SingleAttributeNode::NoAttrValueSet (any
296
+ other exceptions fall through unchanged). As said above,
297
+ XML::Mapping::NoAttrValueSet is then caught by our superclass
298
+ (XML::Mapping::SingleAttributeNode), and the default value is set if
299
+ it was provided. So you should just wrap +default_when_xpath_err+
300
+ around any applications of XPath expressions whose non-presence in the
301
+ XML you want to be considered a non-presence of the attribute you're
302
+ trying to extract. (XML::XXPath is designed to know knothing about
303
+ XML::Mapping, so it doesn't raise
304
+ XML::Mapping::SingleAttributeNode::NoAttrValueSet directly)
305
+
306
+ The +set_attr_value+ method is called whenever an instance of the
307
+ class the node belongs to (+Signature+ in the example) is being stored
308
+ into an XML tree. The _xml_ parameter is the XML tree (a REXML element
309
+ node; here this is again the tree rooted at the +<Signature>+
310
+ element); _value_ is the current value of the attribute. _xml_ will
311
+ most probably be "half-populated" by the time this method is called --
312
+ the framework calls the +set_attr_value+ methods of all nodes of a
313
+ mapping class in the order of their definition, letting each node fill
314
+ its "bit" into _xml_. The method implementation is expected to write
315
+ _value_ into (the correct sub-elements of) _xml_, or raise an
316
+ exception to signal an error and abort the whole process. No default
317
+ value handling is done here; +set_attr_value+ won't be called at all
318
+ if the attribute had been set to its default value. In our
319
+ implementation we grab the year, month and day values from _value_
320
+ (which must be a +Time+), and store it into the sub-elements of _xml_
321
+ identified by XPath expressions <tt>@y_path</tt>, <tt>@m_path</tt> and
322
+ <tt>@d_path</tt>, respectively. We do this by calling XML::XXPath#first
323
+ with an additional parameter <tt>:ensure_created=>true</tt>. An
324
+ expression _xpath_expr_.first(_xml_,:ensure_created=>true) works just
325
+ like _xpath_expr_.first(_xml_) if _xpath_expr_ was already present in
326
+ _xml_. If it was not, it is created (preferable at the end of _xml_'s
327
+ list of sub-nodes), and returned. See below for a more detailed
328
+ documentation of the XPath interpreter.
329
+
330
+ === Element order in created XML documents
331
+
332
+ As just said, XML::XXPath, when used to create new XML nodes, generally
333
+ appends those nodes to the end of the list of subnodes of the node the
334
+ xpath expression was applied to. All xml-mapping nodes that come with
335
+ xml-mapping use XML::XXPath when writing data to XML, and therefore
336
+ also append their data to the XML data written by preceding nodes (the
337
+ nodes are invoked in the order of their definition). This means that,
338
+ generally, your output data will appear in the XML document in the
339
+ same order in which the corresponding xml-mapping node definitions
340
+ appeared in the mapping class (unless you used XPath expressions like
341
+ foo[number] which explicitly dictate a fixed position in the sequence
342
+ of XML nodes). For instance, in the example from the beginning of this
343
+ document, if we put the <tt>:signatures</tt> node _before_ the
344
+ <tt>:items</tt> node, the <tt><Signed-By></tt> element will appear
345
+ _before_ the sequence of <tt><Item></tt> elements in the output XML.
346
+
347
+
348
+
349
+ == XPath interpreter
350
+
351
+ XML::XXPath is an XPath parser. It is used in xml-mapping node type
352
+ definitions, but can just as well be utilized stand-alone (it does
353
+ not depend on xml-mapping). XML::XXPath is very incomplete and probably
354
+ will always be (it only supports path elements of types _elt_name_,
355
+ @_attr_name_, _elt_name_[@_attr_name_=_attr_value_],
356
+ _elt_name_[_index_], and *), but it should be reasonably efficient
357
+ (XPath expressions are precompiled), and, most importantly, it
358
+ supports write access. For example, if you create the path
359
+ "/foo/bar[3]/baz[@key='hiho']" in the XML document
360
+
361
+ <foo>
362
+ <bar>
363
+ <baz key="ab">hello</baz>
364
+ <baz key="xy">goodbye</baz>
365
+ </bar>
366
+ </foo>
367
+
368
+ , you'll get:
369
+
370
+ <foo>
371
+ <bar>
372
+ <baz key='ab'>hello</baz>
373
+ <baz key='xy'>goodbye</baz>
374
+ </bar>
375
+ <bar/>
376
+ <bar>
377
+ <baz key='hiho'/>
378
+ </bar>
379
+ </foo>
380
+
381
+ XML::XXPath is explained in more detail in the reference documentation.
382
+
383
+
384
+ == License
385
+
386
+ Ruby's.