sdl4r 0.9.8 → 0.9.9

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