libis-tools 1.0.5-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +40 -0
  6. data/Gemfile +7 -0
  7. data/README.md +202 -0
  8. data/Rakefile +11 -0
  9. data/bin/libis_tool +5 -0
  10. data/lib/libis-tools.rb +1 -0
  11. data/lib/libis/tools.rb +25 -0
  12. data/lib/libis/tools/assert.rb +52 -0
  13. data/lib/libis/tools/checksum.rb +106 -0
  14. data/lib/libis/tools/cli/cli_helper.rb +189 -0
  15. data/lib/libis/tools/cli/reorg.rb +416 -0
  16. data/lib/libis/tools/command.rb +133 -0
  17. data/lib/libis/tools/command_line.rb +23 -0
  18. data/lib/libis/tools/config.rb +147 -0
  19. data/lib/libis/tools/config_file.rb +85 -0
  20. data/lib/libis/tools/csv.rb +38 -0
  21. data/lib/libis/tools/deep_struct.rb +71 -0
  22. data/lib/libis/tools/extend/array.rb +16 -0
  23. data/lib/libis/tools/extend/empty.rb +7 -0
  24. data/lib/libis/tools/extend/hash.rb +147 -0
  25. data/lib/libis/tools/extend/kernel.rb +25 -0
  26. data/lib/libis/tools/extend/ostruct.rb +3 -0
  27. data/lib/libis/tools/extend/roo.rb +91 -0
  28. data/lib/libis/tools/extend/string.rb +94 -0
  29. data/lib/libis/tools/extend/struct.rb +29 -0
  30. data/lib/libis/tools/extend/symbol.rb +8 -0
  31. data/lib/libis/tools/logger.rb +130 -0
  32. data/lib/libis/tools/mets_dnx.rb +61 -0
  33. data/lib/libis/tools/mets_file.rb +504 -0
  34. data/lib/libis/tools/mets_objects.rb +547 -0
  35. data/lib/libis/tools/parameter.rb +372 -0
  36. data/lib/libis/tools/spreadsheet.rb +196 -0
  37. data/lib/libis/tools/temp_file.rb +42 -0
  38. data/lib/libis/tools/thread_safe.rb +31 -0
  39. data/lib/libis/tools/version.rb +5 -0
  40. data/lib/libis/tools/xml_document.rb +583 -0
  41. data/libis-tools.gemspec +55 -0
  42. data/spec/assert_spec.rb +65 -0
  43. data/spec/checksum_spec.rb +68 -0
  44. data/spec/command_spec.rb +90 -0
  45. data/spec/config_file_spec.rb +83 -0
  46. data/spec/config_spec.rb +113 -0
  47. data/spec/csv_spec.rb +159 -0
  48. data/spec/data/test-headers.csv +2 -0
  49. data/spec/data/test-headers.tsv +2 -0
  50. data/spec/data/test-noheaders.csv +1 -0
  51. data/spec/data/test-noheaders.tsv +1 -0
  52. data/spec/data/test.data +9 -0
  53. data/spec/data/test.xlsx +0 -0
  54. data/spec/data/test.xml +8 -0
  55. data/spec/data/test.yml +2 -0
  56. data/spec/data/test_config.yml +15 -0
  57. data/spec/deep_struct_spec.rb +138 -0
  58. data/spec/logger_spec.rb +165 -0
  59. data/spec/mets_file_spec.rb +223 -0
  60. data/spec/parameter_container_spec.rb +152 -0
  61. data/spec/parameter_spec.rb +148 -0
  62. data/spec/spec_helper.rb +29 -0
  63. data/spec/spreadsheet_spec.rb +1820 -0
  64. data/spec/temp_file_spec.rb +76 -0
  65. data/spec/test.xsd +20 -0
  66. data/spec/thread_safe_spec.rb +64 -0
  67. data/spec/xmldocument_spec.rb +421 -0
  68. data/test/test_helper.rb +7 -0
  69. data/test/webservices/test_ca_item_info.rb +59 -0
  70. data/test/webservices/test_ca_search.rb +35 -0
  71. metadata +437 -0
