udat 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/LICENSE +23 -0
  2. data/lib/udat.rb +803 -0
  3. metadata +51 -0
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ # Copyright (c) 2007 FlexiGuided GmbH, Berlin
2
+ #
3
+ # Author: Jan Behrens
4
+ #
5
+ # Website: http://www.flexiguided.de/publications.flexirecord.en.html
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
@@ -0,0 +1,803 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'monitor'
4
+
5
+
6
+ # Copyright (c) 2007 FlexiGuided GmbH, Berlin
7
+ #
8
+ # Author: Jan Behrens
9
+ #
10
+ # Website: http://www.flexiguided.de/publications.udat.en.html
11
+ #
12
+ # -----
13
+ #
14
+ # Abstract class of an UDAT object. UDAT objects basically consist of a tag
15
+ # and the content. The tag can be a String or be nil. The content is either
16
+ # a scalar value stored as a String, or an ordered/unordered collection of
17
+ # values (where each value can optionally have a key associated with it).
18
+ # Keys and values of collections are always UDAT objects too.
19
+ #
20
+ # UDAT objects can most easily constructed from other ruby objects by using
21
+ # the Object#to_udat method.
22
+ #
23
+ # By calling the method Udat#encode, the UDAT object is encoded in a both
24
+ # easily human readable and easily machine readable format. The output can
25
+ # be later parsed by String#parse_udat.
26
+ class Udat
27
+
28
+ include MonitorMixin
29
+
30
+ # A string containing an UDAT example document.
31
+ ExampleDocument = <<-'END_OF_EXAMPLE'
32
+ This file contains several UDAT objects (each of them enclosed by
33
+ square brackets), serving as an example for the UDAT format:
34
+
35
+
36
+ Scalar values:
37
+
38
+ [Hello World!]
39
+
40
+ [UTF-8|Hello World!] This object has a tag "UTF-8".
41
+ Intepretation of tags is task of the
42
+ application, there are no standard tags
43
+ defined.
44
+
45
+ [23]
46
+
47
+ [integer|23]
48
+
49
+ [rational|2/3]
50
+
51
+
52
+ Arrays (or sets):
53
+
54
+ [ [1] [2] [3] ]
55
+
56
+ [ [1] [2] Comments are allowed here! (and alomost everywhere) [3] ]
57
+
58
+ [ [person| <firstname>[Max] <lastname>[Mustermann] ]
59
+ [person| <firstname>[Martin] <lastname>[html|M&uuml;ller] ]
60
+ [group| <name>[Sample group] <members> [
61
+ [person| <firstname>[Max] <lastname>[Mustermann] ]
62
+ [person| <firstname>[Martin] <lastname>[html|M&uuml;ller] ]
63
+ ] ] ]
64
+
65
+ References like in YAML with &id and *id
66
+ are not directly supported by UDAT.
67
+
68
+
69
+ Ordered or unordered maps:
70
+
71
+ [ <&> [html|&amp;]
72
+ <"> [html|&quot;]
73
+ <\<> [html|&lt;]
74
+ <\>> [html|&gt;] ]
75
+
76
+ [ <color combination|[red][blue]> [nice]
77
+ <color combination|[red][magenta]> [ugly] ]
78
+
79
+ [ < <red>[on] <yellow>[off] <green>[off] > [stop]
80
+ < <red>[on] <yellow>[on] <green>[off] > [attention]
81
+ < <red>[off] <yellow>[off] <green>[on] > [go]
82
+ < <red>[off] <yellow>[on] <green>[off] > [prepare to stop] ]
83
+
84
+
85
+ Sample configuration file:
86
+
87
+ [sample config v1.0|
88
+ <locale> [
89
+ <language> [de]
90
+ <timezone> [CET]
91
+ ]
92
+ <logging> [
93
+ <verbosity> [high]
94
+ <destination> [file|/var/log/sample.log]
95
+ ]
96
+ <network> [
97
+ <max connections> [256]
98
+ <reverse lookups> [yes]
99
+ ]
100
+ <access control> [
101
+ <allowed> [ [user|martin] [user|max] [group|admins] ]
102
+ ]
103
+ ]
104
+
105
+
106
+ END_OF_EXAMPLE
107
+
108
+ # Error raised by UdatCollection#fetch,
109
+ # when a fetched value doesn't have the expected tag (i.e. type).
110
+ class UdatTagMismatch < StandardError; end
111
+ # Error raised by UdatCollection#fetch,
112
+ # when a fetched value is a collection instead of a scalar or vice versa.
113
+ class UdatTypeMismatch < StandardError; end
114
+ # UDAT parsing error
115
+ class UdatParseError < StandardError; end
116
+
117
+ private_class_method :new
118
+ # Creates a new Udat object with a given tag (which may be nil).
119
+ # This method is private,
120
+ # Udat#initialize is only called through supercalls.
121
+ def initialize(tag)
122
+ super()
123
+ self.tag = tag
124
+ end
125
+
126
+ # Returns an escaped version of a string, where backslashes are preceding
127
+ # certain reserved characters.
128
+ def escape_string(string)
129
+ string.to_s.gsub /([<>\[\]|\\])/, "\\\\\\1"
130
+ end
131
+ private :escape_string
132
+
133
+ # Tag (i.e. type of the content).
134
+ attr_reader :tag
135
+ # Sets the tag (i.e. type of the content).
136
+ def tag=(tag)
137
+ synchronize do
138
+ @tag = tag ? tag.to_s : nil
139
+ end
140
+ return tag
141
+ end
142
+
143
+ # Returns true, if the UDAT object represents a scalar value.
144
+ def scalar?
145
+ kind_of? UdatScalar
146
+ end
147
+ # Returns true, if the UDAT object represents a collection.
148
+ def collection?
149
+ kind_of? UdatCollection
150
+ end
151
+
152
+ # Returns the data (including it's tag) encoded in the UDAT format.
153
+ #
154
+ # Note: The UDAT format doesn't contain information, whether contained
155
+ # collections are ordered or unordered. This information is lost during
156
+ # the encoding process, and has to be restored in an application
157
+ # specific way, if neccessary.
158
+ def encode
159
+ synchronize do
160
+ if tag
161
+ return "#{escape_string tag}|#{encoded_content}"
162
+ else
163
+ return encoded_content
164
+ end
165
+ end
166
+ end
167
+ # Here the method does the same as Udat#encode, but this method is
168
+ # overwritten in UdatScalar!
169
+ def to_s
170
+ encode
171
+ end
172
+ # Does the same as Udat#encode, but encloses the results in curly
173
+ # brackets and preceds them with the string "udat".
174
+ def inspect
175
+ "udat{#{self.encode}}"
176
+ end
177
+
178
+ # Returns a hash key used by ruby's Hash'es.
179
+ def hash
180
+ to_s.hash
181
+ end
182
+ # Returns true, if class, tag and content are matching another object.
183
+ def eql?(other)
184
+ self.class == other.class and
185
+ self.tag == other.tag and
186
+ self.to_s == other.to_s
187
+ end
188
+ # Same as Udat#eql?.
189
+ def ==(other)
190
+ self.eql? other
191
+ end
192
+
193
+ # Casts a value to an Udat object. If two arguments are supplied,
194
+ # the first argument is a tag, while the second is the content.
195
+ # If just one argument is supplied, then the tag is assumed to be nil,
196
+ # and the argument given is the content.
197
+ # Interpretation of the content depends on the type: If the content is
198
+ # an Array (responds to 'to_ary') an ordered collection will be created,
199
+ # if the content is a Hash (responds to 'to_hash'), an unordered
200
+ # collection will be created. Otherwise the content will be casted to a
201
+ # String with 'to_s' to be stored as a scalar.
202
+ # If you pass an Array, each element of that array may be an Array
203
+ # of size 2, in which case the 2 entries are used as key and value.
204
+ # If an element of the Array is not an Array, it will be used as value
205
+ # without an associated key.
206
+ # This method is called by Object#to_udat.
207
+ def self.construct(*args)
208
+ if args.length == 1
209
+ tag = nil
210
+ content = args[0]
211
+ elsif args.length == 2
212
+ tag = args[0]
213
+ content = args[1]
214
+ else
215
+ raise ArgumentError, "Wrong number of arguments supplied."
216
+ end
217
+ if content.kind_of? Udat
218
+ if tag
219
+ content = content.dup
220
+ content.tag = tag
221
+ end
222
+ return content
223
+ elsif content.respond_to? :to_ary
224
+ return UdatCollection.new(tag, content.to_ary)
225
+ elsif content.respond_to? :to_hash
226
+ return UdatCollection.new(tag, content.to_hash.to_a).unordered!
227
+ else
228
+ return UdatScalar.new(tag, content)
229
+ end
230
+ end
231
+
232
+ # Internal parsing function.
233
+ def self.parse_intern(input, start_pos = 0)
234
+ string = ""
235
+ tag = nil
236
+ key = nil
237
+ collection = nil
238
+ pos = start_pos
239
+ while pos < input.length
240
+ char = input[pos, 1]
241
+ case char
242
+ when "\\"
243
+ pos += 1
244
+ char = input[pos, 1]
245
+ if char.empty?
246
+ raise UdatParseError, "Backslash at end of input."
247
+ end
248
+ if char =~ /([<>\[\]|\\])/
249
+ string << char
250
+ elsif char == "\n"
251
+ elsif char == "\r"
252
+ next_char = input[pos + 1, 1]
253
+ pos += 1 if next_char == "\n"
254
+ else
255
+ raise UdatParseError, "Unknown escape sequence found."
256
+ end
257
+ pos += 1
258
+ when "|"
259
+ if tag
260
+ raise UdatParseError, "Multiple occurrences of pipe symbol."
261
+ end
262
+ tag = string
263
+ string = ""
264
+ pos += 1
265
+ when "<"
266
+ if key
267
+ raise UdatParseError, "Unexpected opening angle bracket."
268
+ end
269
+ key, pos = parse_intern(input, pos + 1)
270
+ unless input[pos, 1] == ">"
271
+ raise ArgumentError, "Closing angle bracket not found."
272
+ end
273
+ pos += 1
274
+ when ">"
275
+ break
276
+ when "["
277
+ value, pos = parse_intern(input, pos + 1)
278
+ unless input[pos, 1] == "]"
279
+ raise UdatParseError, "Closing square bracket not found."
280
+ end
281
+ collection ||= UdatCollection.new(tag, [])
282
+ if key
283
+ collection.append_pair(key, value)
284
+ key = nil
285
+ else
286
+ collection.append_value(value)
287
+ end
288
+ pos += 1
289
+ when "]"
290
+ break
291
+ else
292
+ string << char
293
+ pos += 1
294
+ end
295
+ end
296
+ raise UdatParseError, "Key without value." if key
297
+ return (collection ? collection : string.to_udat(tag)), pos
298
+ end
299
+ private_class_method :parse_intern
300
+
301
+ # Parses a given string and returns a structure of Udat objects.
302
+ #
303
+ # Note: When parsing UDAT data, no information is gained, whether
304
+ # collections are ordered or unordered. After parsing all collections
305
+ # will be marked as unordered, until changed.
306
+ def self.parse(input)
307
+ input = input.to_s
308
+ result, pos = parse_intern(input)
309
+ if pos < input.length
310
+ raise UdatParseError, "Closing bracket without opening bracket."
311
+ end
312
+ return result
313
+ end
314
+
315
+ end
316
+
317
+
318
+ # Class of UDAT objects holding an ordered or unordered collection of
319
+ # values (where each value can optionally have a key associated with it).
320
+ # Keys and values of collections are always UDAT objects too.
321
+ # UdatCollection's can be interpreted as maps, arrays, sets or any other
322
+ # non-scalar data structure.
323
+ #
324
+ # Note: Keys and values are always implicitly casted by all instance
325
+ # methods of this class to Udat objects using Object#to_udat.
326
+ class UdatCollection < Udat
327
+
328
+ public_class_method :new
329
+ # Creates a new Udat object with a given tag (which may be nil) and a
330
+ # given content, represented by a nested Array structure. See the source
331
+ # code for details. It is not recommended to use this method. Use
332
+ # Object#to_udat instead.
333
+ def initialize(tag, content)
334
+ super tag
335
+ @ordered = true
336
+ clear
337
+ unless content.nil?
338
+ if content.respond_to? :to_ary
339
+ content = content.to_ary
340
+ else
341
+ content = [content]
342
+ end
343
+ content.each { |entry| self << entry }
344
+ end
345
+ end
346
+
347
+ # Deletes the internal index hashes.
348
+ def index_void!
349
+ synchronize do
350
+ @key_to_value = nil
351
+ @value_to_key = nil
352
+ end
353
+ return self
354
+ end
355
+ # Checks, if the internal index hashes are existent
356
+ # (and therefore up to date).
357
+ def index_void?
358
+ synchronize do
359
+ return @key_to_value.nil?
360
+ end
361
+ end
362
+ # Creates index hashes, if they are not existent.
363
+ def require_index
364
+ synchronize do
365
+ if index_void?
366
+ @key_to_value = {}
367
+ @value_to_key = {}
368
+ @entries.each do |key, value|
369
+ unless key.nil?
370
+ @key_to_value[key] = value unless @key_to_value.has_key? key
371
+ @value_to_key[value] = key unless @value_to_key.has_key? value
372
+ end
373
+ end
374
+ end
375
+ end
376
+ return self
377
+ end
378
+ private :index_void!, :index_void?, :require_index
379
+
380
+ # Empties the collection.
381
+ def clear
382
+ @entries = []
383
+ @key_to_value = {}
384
+ @value_to_key = {}
385
+ return self
386
+ end
387
+ # Deletes all entries with a given key.
388
+ def delete_key(key)
389
+ key = key.to_udat
390
+ synchronize do
391
+ @entries.delete_if { |e_key, e_value| e_key == key }
392
+ index_void!
393
+ end
394
+ return self
395
+ end
396
+ # Deletes all entries with a given value.
397
+ def delete_value(value)
398
+ value = value.to_udat
399
+ synchronize do
400
+ @entries.delete_if { |e_key, e_value| e_value == value }
401
+ index_void!
402
+ end
403
+ return self
404
+ end
405
+
406
+ # Appends a new key with a new value.
407
+ def append_pair(key, value)
408
+ key = key.to_udat
409
+ value = value.to_udat
410
+ synchronize do
411
+ @entries << [key, value]
412
+ unless index_void?
413
+ @key_to_value[key] = value unless @key_to_value.has_key? key
414
+ @value_to_key[value] = key unless @value_to_key.has_key? value
415
+ end
416
+ end
417
+ return self
418
+ end
419
+ # Appends a new value.
420
+ def append_value(value)
421
+ value = value.to_udat
422
+ synchronize do
423
+ @entries << [nil, value]
424
+ end
425
+ return self
426
+ end
427
+ # Deletes all key/value pairs where the given key matches,
428
+ # and appends a new key/value pair.
429
+ def set_value_for_key(key, value)
430
+ delete_key(key)
431
+ append_pair(key, value)
432
+ return self
433
+ end
434
+ # Deletes all values or key/value pairs where the given value matches,
435
+ # and appends a new key/value pair.
436
+ def set_key_for_value(key, value)
437
+ delete_value(value)
438
+ append_pair(key, value)
439
+ return self
440
+ end
441
+
442
+ # Behaves differently depending on the type of the argument. If the
443
+ # argument is an Array (responds to 'to_ary') with 2 elements, a new
444
+ # key/value pair is added, with the key being the first element of the
445
+ # array, and the value being the second argument of the array. Arrays
446
+ # with any other number of elements cause an error. If the argument is
447
+ # not an array, then it is added as a value without a key.
448
+ def <<(value)
449
+ if value.respond_to? :to_ary
450
+ value = value.to_ary
451
+ unless value.length == 2
452
+ raise ArgumentError,
453
+ "#{value.length} array elements found, while 2 were expected."
454
+ end
455
+ return append_pair(value[0], value[1])
456
+ else
457
+ return append_value(value)
458
+ end
459
+ end
460
+ # Same as UdatCollection#set_value_for_key.
461
+ def []=(key, value)
462
+ set_value_for_key(key, value)
463
+ end
464
+
465
+ # Returns the key at a numeric index, or nil, if the index is out of
466
+ # bounds or at the given index there is only a value without key.
467
+ def key_by_index(index)
468
+ index = index.to_int
469
+ synchronize do
470
+ entry = @entries[index]
471
+ return entry ? entry[0] : nil
472
+ end
473
+ end
474
+ # Returns the value at a numeric index, or nil, if the index is out of
475
+ # bounds.
476
+ def value_by_index(index)
477
+ index = index.to_int
478
+ synchronize do
479
+ entry = @entries[index]
480
+ return entry ? entry[1] : nil
481
+ end
482
+ end
483
+ # Returns the first value having a given key.
484
+ def value_by_key(key)
485
+ synchronize do
486
+ require_index
487
+ return @key_to_value[key.to_udat]
488
+ end
489
+ end
490
+ # Returns the first key having a given value.
491
+ def key_by_value(value)
492
+ synchronize do
493
+ require_index
494
+ @value_to_key[value.to_udat]
495
+ end
496
+ end
497
+
498
+ # Behaves differently depending on the type of the argument.
499
+ # If the argument is an Integer the method behaves same as
500
+ # UdatCollection#value_by_index.
501
+ # Otherwise the method behaves same as UdatCollection#value_by_key.
502
+ def [](key)
503
+ if key.kind_of? Integer
504
+ return value_by_index(key)
505
+ else
506
+ return value_by_key(key)
507
+ end
508
+ end
509
+ # Same as UdatCollection#[], but raises an error, if no value was found.
510
+ # If an additional second argument is passed, an error will also be
511
+ # raised if the tag (i.e. type) of the value is not equal to the second
512
+ # argument.
513
+ def fetch(*args)
514
+ if args.length == 1
515
+ value = self[args[0]]
516
+ unless value
517
+ raise IndexError, "Value for the given key or index not found."
518
+ end
519
+ return value
520
+ elsif args.length == 2
521
+ value = fetch(args[0])
522
+ unless value.tag == args[1]
523
+ raise UdatTagMismatch, "UDAT tag mismatch."
524
+ end
525
+ return value
526
+ else
527
+ raise ArgumentError, "Wrong number of arguments supplied."
528
+ end
529
+ end
530
+ # Same as UdatCollection#fetch, but raises an error, if the value is
531
+ # not an UdatCollection.
532
+ def fetch_collection(*args)
533
+ value = fetch(*args)
534
+ if value.scalar?
535
+ raise UdatTypeMismatch,
536
+ "Scalar value found, where a collection was expected."
537
+ end
538
+ return value
539
+ end
540
+ # Same as UdatCollection#fetch, but raises an error, if the value is
541
+ # not an UdatScalar.
542
+ def fetch_scalar(*args)
543
+ value = fetch(*args)
544
+ if value.collection?
545
+ raise UdatTypeMismatch,
546
+ "Collection found, where a scalar value was expected."
547
+ end
548
+ return value
549
+ end
550
+ # Returns the first value of the collection, or nil if empty.
551
+ def first
552
+ self[0]
553
+ end
554
+ # Returns the last value of the collection, or nil if empty.
555
+ def last
556
+ self[-1]
557
+ end
558
+
559
+ # Returns the number of values in the collection.
560
+ def length
561
+ synchronize do
562
+ @entries.length
563
+ end
564
+ end
565
+ alias size length
566
+ # Returns true, if the collection is empty.
567
+ def empty?
568
+ length == 0
569
+ end
570
+ # Calls a given block for each numeric index.
571
+ def each_index
572
+ synchronize do
573
+ (0...length).each { |i| yield i }
574
+ end
575
+ return self
576
+ end
577
+
578
+ # Returns an Array containing the key/value pairs each as an Array of
579
+ # size 2. If there is no key, the first element of the sub Array is nil.
580
+ def key_value_pairs
581
+ synchronize do
582
+ return @entries.collect { |entry| entry.dup }
583
+ end
584
+ end
585
+ # Returns an Array containing all keys of the collection.
586
+ def keys
587
+ keys = nil
588
+ synchronize do
589
+ keys = @entries.collect { |key, value| key }
590
+ end
591
+ keys.compact!
592
+ return keys
593
+ end
594
+ # Returns an Array containing all values of the collection.
595
+ def values
596
+ synchronize do
597
+ return @entries.collect { |key, value| value }
598
+ end
599
+ end
600
+
601
+ # Returns a hash, where each key is mapped to the respective value.
602
+ def to_hash
603
+ hash = {}
604
+ synchronize do
605
+ @entries.each do |key, value|
606
+ next if key.nil?
607
+ hash[key] = value unless hash.has_key? key
608
+ end
609
+ end
610
+ return hash
611
+ end
612
+ # Same as UdatCollection#values.
613
+ def to_ary
614
+ values
615
+ end
616
+ alias to_a values
617
+
618
+ # Appends the values (and corresponding keys) of another UdatCollection.
619
+ def concat(other)
620
+ synchronize do
621
+ other.key_value_pairs.each do |key, value|
622
+ if key.nil?
623
+ append_value(value)
624
+ else
625
+ append_pair(key, value)
626
+ end
627
+ end
628
+ end
629
+ return self
630
+ end
631
+ # Replaces the values (and corresponding keys) with the values/keys of
632
+ # another UdatCollection.
633
+ def replace(other)
634
+ synchronize do
635
+ clear
636
+ concat(other)
637
+ end
638
+ return self
639
+ end
640
+
641
+ # Returns the encoded form of the content as a string.
642
+ # This method is used by Udat#encode, which returns the complete encoding
643
+ # of the data (including the tag).
644
+ def encoded_content
645
+ synchronize do
646
+ return (
647
+ @entries.collect do |key, value|
648
+ if key
649
+ "<#{key.encode}>[#{value.encode}]"
650
+ else
651
+ "[#{value.encode}]"
652
+ end
653
+ end.join
654
+ )
655
+ end
656
+ end
657
+
658
+ # Same as UdatCollection#ordered?.
659
+ def ordered
660
+ synchronize do
661
+ return @ordered
662
+ end
663
+ end
664
+ # Returns false, if the order of the contents of this collection is to be
665
+ # ignored for comparisons.
666
+ def ordered?
667
+ self.ordered
668
+ end
669
+ # Returns true, if the order of the contents of this collection is to be
670
+ # ignored for comparisons.
671
+ def unordered?
672
+ not self.ordered
673
+ end
674
+ # If set to false, the order of the contents of this collection will be
675
+ # ignored for comparisons.
676
+ def ordered=(ordered)
677
+ synchronize do
678
+ index_void!
679
+ if ordered == false
680
+ @ordered = false
681
+ else
682
+ @ordered = true
683
+ end
684
+ end
685
+ end
686
+ # Same as UdatCollection#ordered = false, but returns self.
687
+ def unordered!
688
+ self.ordered = false
689
+ return self
690
+ end
691
+ # Same as UdatCollection#ordered = true, but returns self.
692
+ def ordered!
693
+ self.ordered = true
694
+ return self
695
+ end
696
+
697
+ # Same as Udat#inspect, but in case of unordered collections it prepends
698
+ # "udat-unordered" instead of just "udat".
699
+ def inspect
700
+ if ordered?
701
+ return super
702
+ else
703
+ "udat-unordered{#{self.encode}}"
704
+ end
705
+ end
706
+
707
+ # Returns a hash key used by ruby's Hash'es.
708
+ def hash
709
+ hash = 0
710
+ @entries.each do |key, value|
711
+ hash += key.hash
712
+ hash += value.hash
713
+ end
714
+ return hash
715
+ end
716
+ # Returns true, if class, tag and content are matching another object.
717
+ # The order of the content is ignored, if this or the other object has
718
+ # the 'ordered' attribute set to false.
719
+ def eql?(other)
720
+ if self.class == other.class and self.tag == other.tag
721
+ if self.ordered? and other.ordered?
722
+ return self.to_s == other.to_s
723
+ else
724
+ own_pairs = self.key_value_pairs
725
+ other_pairs = other.key_value_pairs
726
+ own_pairs.each do |own_pair|
727
+ catch :found do
728
+ other_pairs.each_index do |index|
729
+ if own_pair.eql? other_pairs[index]
730
+ other_pairs.delete_at index
731
+ throw :found
732
+ end
733
+ end
734
+ return false
735
+ end
736
+ end
737
+ return true
738
+ end
739
+ else
740
+ return false
741
+ end
742
+ end
743
+
744
+ end
745
+
746
+
747
+ # Class of UDAT objects holding scalar values (stored as a String).
748
+ class UdatScalar < Udat
749
+
750
+ public_class_method :new
751
+ # Creates a new Udat object with a given tag (which may be nil) and a
752
+ # given content, which is transformed to a string (via 'to_s').
753
+ # It is not recommended to use this method. Use Object#to_udat instead.
754
+ def initialize(tag, content)
755
+ super tag
756
+ self.content = content
757
+ end
758
+
759
+ # Content of the scalar (a String).
760
+ attr_reader :content
761
+ def content=(content)
762
+ @content = content.to_s
763
+ end
764
+
765
+ # Same as UdatScalar#content.
766
+ def to_s
767
+ content
768
+ end
769
+ # Returns the content casted to an Integer.
770
+ def to_i
771
+ content.to_i
772
+ end
773
+
774
+ # Returns true, if the (String representation of the) content is empty.
775
+ def empty?
776
+ self.to_s.empty?
777
+ end
778
+
779
+ # Returns the encoded form of the content as a string.
780
+ # In this case this is simply an escaped version of the String.
781
+ # This method is used by Udat#encode, which returns the complete encoding
782
+ # of the data (including the tag).
783
+ def encoded_content
784
+ escape_string(self.to_s)
785
+ end
786
+
787
+ end
788
+
789
+
790
+ class Object
791
+ # Calls Udat.construct(tag, self).
792
+ def to_udat(tag = nil)
793
+ Udat.construct(tag, self)
794
+ end
795
+ end
796
+
797
+ class String
798
+ # Calls Udat.parse(self).
799
+ def parse_udat
800
+ Udat.parse(self)
801
+ end
802
+ end
803
+
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: udat
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.0
7
+ date: 2007-05-22 00:00:00 +00:00
8
+ summary: Parser and generator for UDAT documents, a generic data format similar to XML or YAML.
9
+ require_paths:
10
+ - lib/
11
+ email: jan.behrens@flexiguided.de
12
+ homepage: http://www.flexiguided.de/publications.udat.en.html
13
+ rubyforge_project:
14
+ description:
15
+ autorequire: udat
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Jan Behrens
31
+ files:
32
+ - ./LICENSE
33
+ - lib/udat.rb
34
+ test_files: []
35
+
36
+ rdoc_options:
37
+ - --title
38
+ - UDAT Documentation
39
+ - --main
40
+ - Udat
41
+ - --line-numbers
42
+ extra_rdoc_files: []
43
+
44
+ executables: []
45
+
46
+ extensions: []
47
+
48
+ requirements: []
49
+
50
+ dependencies: []
51
+