pdf-labels 1.0.0

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