@@ -0,0 +1,42 @@
1
+ require 'libis/tools/extend/empty'
2
+
3
+ module Libis
4
+ module Tools
5
+
6
+ module TempFile
7
+
8
+ def self.dir
9
+ Dir.tmpdir
10
+ end
11
+
12
+ def self.name(prefix = '', suffix = '', _dir = nil)
13
+ _dir ||= dir
14
+ t = Time.now.strftime('%Y%m%d')
15
+ t = '_' + t unless prefix.empty?
16
+ File.join(_dir, "#{prefix}#{t}_#{$$}_#{rand(0x100000000).to_s(36)}#{suffix}".freeze)
17
+ end
18
+
19
+ def self.file(prefix = '', suffix = '', dir = nil)
20
+ f = File.open(name(prefix, suffix, dir), 'w')
21
+
22
+ def f.unlink
23
+ File.unlink self
24
+ end
25
+
26
+ def f.delete
27
+ File.delete self
28
+ end
29
+
30
+ if block_given?
31
+ x = yield(f)
32
+ f.close
33
+ f.delete
34
+ return x
35
+ else
36
+ return f
37
+ end
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,31 @@
1
+ require 'monitor'
2
+
3
+ module Libis
4
+ module Tools
5
+
6
+ # Module to safely create a mutex for creating thread safe classes.
7
+ #
8
+ # Usage: include this module in a class or extend a module with this one.
9
+ module ThreadSafe
10
+
11
+ # Access the instance mutex
12
+ def mutex
13
+ self.class.class_mutex.synchronize do
14
+ @mutex ||= Monitor.new
15
+ end
16
+ end
17
+
18
+ # @!visibility private
19
+ module MutexCreator
20
+ attr_accessor :class_mutex
21
+ end
22
+
23
+ # @!visibility private
24
+ def self.included(klass)
25
+ klass.extend(MutexCreator)
26
+ # noinspection RubyResolve
27
+ klass.class_mutex = Monitor.new
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ module Libis
2
+ module Tools
3
+ VERSION = '1.0.5'
4
+ end
5
+ end
@@ -0,0 +1,583 @@
1
+ # coding: utf-8
2
+
3
+ require 'nokogiri'
4
+ require 'gyoku'
5
+ require 'nori'
6
+
7
+ module Libis
8
+ module Tools
9
+
10
+ # noinspection RubyTooManyMethodsInspection
11
+
12
+ # This class embodies most used features of Nokogiri, Nori and Gyoku in one convenience class. The Nokogiri document
13
+ # is stored in the class variable 'document' and can be accessed and manipulated directly - if required.
14
+ #
15
+ # In the examples we assume the following XML code:
16
+ #
17
+ # <?xml version="1.0" encoding="utf-8"?>
18
+ # <patron>
19
+ # <name>Harry Potter</name>
20
+ # <barcode library='Hogwarts Library'>1234567890</barcode>
21
+ # <access_level>student</access_level>
22
+ # <email>harry.potter@hogwarts.edu</email>
23
+ # <email>hpotter@JKRowling.com</email>
24
+ # </patron>
25
+ class XmlDocument
26
+
27
+ attr_accessor :document
28
+
29
+ # Check if the embedded XML document is not present or invalid.
30
+ def invalid?
31
+ @document.nil? or !document.is_a?(::Nokogiri::XML::Document) or @document.root.nil?
32
+ end
33
+
34
+ # Check if the embedded XML document is present and valid
35
+ def valid?
36
+ !invalid?
37
+ end
38
+
39
+ # Create new XmlDocument instance.
40
+ # The object will contain a new and emtpy Nokogiri XML Document.
41
+ # The object will not be valid until a root node is added.
42
+ # @param [String] encoding character encoding for the XML content; default value is 'utf-8'
43
+ # @return [XmlDocument] new instance
44
+ def initialize(encoding = 'utf-8')
45
+ @document = Nokogiri::XML::Document.new
46
+ @document.encoding = encoding
47
+ end
48
+
49
+ # Create a new instance initialized with the content of an XML file.
50
+ # @param [String] file path to the XML file
51
+ # @return [XmlDocument] new instance
52
+ def self.open(file)
53
+ doc = XmlDocument.new
54
+ doc.document = Nokogiri::XML(File.open(file))
55
+ doc
56
+ end
57
+
58
+ # Create a new instance initialized with an XML String.
59
+ # @param [String] xml XML string
60
+ # @return [XmlDocument] new instance
61
+ def self.parse(xml)
62
+ doc = XmlDocument.new
63
+ doc.document = Nokogiri::XML.parse(xml)
64
+ doc
65
+ end
66
+
67
+ # Create a new instance initialized with a Hash.
68
+ # @note The Hash will be converted with Gyoku. See the Gyoku documentation for the Hash format requirements.
69
+ # @param [Hash] hash the content
70
+ # @param [Hash] options options passed to Gyoku upon parsing the Hash into XML
71
+ # @return [XmlDocument] new instance
72
+ def self.from_hash(hash, options = {})
73
+ doc = XmlDocument.new
74
+ doc.document = Nokogiri::XML(Gyoku.xml(hash, options))
75
+ doc.document.encoding = 'utf-8'
76
+ doc
77
+ end
78
+
79
+ # Save the XML Document to a given XML file.
80
+ # @param [String] file name of the file to save to
81
+ # @param [Integer] indent amount of space for indenting; default 2
82
+ # @param [String] encoding character encoding; default 'utf-8'
83
+ def save(file, indent = 2, encoding = 'utf-8')
84
+ fd = File.open(file, 'w')
85
+ @document.write_xml_to(fd, :indent => indent, :encoding => encoding)
86
+ fd.close
87
+ end
88
+
89
+ # Export the XML Document to an XML string.
90
+ # @param [Hash] options options passed to the underlying Nokogiri::XML::Document#to_xml; default is:
91
+ # !{indent: 2, encoding: 'utf-8'}
92
+ # @return [String] a string
93
+ def to_xml(options = {})
94
+ options = {indent: 2, encoding: 'utf-8', save_with: Nokogiri::XML::Node::SaveOptions::DEFAULT_XML}.merge(options)
95
+ @document.to_xml(options)
96
+ end
97
+
98
+ # Export the XML Document to a Hash.
99
+ #
100
+ # @note The hash is generated using the Nori gem. The options passed to this call are used to configure Nori in
101
+ # the constructor call. For content and syntax see the
102
+ # {http://www.rubydoc.info/gems/nori/2.6.0 Nori documentation}. Nori also uses an enhanced
103
+ # String class with an extra method #attributes that will return a Hash containing tag-value pairs for each
104
+ # attribute of the XML element.
105
+ #
106
+ # Example:
107
+ #
108
+ # h = xml_doc.to_hash
109
+ # # => { "patron" =>
110
+ # { "name" => "Harry Potter",
111
+ # "barcode" => "1234567890",
112
+ # "access_level" => "student",
113
+ # "email" => ["harry.potter@hogwarts.edu", "hpotter@JKRowling.com"],
114
+ # } }
115
+ # h['patron']['barcode']
116
+ # # => "12345678890"
117
+ # h['patron']['barcode'].attributes
118
+ # # => {"library" => "Hogwarts Library"}
119
+ # h['patron']['barcode'].attributes['library']
120
+ # # => "Hogwarts Library"
121
+ #
122
+ # @param [Hash] options
123
+ # @return [Hash]
124
+ def to_hash(options = {})
125
+ Nori.new(options).parse(to_xml)
126
+ end
127
+
128
+ # Check if the document validates against a given XML schema file.
129
+ # @param [String] schema the file path of the XML schema
130
+ # @return [Boolean]
131
+ def validates_against?(schema)
132
+ schema_doc = Nokogiri::XML::Schema.new(File.open(schema))
133
+ schema_doc.valid?(@document)
134
+ end
135
+
136
+ # Check if the document validates against a given XML schema file.
137
+ # @return [Array<{Nokogiri::XML::SyntaxError}>] a list of validation errors
138
+ def validate(schema)
139
+ schema_doc = Nokogiri::XML::Schema.new(File.open(schema))
140
+ schema_doc.validate(@document)
141
+ end
142
+
143
+ # Add a processing instruction to the current XML document.
144
+ # @note unlike regular nodes, these nodes are automatically added to the document.
145
+ # @param [String] name instruction name
146
+ # @param [String] content instruction content
147
+ # @return [Nokogiri::XML::Node] the processing instruction node
148
+ def add_processing_instruction(name, content)
149
+ processing_instruction = Nokogiri::XML::ProcessingInstruction.new(@document, name, content)
150
+ @document.root.add_previous_sibling processing_instruction
151
+ processing_instruction
152
+ end
153
+
154
+ # Get the root node of the XML Document.
155
+ #
156
+ # Example:
157
+ #
158
+ # puts xml_doc.root.to_xml
159
+ # # =>
160
+ # <patron>
161
+ # ...
162
+ # </patron>
163
+ #
164
+ # @return [{Nokogiri::XML::Node}] the root node of the XML Document
165
+ def root
166
+ raise ArgumentError, 'XML document not valid.' if @document.nil?
167
+ @document.root
168
+ end
169
+
170
+ # Set the root node of the XML Document.
171
+ #
172
+ # Example:
173
+ #
174
+ # patron = ::Nokogiri::XML::Node.new 'patron', xml_doc.document
175
+ # xml_doc.root = patron
176
+ # puts xml_doc.to_xml
177
+ # # =>
178
+ # <?xml version="1.0" encoding="utf-8"?>
179
+ # <patron/>
180
+ #
181
+ # @param [{Nokogiri::XML::Node}] node new root node
182
+ # @return [{Nokogiri::XML::Node}] the new root node
183
+ def root=(node)
184
+ raise ArgumentError, 'XML document not valid.' if @document.nil?
185
+ #noinspection RubyArgCount
186
+ @document.root = node
187
+ end
188
+
189
+ # Creates nodes using the Nokogiri build short syntax.
190
+ #
191
+ # Example:
192
+ #
193
+ # xml_doc.build(xml_doc.root) do |xml|
194
+ # xml.books do
195
+ # xml.book title: 'Quidditch Through the Ages', author: 'Kennilworthy Whisp', due_date: '1992-4-23'
196
+ # end
197
+ # end
198
+ # p xml_doc.to_xml
199
+ # # =>
200
+ # <?xml version="1.0" encoding="utf-8"?>
201
+ # <patron>
202
+ # ...
203
+ # <books>
204
+ # <book title="Quidditch Through the Ages" author="Kennilworthy Whisp" due_date="1992-4-23"/>
205
+ # </books>
206
+ # </patron>
207
+ #
208
+ # @param [Nokogiri::XML::Node] at_node the node to attach the new nodes to;
209
+ # optional - if missing or nil the new nodes will replace the entire document
210
+ # @param [Code block] block Build instructions
211
+ # @return [XmlDocument] the XML Document itself
212
+ def build(at_node = nil, options = {}, &block)
213
+ options = {encoding: 'utf-8' }.merge options
214
+ if at_node
215
+ Nokogiri::XML::Builder.new(options,at_node, &block)
216
+ else
217
+ xml = Nokogiri::XML::Builder.new(options, &block)
218
+ @document = xml.doc
219
+ end
220
+ self
221
+ end
222
+
223
+ # Creates a new XML document with contents supplied in Nokogiri build short syntax.
224
+ #
225
+ # Example:
226
+ #
227
+ # xml_doc = ::Libis::Tools::XmlDocument.build do
228
+ # patron {
229
+ # name 'Harry Potter'
230
+ # barcode( '1234567890', library: 'Hogwarts Library')
231
+ # access_level 'student'
232
+ # email 'harry.potter@hogwarts.edu'
233
+ # email 'hpotter@JKRowling.com'
234
+ # books {
235
+ # book title: 'Quidditch Through the Ages', author: 'Kennilworthy Whisp', due_date: '1992-4-23'
236
+ # }
237
+ # }
238
+ # end
239
+ # p xml_doc.to_xml
240
+ # # =>
241
+ # <?xml version="1.0" encoding="utf-8"?>
242
+ # <patron>
243
+ # <name>Harry Potter</name>
244
+ # <barcode library="Hogwarts Library">1234567890</barcode>
245
+ # <access_level>student</access_level>
246
+ # <email>harry.potter@hogwarts.edu</email>
247
+ # <email>hpotter@JKRowling.com</email>
248
+ # <books>
249
+ # <book title="Quidditch Through the Ages" author="Kennilworthy Whisp" due_date="1992-4-23"/>
250
+ # </books>
251
+ # </patron>
252
+ #
253
+ # @param [Code block] block Build instructions
254
+ # @return [XmlDocument] the new XML Document
255
+ def self.build(&block)
256
+ self.new.build(nil, &block)
257
+ end
258
+
259
+ # Adds a new XML node to the document.
260
+ #
261
+ # Example:
262
+ #
263
+ # xml_doc = ::Libis::Tools::XmlDocument.new
264
+ # xml_doc.valid? # => false
265
+ # xml_doc.add_node :patron
266
+ # xml_doc.add_node :name, 'Harry Potter'
267
+ # books = xml_doc.add_node :books, nil, nil, namespaces: { jkr: 'http://JKRowling.com', node_ns: 'jkr' }
268
+ # xml_doc.add_node :book, nil, books,
269
+ # title: 'Quidditch Through the Ages', author: 'Kennilworthy Whisp', due_date: '1992-4-23',
270
+ # namespaces: { node_ns: 'jkr' }
271
+ # p xml_doc.to_xml
272
+ # # =>
273
+ # <?xml version="1.0" encoding="utf-8"?>
274
+ # <patron>
275
+ # <name>Harry Potter</name>
276
+ # <jkr:books xmlns:jkr="http://JKRowling.com">
277
+ # <jkr:book author="Kennilworthy Whisp" due_date="1992-4-23" title="Quidditch Through the Ages"/>
278
+ # </jkr:books>
279
+ # </patron>
280
+ #
281
+ # @param [Array] args arguments being:
282
+ # - tag for the new node
283
+ # - optional content for new node; empty if nil or not present
284
+ # - optional parent node for new node; root if nil or not present; xml document if root is not defined
285
+ # - a Hash containing tag-value pairs for each attribute; the special key ':namespaces'
286
+ # contains a Hash of namespace definitions as in {#add_namespaces}
287
+ # @return [Nokogiri::XML::Node] the new node
288
+ def add_node(*args)
289
+ attributes = {}
290
+ attributes = args.pop if args.last.is_a? Hash
291
+ name, value, parent = *args
292
+
293
+ return nil if name.nil?
294
+
295
+ node = Nokogiri::XML::Node.new name.to_s, @document
296
+ node.content = value
297
+
298
+ if !parent.nil?
299
+ parent << node
300
+ elsif !self.root.nil?
301
+ self.root << node
302
+ else
303
+ self.root = node
304
+ end
305
+
306
+ return node if attributes.empty?
307
+
308
+ namespaces = attributes.delete :namespaces
309
+ add_namespaces(node, namespaces) if namespaces
310
+
311
+ add_attributes(node, attributes) if attributes
312
+
313
+ node
314
+
315
+ end
316
+
317
+ # Add attributes to a node.
318
+ # @note The Nokogiri method Node#[]= is probably easier to use if you only want to add a single attribute ;the
319
+ # main purpose of this method is to make it easier to add attributes in bulk or if you have them already
320
+ # available as a Hash
321
+ #
322
+ # Example:
323
+ #
324
+ # xml_doc.add_attributes xml_doc.root, status: 'active', id: '123456'
325
+ # xml_doc.to_xml
326
+ # # =>
327
+ # <?xml version="1.0" encoding="utf-8"?>
328
+ # <patron id="123456" status="active">
329
+ # ...
330
+ # </patron>
331
+ #
332
+ # @param [Nokogiri::XML::Node] node node to add the attributes to
333
+ # @param [Hash] attributes a Hash with tag - value pairs for each attribute
334
+ # @return [Nokogiri::XML::Node] the node
335
+ def add_attributes(node, attributes)
336
+ XmlDocument.add_attributes node, attributes
337
+ end
338
+
339
+ # (see #add_attributes)
340
+ def self.add_attributes(node, attributes)
341
+
342
+ attributes.each do |name, value|
343
+ node.set_attribute name.to_s, value
344
+ end
345
+
346
+ node
347
+
348
+ end
349
+
350
+ # Add namespace information to a node
351
+ #
352
+ # Example:
353
+ #
354
+ # xml_doc.add_namespaces xml_doc.root, jkr: 'http://JKRowling.com', node_ns: 'jkr'
355
+ # xml_doc.to_xml
356
+ # # =>
357
+ # <?xml version="1.0" encoding="utf-8"?>
358
+ # <jkr:patron xmlns:jkr="http://JKRowling.com">
359
+ # ...
360
+ # </jkr:patron>
361
+ #
362
+ # xml_doc.add_namespaces xml_doc.root, nil => 'http://JKRowling.com'
363
+ # xml_doc.to_xml
364
+ # # =>
365
+ # <?xml version="1.0" encoding="utf-8"?>
366
+ # <patron xmlns="http://JKRowling.com">
367
+ # ...
368
+ # </patron>
369
+ #
370
+ # @param [Nokogiri::XML::Node] node the node where the namespace info should be added to
371
+ # @param [Hash] namespaces a Hash with prefix - URI pairs for each namespace definition that should be added. The
372
+ # special key +:node_ns+ is reserved for specifying the prefix for the node itself. To set the default
373
+ # namespace, use the prefix +nil+
374
+ def add_namespaces(node, namespaces)
375
+ XmlDocument.add_namespaces node, namespaces
376
+ end
377
+
378
+ # (see #add_namespaces)
379
+ def self.add_namespaces(node, namespaces)
380
+
381
+ node_ns = namespaces.delete :node_ns
382
+ default_ns = namespaces.delete nil
383
+
384
+ namespaces.each do |prefix, prefix_uri|
385
+ node.add_namespace prefix.to_s, prefix_uri
386
+ end
387
+
388
+ node.namespace_scopes.each do |ns|
389
+ node.namespace = ns if ns.prefix == node_ns.to_s
390
+ end if node_ns
391
+
392
+ node.default_namespace = default_ns if default_ns
393
+
394
+ node
395
+
396
+ end
397
+
398
+ # Search for nodes in the current document root.
399
+ #
400
+ # Example:
401
+ #
402
+ # nodes = xml_doc.xpath('//email')
403
+ # nodes.size # => 2
404
+ # nodes.map(&:content) # => ["harry.potter@hogwarts.edu", "hpotter@JKRowling.com"]
405
+ #
406
+ # @param [String] path XPath search string
407
+ # @return [{Nokogiri::XML::NodeSet}] set of nodes found
408
+ def xpath(path)
409
+ raise ArgumentError, 'XML document not valid.' if self.invalid?
410
+ @document.xpath(path.to_s)
411
+ end
412
+
413
+ # Check if the XML document contains certain element(s) anywhere in the XML document.
414
+ #
415
+ # Example:
416
+ #
417
+ # xml_doc.has_element? 'barcode[@library="Hogwarts Library"]' # => true
418
+ #
419
+ # @param [String] element_name name of the element(s) to search
420
+ # @return [Integer] number of elements found
421
+ def has_element?(element_name)
422
+ list = xpath("//#{element_name}")
423
+ list.nil? ? 0 : list.size
424
+ end
425
+
426
+ # Return the content of the first element found.
427
+ #
428
+ # Example:
429
+ #
430
+ # xml_doc.value('//email') # => "harry.potter@hogwarts.edu"
431
+ #
432
+ # @param [String] path the name or XPath term to search the node(s)
433
+ # @param [Node] parent parent node; document if nil
434
+ # @return [String] content or nil if not found
435
+ def value(path, parent = nil)
436
+ parent ||= document
437
+ parent.xpath(path).first.content rescue nil
438
+ end
439
+
440
+ # Return the content of the first element found.
441
+ #
442
+ # Example:
443
+ #
444
+ # xml_doc['email'] # => "harry.potter@hogwarts.edu"
445
+ #
446
+ # @param [String] path the name or XPath term to search the node(s)
447
+ # @return [String] content or nil if not found
448
+ def [](path)
449
+ xpath(path).first.content rescue nil
450
+ end
451
+
452
+ # Return the content of all elements found.
453
+ # Example:
454
+ #
455
+ # xml_doc.values('//email') # => [ "harry.potter@hogwarts.edu", "hpotter@JKRowling.com" ]
456
+ #
457
+ # @param (see #value)
458
+ # @return [Array<String>] content
459
+ def values(path)
460
+ xpath(path).map &:content
461
+ end
462
+
463
+ # Return the content of the first element in the set of nodes.
464
+ #
465
+ # Example:
466
+ #
467
+ # ::Libis::Tools::XmlDocument.get_content(xml_doc.xpath('//email')) # => "harry.potter@hogwarts.edu"
468
+ #
469
+ # @param [{Nokogiri::XML::NodeSet}] nodelist set of nodes to get content from
470
+ # @return [String] content of the first node; always returns at least an empty string
471
+ def self.get_content(nodelist)
472
+ (nodelist.first && nodelist.first.content) || ''
473
+ end
474
+
475
+ # Find a node and set its content.
476
+ #
477
+ # Example:
478
+ #
479
+ # xml_doc['//access_level'] = 'postgraduate'
480
+ # p xml_doc.to_xml
481
+ # # =>
482
+ # <?xml version="1.0" encoding="utf-8"?>
483
+ # <patron>
484
+ # ...
485
+ # <access_level>postgraduate</access_level>
486
+ # ...
487
+ # </patron>
488
+ #
489
+ # @param (see #value)
490
+ # @param [String] value the content
491
+ # @return [String] the value
492
+ def []=(path, value)
493
+ begin
494
+ nodes = xpath(path)
495
+ nodes.first.content = value
496
+ rescue
497
+ # ignored
498
+ end
499
+ end
500
+
501
+ # Node access by method name.
502
+ #
503
+ # Nodes can be accessed through a method with signature the tag name of the node. There are several ways to use
504
+ # this shorthand method:
505
+ #
506
+ # * without arguments it simply returns the first node found
507
+ # * with one argument it retrieves the node's attribute
508
+ # * with one argument and '=' sign it sets the content of the node
509
+ # * with two arguments it sets the value of the node's attribute
510
+ # * with a code block it implements the build pattern
511
+ #
512
+ #
513
+ # Examples:
514
+ #
515
+ # xml_doc.email
516
+ # # => "harry.potter@hogwarts.edu"
517
+ # p xml_doc.barcode 'library'
518
+ # # => "Hogwarts Library"
519
+ # xml_doc.access_level = 'postgraduate'
520
+ # xml_doc.barcode 'library', 'Hogwarts Dumbledore Library'
521
+ # xml_doc.dates do |dates|
522
+ # dates.birth_date '1980-07-31'
523
+ # dates.member_since '1991-09-01'
524
+ # end
525
+ # p xml_doc.to_xml
526
+ # # => <patron>
527
+ # ...
528
+ # <barcode library='Hogwarts Dumbledore Library'>1234567890</barcode>
529
+ # <access_level>postgraduate</access_level>
530
+ # ...
531
+ # <dates>
532
+ # <birth_date>1980-07-31</birth_date>
533
+ # <member_since>1991-09-01</member_since>
534
+ # </dates>
535
+ # </patron>
536
+ #
537
+ #
538
+ def method_missing(method, *args, &block)
539
+ super unless method.to_s =~ /^([a-z_][a-z_0-9]*)(!|=)?$/i
540
+ node = get_node($1)
541
+ node = add_node($1) if node.nil? || $2 == '!'
542
+ case args.size
543
+ when 0
544
+ if block_given?
545
+ build(node, &block)
546
+ end
547
+ when 1
548
+ if $2.blank?
549
+ return node[args.first.to_s]
550
+ else
551
+ node.content = args.first.to_s
552
+ end
553
+ when 2
554
+ node[args.first.to_s] = args[1].to_s
555
+ return node[args.first.to_s]
556
+ else
557
+ raise ArgumentError, 'Too many arguments.'
558
+ end
559
+ node
560
+ end
561
+
562
+ # Get the first node matching the tag. The node will be seached with XPath search term = "//#!{tag}".
563
+ #
564
+ # @param [String] tag XML tag to look for; XPath syntax is allowed
565
+ # @param [Node] parent
566
+ def get_node(tag, parent = nil)
567
+ get_nodes(tag, parent).first
568
+ end
569
+
570
+ # Get all the nodes matching the tag. The node will be seached with XPath search term = "//#!{tag}".
571
+ #
572
+ # @param [String] tag XML tag to look for; XPath syntax is allowed
573
+ # @param [Node] parent
574
+ def get_nodes(tag, parent = nil)
575
+ parent ||= root
576
+ term = "#{tag.to_s =~ /^\// ? '' : '//'}#{tag.to_s}"
577
+ parent.xpath(term)
578
+ end
579
+
580
+ end
581
+
582
+ end
583
+ end