sdl4r 0.9.8 → 0.9.9

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 (48) hide show
  1. data/CHANGELOG +14 -1
  2. data/README +4 -4
  3. data/Rakefile +15 -12
  4. data/TODO +11 -4
  5. data/lib/sdl4r/parser.rb +25 -33
  6. data/lib/sdl4r/parser/token.rb +1 -1
  7. data/lib/sdl4r/sdl4r.rb +50 -22
  8. data/lib/sdl4r/sdl_time_span.rb +2 -2
  9. data/lib/sdl4r/serializer.rb +152 -0
  10. data/lib/sdl4r/tag.rb +15 -1
  11. data/test/sdl4r/parser_test.rb +5 -0
  12. data/test/sdl4r/serializer_test.rb +78 -0
  13. data/test/sdl4r/tag_test.rb +20 -1
  14. metadata +52 -81
  15. data/doc/classes/SDL4R.html +0 -408
  16. data/doc/classes/SDL4R/Parser.html +0 -190
  17. data/doc/classes/SDL4R/ParserTest.html +0 -385
  18. data/doc/classes/SDL4R/SDL4RTest.html +0 -532
  19. data/doc/classes/SDL4R/SDLTest.html +0 -77
  20. data/doc/classes/SDL4R/SdlBinary.html +0 -188
  21. data/doc/classes/SDL4R/SdlParseError.html +0 -110
  22. data/doc/classes/SDL4R/SdlTimeSpan.html +0 -637
  23. data/doc/classes/SDL4R/Tag.html +0 -1249
  24. data/doc/classes/SDL4R/TagTest.html +0 -292
  25. data/doc/created.rid +0 -1
  26. data/doc/files/CHANGELOG.html +0 -200
  27. data/doc/files/LICENSE.html +0 -497
  28. data/doc/files/README.html +0 -406
  29. data/doc/files/lib/sdl4r/parser/reader_rb.html +0 -54
  30. data/doc/files/lib/sdl4r/parser/time_span_with_zone_rb.html +0 -54
  31. data/doc/files/lib/sdl4r/parser/token_rb.html +0 -54
  32. data/doc/files/lib/sdl4r/parser/tokenizer_rb.html +0 -54
  33. data/doc/files/lib/sdl4r/parser_rb.html +0 -62
  34. data/doc/files/lib/sdl4r/sdl4r_rb.html +0 -64
  35. data/doc/files/lib/sdl4r/sdl_binary_rb.html +0 -54
  36. data/doc/files/lib/sdl4r/sdl_parse_error_rb.html +0 -54
  37. data/doc/files/lib/sdl4r/sdl_time_span_rb.html +0 -54
  38. data/doc/files/lib/sdl4r/tag_rb.html +0 -64
  39. data/doc/files/lib/sdl4r_rb.html +0 -54
  40. data/doc/files/test/sdl4r/parser_test_rb.html +0 -64
  41. data/doc/files/test/sdl4r/sdl4r_test_rb.html +0 -67
  42. data/doc/files/test/sdl4r/sdl_test_rb.html +0 -64
  43. data/doc/files/test/sdl4r/tag_test_rb.html +0 -64
  44. data/doc/fr_class_index.html +0 -19
  45. data/doc/fr_file_index.html +0 -37
  46. data/doc/fr_method_index.html +0 -4711
  47. data/doc/index.html +0 -15
  48. data/doc/rdoc-style.css +0 -328
data/CHANGELOG CHANGED
@@ -1,4 +1,17 @@
1
- == v0.9.9 (建設中)
1
+ == v0.9.10 (建設中)
2
+ == v0.9.9 (19-11-2010)
3
+
4
+ === Major changes:
5
+
6
+ * SDL4R#to_attribute_map renamed to SDL4R#to_attribute_hash (as the doc stated previously).
7
+ * Symbols are now converted to Strings when used as SDL values. It is therefore possible to write:
8
+
9
+ tag.set_attribute("type", :square) #tag.attribute("type") => "square"
10
+
11
+ === Minor changes:
12
+
13
+ * New methods: Tag#has_attributes? and Tag#has_values?
14
+
2
15
  == v0.9.8 (16-sep-2010)
3
16
 
4
17
  === Major changes:
data/README CHANGED
@@ -71,7 +71,7 @@ attributes, and (if it has a body) child tags. In the example above, the
71
71
  attributes, and no bodies.
72
72
 
73
73
  SDL is often used for simple key-value mappings. To simplify things Tag
74
- has the methods getValue and setValue which operate on the first element in
74
+ has the methods value and value= which operate on the first element in
75
75
  the values list. Also notice SDL understands types which are determined
76
76
  using type inference.
77
77
 
@@ -112,7 +112,7 @@ demonstrates the use of anonymous tags.
112
112
 
113
113
  # If we have a handle on the "greetings" tag we can access the
114
114
  # anonymous child tag by calling
115
- # Tag child1 = greetingTag.getChild("content");
115
+ # Tag child1 = greetingTag.child("content");
116
116
 
117
117
  == String literals
118
118
 
@@ -299,7 +299,7 @@ An example SDL file:
299
299
 
300
300
  # To retrieve the files as a list of strings
301
301
  #
302
- # List files = tag.getChild("files").getChildrenValues("content");
302
+ # List files = tag.child("files").children_values("content");
303
303
  #
304
304
  # We us the name "content" because the files tag has two children, each of
305
305
  # which are anonymous tags (values with no name.) These tags are assigned
@@ -312,7 +312,7 @@ An example SDL file:
312
312
 
313
313
  # To retrieve the values from the matrix (as a list of lists)
314
314
  #
315
- # List rows = tag.getChild("matrix").getChildrenValues("content");
315
+ # List rows = tag.child("matrix").children_values("content");
316
316
 
317
317
 
318
318
  Example of getting the "location" attribute from the "daughter" tag
data/Rakefile CHANGED
@@ -17,13 +17,13 @@ spec = Gem::Specification.new do |s|
17
17
  s.platform = Gem::Platform::RUBY
18
18
  s.summary = "Simple Declarative Language for Ruby library"
19
19
  s.name = 'sdl4r'
20
- s.version = '0.9.8'
20
+ s.version = '0.9.9'
21
21
  s.requirements << 'none'
22
22
  s.require_path = 'lib'
23
23
  s.authors = ['Philippe Vosges', 'Daniel Leuck']
24
24
  s.email = 'sdl-users@ikayzo.org'
25
25
  s.rubyforge_project = 'sdl4r'
26
- s.homepage = 'http://www.ikayzo.org/confluence/display/SDL/Home'
26
+ s.homepage = 'http://sdl4r.rubyforge.org/'
27
27
  s.files = FileList['lib/sdl4r.rb', 'lib/sdl4r/**/*.rb', 'bin/*', '[A-Z]*', 'test/**/*', 'doc/**/*'].to_a
28
28
  s.test_files = FileList[ 'test/**/*test.rb' ].to_a
29
29
  s.description = <<EOF
@@ -44,16 +44,19 @@ end
44
44
  # - once with the provided command (i.e. 7zip if zip is not available)
45
45
  # - once with "zip" disregarding the configuration (that attempt fails if you do not have the
46
46
  # "zip" command)
47
- Rake::PackageTask.new(spec.name, spec.version) do |p|
48
- p.need_zip = true
49
- p.need_tar = false
50
- p.need_tar_gz = false
51
- p.need_tar_bz2 = false
52
-
53
- # If "zip" is not available, we try 7-zip.
54
- system("zip")
55
- p.zip_command = "7z a -tzip" if $?.exitstatus == 127
56
- end
47
+ #Rake::PackageTask.new(spec.name, spec.version) do |p|
48
+ # p.need_zip = true
49
+ # p.need_tar = false
50
+ # p.need_tar_gz = false
51
+ # p.need_tar_bz2 = false
52
+ #
53
+ # # If "zip" is not available, we try 7-zip.
54
+ #puts "========================================================"
55
+ # system("zip")
56
+ # p.zip_command = "7z a -tzip" if $?.exitstatus == 127
57
+ # system("tar")
58
+ # p.tar_command = "7z a -ttar" if $?.exitstatus == 127
59
+ #end
57
60
 
58
61
  Rake::RDocTask.new do |rd|
59
62
  files = ['README', 'LICENSE', 'CHANGELOG', 'lib/**/*.rb', 'doc/**/*.rdoc', 'test/**/*.rb']
data/TODO CHANGED
@@ -73,7 +73,8 @@
73
73
  ["ns", "attr"] <=> attribute("ns", "attr")
74
74
  Should we allow attribute("ns:attr")?
75
75
  ==> Mmmm, but it could also mean "get child with that name". Let's wait.
76
- [ ] IDEA: marshaller? easy object <=> SDL read/write?
76
+ [x] IDEA: marshaller? easy object <=> SDL read/write?
77
+ SEE SPECS @ Ikayzo: http://www.ikayzo.org/confluence/display/SDL/Draft+-+Serialization+with+SDL
77
78
  [ ] Check the coverage and make the tests better.
78
79
  ==> Might be better to use some other tool than
79
80
  [ ] IDEA: add an option to the XML export allowing to write anonymous nodes as XML tag content?
@@ -84,7 +85,8 @@
84
85
  [ ] Look into performances, compared to YAML or XML parsers
85
86
  [ ] Future: SDL + ERB to have dynamic config templates
86
87
  see http://github.com/binarylogic/settingslogic
87
- [ ] Future: object dump/load as YAML does
88
+ [x] Future: object dump/load as YAML does
89
+ SEE SPECS @ Ikayzo: http://www.ikayzo.org/confluence/display/SDL/Draft+-+Serialization+with+SDL
88
90
  - add to_sdl(4r) to Object
89
91
 
90
92
  - for each object:
@@ -148,5 +150,10 @@
148
150
  vegetable {}
149
151
 
150
152
  ==> It seems this is not supported by the Java parser. Is it invalid/valid syntax?
151
- It will be in the next version of SDL.
152
- [x] BUG: '$' is a valid identifier character and it is not accepted.
153
+ ==> It will be in the next version of SDL.
154
+ [x] BUG: '$' is a valid identifier character and it is not accepted.
155
+ ====================================================================================================
156
+ [x] Check that if you write 2 values, a date and a timespan, you get both normally when you load.
157
+ ==> Considered as a bug in the language spec by Dan.
158
+ [x] BUG: negative years seem not to be supported in dates (somehow mistaken for integers: -4712/01/01)
159
+ [ ] Allow Symbols as attribute/child names
@@ -200,25 +200,25 @@ module SDL4R
200
200
  next_token = ((i + 1) < size)? tokens[i + 1] : nil
201
201
  if token.type == :DATE && next_token && next_token.type == :TIME
202
202
  date = token.object_for_literal()
203
- time_zone_with_zone = next_token.object_for_literal()
203
+ time_span_with_zone = next_token.object_for_literal()
204
204
 
205
- if time_zone_with_zone.day != 0
205
+ if time_span_with_zone.day
206
206
  # as there are days specified, it can't be a full precision date
207
207
  tag.add_value(date);
208
208
  tag.add_value(
209
209
  SdlTimeSpan.new(
210
- time_zone_with_zone.day,
211
- time_zone_with_zone.hour,
212
- time_zone_with_zone.min,
213
- time_zone_with_zone.sec))
210
+ time_span_with_zone.day || 0,
211
+ time_span_with_zone.hour,
212
+ time_span_with_zone.min,
213
+ time_span_with_zone.sec))
214
214
 
215
215
 
216
- if time_zone_with_zone.time_zone_offset
216
+ if time_span_with_zone.time_zone_offset
217
217
  parse_error("TimeSpan cannot have a timeZone", t.line, t.position)
218
218
  end
219
219
 
220
220
  else
221
- tag.add_value(combine(date, time_zone_with_zone))
221
+ tag.add_value(combine(date, time_span_with_zone))
222
222
  end
223
223
 
224
224
  i += 1
@@ -235,12 +235,7 @@ module SDL4R
235
235
  token.position)
236
236
  end
237
237
 
238
- tag.add_value(
239
- SdlTimeSpan.new(
240
- value.day,
241
- value.hour,
242
- value.min,
243
- value.sec))
238
+ tag.add_value(SdlTimeSpan.new(value.day || 0, value.hour, value.min, value.sec))
244
239
  else
245
240
  tag.add_value(value)
246
241
  end
@@ -370,7 +365,7 @@ module SDL4R
370
365
  date = token.object_for_literal()
371
366
  time_span_with_zone = tokens[i + 1].object_for_literal()
372
367
 
373
- if time_span_with_zone.day != 0
368
+ if time_span_with_zone.day
374
369
  expecting_but_got(
375
370
  "TIME (component of date/time) in attribute value",
376
371
  "TIME SPAN",
@@ -393,7 +388,7 @@ module SDL4R
393
388
  end
394
389
 
395
390
  time_span = SdlTimeSpan.new(
396
- time_span_with_zone.day,
391
+ time_span_with_zone.day || 0,
397
392
  time_span_with_zone.hour,
398
393
  time_span_with_zone.min,
399
394
  time_span_with_zone.sec)
@@ -471,33 +466,31 @@ module SDL4R
471
466
  # [days, hours, minutes, seconds, time_zone_offset].
472
467
  # 'days', 'hours' and 'minutes' are integers.
473
468
  # 'seconds' and 'time_zone_offset' are rational numbers.
474
- # 'days' and 'seconds' are equal to 0 if they're not specified in ((|literal|)).
469
+ # 'days' and 'seconds' are equal to 0 if they're not specified in +literal+.
475
470
  # 'time_zone_offset' is equal to nil if not specified.
476
471
  #
477
- # ((|allowDays|)) indicates whether the specification of days is allowed
478
- # in ((|literal|))
479
- # ((|allowTimeZone|)) indicates whether the specification of the timeZone is
480
- # allowed in ((|literal|))
472
+ # +allowDays+ indicates whether the specification of days is allowed
473
+ # in +literal+
474
+ # +allowTimeZone+ indicates whether the specification of the timeZone is
475
+ # allowed in +literal+
481
476
  #
482
- # All components are returned disregarding the values of ((|allowDays|)) and
483
- # ((|allowTimeZone|)).
477
+ # All components are returned disregarding the values of +allowDays+ and
478
+ # +allowTimeZone+.
484
479
  #
485
- # Raises an ArgumentError if ((|literal|)) has a bad format.
480
+ # Raises an ArgumentError if +literal+ has a bad format.
486
481
  def Parser.parse_time_span_and_time_zone(literal, allowDays, allowTimeZone)
487
482
  overall_sign = (literal =~ /^-/)? -1 : +1
488
483
 
489
484
  if literal =~ /^(([+\-]?\d+)d:)/
490
485
  if allowDays
491
486
  days = Integer($2)
492
- days_specified = true
493
487
  time_part = literal[($1.length)..-1]
494
488
  else
495
489
  # detected a day specification in a pure time literal
496
490
  raise ArgumentError, "unexpected day specification in #{literal}"
497
491
  end
498
492
  else
499
- days = 0;
500
- days_specified = false
493
+ days = nil
501
494
  time_part = literal
502
495
  end
503
496
 
@@ -544,7 +537,7 @@ module SDL4R
544
537
  end
545
538
 
546
539
  # take the sign into account
547
- hours *= overall_sign if days_specified # otherwise the sign is already applied to the hours
540
+ hours *= overall_sign if days # otherwise the sign is already applied to the hours
548
541
  minutes *= overall_sign
549
542
  seconds *= overall_sign
550
543
 
@@ -557,7 +550,7 @@ module SDL4R
557
550
 
558
551
  # Parses the given literal (String) into a returned DateTime object.
559
552
  #
560
- # Raises an ArgumentError if ((|literal|)) has a bad format.
553
+ # Raises an ArgumentError if +literal+ has a bad format.
561
554
  def Parser.parse_date_time(literal)
562
555
  raise ArgumentError("date literal is nil") if literal.nil?
563
556
 
@@ -607,7 +600,7 @@ module SDL4R
607
600
 
608
601
  def Parser.parse_date(literal)
609
602
  # here, we're being stricter than strptime() alone as we forbid trailing chars
610
- if literal =~ /^(\d+)\/(\d+)\/(\d+)$/
603
+ if literal =~ /^(-?\d+)\/(\d+)\/(\d+)$/
611
604
  begin
612
605
  return Date.strptime(literal, "%Y/%m/%d")
613
606
  rescue ArgumentError
@@ -618,10 +611,9 @@ module SDL4R
618
611
  raise ArgumentError, "Malformed Date <#{literal}>"
619
612
  end
620
613
 
621
- # Returns a String that contains the binary content corresponding to ((|literal|)).
614
+ # Returns a String that contains the binary content corresponding to +literal+.
622
615
  #
623
- # ((|literal|)) : a base-64 encoded literal (e.g.
624
- # "[V2hvIHdhbnRzIHRvIGxpdmUgZm9yZXZlcj8=]")
616
+ # +literal+ : a base-64 encoded literal (e.g. "[V2hvIHdhbnRzIHRvIGxpdmUgZm9yZXZlcj8=]")
625
617
  def Parser.parse_binary(literal)
626
618
  clean_literal = literal[1..-2] # remove square brackets
627
619
  return SdlBinary.decode64(clean_literal)
@@ -64,7 +64,7 @@ module SDL4R
64
64
  @type=:BINARY
65
65
  @object = Parser.parse_binary(text)
66
66
 
67
- elsif text =~ /^\d+\/\d+\/\d+$/
67
+ elsif text =~ /^-?\d+\/\d+\/\d+$/
68
68
  @type = :DATE;
69
69
  @object = Parser.parse_date_time(text)
70
70
 
@@ -45,23 +45,17 @@ module SDL4R
45
45
  # +indent+:: the indent string to use ("\t" by default)
46
46
  #
47
47
  def self.format(o, add_quotes = true, line_prefix = "", indent = "\t")
48
- if o.is_a?(String)
49
- if add_quotes
50
- o_length = 0
51
- o.scan(/./m) { o_length += 1 } # counts the number of chars (as opposed of bytes)
52
- if o_length == 1
53
- return "'" + escape(o, "'") + "'"
54
- else
55
- return '"' + escape(o, '"') + '"'
56
- end
57
- else
58
- return escape(o)
59
- end
48
+ case o
49
+ when String
50
+ return format_string(o)
60
51
 
61
- elsif o.is_a?(Bignum)
52
+ when Symbol
53
+ return format_string(o.to_s)
54
+
55
+ when Bignum
62
56
  return o.to_s + "BD"
63
57
 
64
- elsif o.is_a?(Integer)
58
+ when Integer
65
59
  if MIN_INTEGER_32 <= o and o <= MAX_INTEGER_32
66
60
  return o.to_s
67
61
  elsif MIN_INTEGER_64 <= o and o <= MAX_INTEGER_64
@@ -70,21 +64,21 @@ module SDL4R
70
64
  return o.to_s + "BD"
71
65
  end
72
66
 
73
- elsif o.is_a?(Float)
67
+ when Float
74
68
  return (o.to_s + "F")
75
69
 
76
- elsif o.is_a?(Rational)
70
+ when Rational
77
71
  return o.to_f.to_s + "F"
78
72
 
79
- elsif o.is_a?(BigDecimal)
73
+ when BigDecimal
80
74
  s = o.to_s('F')
81
75
  s.sub!(/\.0$/, "")
82
76
  return "#{s}BD"
83
77
 
84
- elsif o.nil?
78
+ when NilClass
85
79
  return "null"
86
80
 
87
- elsif o.is_a?(SdlBinary)
81
+ when SdlBinary
88
82
  encoded_o = Base64.encode64(o.bytes)
89
83
  encoded_o.gsub!(/[\r\n]/m, "") # Remove the EOL inserted every 60 chars
90
84
 
@@ -104,7 +98,7 @@ module SDL4R
104
98
 
105
99
  # Below, we use "#{o.year}" instead of "%Y" because "%Y" always emit 4 chars at least even if
106
100
  # the date is before 1000.
107
- elsif o.is_a?(DateTime) || o.is_a?(Time)
101
+ when DateTime, Time
108
102
  milliseconds = get_datetime_milliseconds(o)
109
103
 
110
104
  if milliseconds == 0
@@ -123,7 +117,7 @@ module SDL4R
123
117
  end
124
118
  end
125
119
 
126
- elsif o.is_a?(Date)
120
+ when Date
127
121
  return o.strftime("#{o.year}/%m/%d")
128
122
 
129
123
  else
@@ -152,6 +146,7 @@ module SDL4R
152
146
  # SdlTimeSpan, SdlBinary,
153
147
  #
154
148
  # Rationals are turned into Floats using Rational#to_f.
149
+ # Symbols are turned into Strings using Symbol#to_s.
155
150
  #
156
151
  def self.coerce_or_fail(o)
157
152
  case o
@@ -159,6 +154,9 @@ module SDL4R
159
154
  when Rational
160
155
  return o.to_f
161
156
 
157
+ when Symbol
158
+ return o.to_s
159
+
162
160
  when NilClass,
163
161
  String,
164
162
  Numeric,
@@ -176,6 +174,19 @@ module SDL4R
176
174
 
177
175
  raise ArgumentError, "#{o.class.name} is not coercible to an SDL type"
178
176
  end
177
+
178
+ # Indicates whether 'o' is coercible to a SDL litteral type.
179
+ # See #coerce_or_fail
180
+ #
181
+ def self.is_coercible?(o)
182
+ begin
183
+ coerce_or_fail(o)
184
+ true
185
+
186
+ rescue ArgumentError
187
+ false
188
+ end
189
+ end
179
190
 
180
191
  # Validates an SDL identifier String. SDL Identifiers must start with a
181
192
  # Unicode letter or underscore (_) and contain only unicode letters,
@@ -272,7 +283,7 @@ module SDL4R
272
283
  #
273
284
  # # { "value" => 1, "debugging" => true, "time" => SdlTimeSpan.new(12, 24, 01) }
274
285
  #
275
- def self.to_attribute_map(s)
286
+ def self.to_attribute_hash(s)
276
287
  raise ArgumentError, "'s' cannot be null" if s.nil?
277
288
  return read("atts " + s).child.attributes
278
289
  end
@@ -282,6 +293,23 @@ module SDL4R
282
293
  class << self
283
294
  private
284
295
 
296
+ # Returns the specified string 's' formatted as a SDL string.
297
+ # See SDL4R#format.
298
+ #
299
+ def format_string(s, add_quotes = true)
300
+ if add_quotes
301
+ s_length = 0
302
+ s.scan(/./m) { s_length += 1 } # counts the number of chars (as opposed to bytes)
303
+ if s_length == 1
304
+ return "'" + escape(s, "'") + "'"
305
+ else
306
+ return '"' + escape(s, '"') + '"'
307
+ end
308
+ else
309
+ return escape(s)
310
+ end
311
+ end
312
+
285
313
  # Wraps lines in "s" (by modifying it). This method only supports 1-byte character strings.
286
314
  #
287
315
  def wrap_lines_in_ascii(s, line_length, line_prefix = nil)
@@ -247,12 +247,12 @@ module SDL4R
247
247
  # 24d:12:13:09.234 (24 days, 12 hours, 13 minutes, 9 seconds,
248
248
  # 234 milliseconds)
249
249
  #
250
- def to_s
250
+ def to_s(force_show_days = false)
251
251
  _days = days
252
252
  _milliseconds = milliseconds
253
253
 
254
254
  s = nil
255
- if _days == 0
255
+ if _days == 0 and not force_show_days
256
256
  if _milliseconds == 0
257
257
  s = sprintf("%d:%02d:%02d", hours, minutes.abs, seconds.abs)
258
258
  else
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env ruby -w
2
+ # encoding: UTF-8
3
+
4
+ #--
5
+ # Simple Declarative Language (SDL) for Ruby
6
+ # Copyright 2005 Ikayzo, inc.
7
+ #
8
+ # This program is free software. You can distribute or modify it under the
9
+ # terms of the GNU Lesser General Public License version 2.1 as published by
10
+ # the Free Software Foundation.
11
+ #
12
+ # This program is distributed AS IS and WITHOUT WARRANTY. OF ANY KIND,
13
+ # INCLUDING MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
14
+ # See the GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with this program; if not, contact the Free Software Foundation, Inc.,
18
+ # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
+ #++
20
+
21
+
22
+ module SDL4R
23
+
24
+ require 'ostruct'
25
+
26
+ require File.dirname(__FILE__) + '/sdl4r'
27
+ require File.dirname(__FILE__) + '/tag'
28
+
29
+ # Allows to serialize/deserialize between SDL and Ruby Objects.
30
+ #
31
+ # == Authors
32
+ # Philippe Vosges
33
+ #
34
+ class Serializer
35
+
36
+ def serialize(o, tag = Tag.new(SDL4R::ROOT_TAG_NAME))
37
+ raise ArgumentError, '"tag" must be a Tag' unless tag.is_a?(Tag)
38
+
39
+ o = o.marshal_dump if o.is_a? OpenStruct
40
+
41
+ if o.is_a? Hash
42
+ o.each_pair { |key, value|
43
+ serialize_variable(key.to_s, value, tag)
44
+ }
45
+
46
+ else
47
+ o.instance_variables.each { |name|
48
+ value = o.instance_variable_get(name)
49
+ name = name[1..name.size] if name =~ /^@/
50
+ serialize_variable(name, value, tag)
51
+ }
52
+ end
53
+
54
+ tag
55
+ end
56
+
57
+ def deserialize(tag, o = OpenStruct.new)
58
+ is_open_struct_or_hash = o.is_a?(OpenStruct) or o.is_a?(Hash)
59
+
60
+ # TODO Deserialization from anonymous child tags
61
+
62
+ # Deserialization from values
63
+ if tag.has_values?
64
+ if tag.has_children? or tag.has_attributes?
65
+ variable_value = tag.values
66
+
67
+ if o.instance_variable_defined?("@value") or not o.instance_variable_defined?("@values")
68
+ # value is preferred
69
+ variable_name = "value"
70
+ variable_value = variable_value[0] if variable_value.length == 1
71
+ else
72
+ variable_name = "values"
73
+ end
74
+
75
+ if is_open_struct_or_hash or o.instance_variable_defined?(variable_name)
76
+ set_object_variable(o, variable_name, variable_value)
77
+ end
78
+
79
+ else
80
+ # the tag only has values
81
+ return variable_value.length == 1 ? variable_value[0] : variable_value
82
+ end
83
+ end
84
+
85
+ # Deserialization from attributes
86
+ tag.attributes do |attribute_namespace, attribute_name, attribute_value|
87
+ if is_open_struct_or_hash or o.instance_variable_defined?("@#{attribute_name}")
88
+ set_object_variable(o, attribute_name, attribute_value)
89
+ end
90
+ end
91
+
92
+ # Deserialization from (not anonymous) child tags
93
+ tag.children do |child|
94
+ # if child.namespace != '' or child.name != SDL4R::ANONYMOUS_TAG_NAME
95
+ variable_name = "@#{child.name}"
96
+
97
+ # Check wether this variable is assignable
98
+ if is_open_struct_or_hash or o.instance_variable_defined?(variable_name)
99
+ variable_value = nil
100
+
101
+ if child.has_values? and not child.has_children? and not child.has_attributes?
102
+ # If the object only has values (no children, no atttributes):
103
+ # then the values are the variable value
104
+ variable_value = child.values
105
+ variable_value = variable_value[0] if variable_value.length == 1
106
+
107
+ else
108
+ # Consider this tag as an object
109
+ previous_value = o.instance_variable_get(variable_name)
110
+ if previous_value.nil?
111
+ variable_value = deserialize(child)
112
+ elsif SDL4R::is_coercible?(previous_value)
113
+ variable_value = deserialize(child)
114
+ else
115
+ variable_value = deserialize(child, previous_value)
116
+ end
117
+ end
118
+
119
+ set_object_variable(o, child.name, variable_value)
120
+ end
121
+ # end
122
+ end
123
+
124
+ return o
125
+ end
126
+
127
+ private
128
+
129
+ def serialize_variable(name, value, tag)
130
+ if SDL4R::is_coercible?(value)
131
+ # SDL litteral type
132
+ tag.set_attribute(name, value)
133
+ else
134
+ # Simple object
135
+ serialize(value, tag.new_child(name))
136
+ end
137
+ end
138
+
139
+ def set_object_variable(o, name, value)
140
+ case o
141
+ when Hash
142
+ o[name] = value
143
+
144
+ when OpenStruct
145
+ o.send "#{name}=", value
146
+
147
+ else
148
+ o.instance_variable_set("@#{name}", value)
149
+ end
150
+ end
151
+ end
152
+ end