edouard-ya2yaml 0.30.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +119 -0
  3. data/lib/ya2yaml.rb +375 -0
  4. data/test/t.gif +0 -0
  5. data/test/t.yaml +5 -0
  6. data/test/test.rb +496 -0
  7. metadata +60 -0
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2006 Akira FUNAI <funai.akira@gmail.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a
4
+ copy of this software and associated documentation files (the "Software"),
5
+ to deal in the Software without restriction, including without limitation
6
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ and/or sell copies of the Software, and to permit persons to whom the
8
+ Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19
+ DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,119 @@
1
+ = Ya2YAML - An UTF8 safe YAML dumper
2
+
3
+ Project Contact: Akira FUNAI <akira -at- funai -dot- com>
4
+
5
+ Ya2YAML is "yet another to_yaml". It emits YAML document with complete UTF8 support (string/binary detection, "\u" escape sequences and Unicode specific line breaks). I hope someone might find it useful until Syck/RbYAML come out with UTF8/16 support.
6
+
7
+ == Usage
8
+
9
+ *code*:
10
+
11
+ # encoding: UTF-8
12
+ $KCODE = 'UTF8' unless RUBY_VERSION >= '1.9'
13
+ require 'ya2yaml'
14
+
15
+ obj = [
16
+ "abc\nxyz\n",
17
+ "日本語\n文字列\n",
18
+ "\xfd\xfe\xff",
19
+ ]
20
+ puts obj.ya2yaml(:syck_compatible => true)
21
+
22
+ *output*:
23
+
24
+ ---
25
+ - |
26
+ abc
27
+ xyz
28
+ - |
29
+ 日本語
30
+ 文字列
31
+ - !binary |
32
+ /f7/
33
+
34
+ == Method and Objects
35
+
36
+ When you require 'ya2yaml', public method 'Object#ya2yaml' is defined. Currently these Objects are supported.
37
+
38
+ as YAML generic types:
39
+ - Array
40
+ - Hash
41
+ - NilClass
42
+ - String
43
+ - TrueClass
44
+ - FalseClass
45
+ - Fixnum
46
+ - Bignum
47
+ - Float
48
+ - Date
49
+ - Time
50
+
51
+ as Ruby specific types:
52
+ - Symbol
53
+ - Range
54
+ - Regexp
55
+ - Struct
56
+ - everything else as a generic Object (serializes instance variables)
57
+
58
+ A String which contains any non-UTF8 character will be regarded as "binary" and encoded in BASE64.
59
+
60
+ == Options
61
+
62
+ # set them individually
63
+ obj.ya2yaml(
64
+ :indent_size => 4,
65
+ :hash_order => ['name','age','address'],
66
+ :minimum_block_length => 16,
67
+ :printable_with_syck => true,
68
+ :escape_b_specific => true,
69
+ :escape_as_utf8 => true
70
+ :preserve_order => true
71
+ )
72
+
73
+ # or simply set this for a safe roundtrip with Syck.
74
+ obj.ya2yaml(:syck_compatible => true)
75
+
76
+ **CAUTION** Some of these options are to avoid compatibility issue with Syck. When you set these options to false, the emitted YAML document might not be loadable with YAML.load() although the syntax is valid.
77
+
78
+ - :indent_size
79
+ - default: 2
80
+ - Number of indentation spaces for a level.
81
+
82
+ - :hash_order
83
+ - default: nil
84
+ - Array of hash keys indicating sort order (this option only works on a top-level hash).
85
+
86
+ - :minimum_block_length
87
+ - default: 0
88
+ - Minimum length of a string emitted in block scalar style. If a string is shorter than this value, it will be emitted in one line.
89
+
90
+ - :printable_with_syck
91
+ - default: false
92
+ - When set to true, Ya2YAML will regard some kind of strings (which cause Syck roundtrip failures) as "non-printable" and double-quote them.
93
+
94
+ - :escape_b_specific
95
+ - default: false
96
+ - When set to true, Ya2YAML will regard Unicode specific line breaks (LS and PS) as "non-printable" and escape them.
97
+
98
+ - :escape_as_utf8
99
+ - default: false
100
+ - When set to true, Ya2YAML uses Ruby-like escape sequences in UTF8 code instead of "\uXXXX" style in UCS code. It also suppresses use of "\L" and "\P" (escape sequences for LS and PS).
101
+
102
+ - :preserve_order
103
+ - default: false
104
+ - When set to true, the order of keys in hashes will be the natural hash order rather than sorted alphabetically or explicitelly (usefull for syck/psych roundtrip and ruby >= 1.9.2)
105
+
106
+ - :syck_compatible
107
+ - default: false
108
+ - When set to true, These options are set to true at once. You have to set this to false when you set them individually.
109
+ - :printable_with_syck
110
+ - :escape_b_specific
111
+ - :escape_as_utf8
112
+
113
+ == More information
114
+
115
+ Visit http://rubyforge.org/projects/ya2yaml
116
+
117
+ == License
118
+
119
+ Ya2YAML is distributed with a MIT license, which can be found in the file LICENSE.
data/lib/ya2yaml.rb ADDED
@@ -0,0 +1,375 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2006-2010 Akira FUNAI
5
+ # License:: MIT License
6
+
7
+ class Ya2YAML
8
+
9
+ def initialize(opts = {})
10
+ options = opts.dup
11
+ options[:indent_size] = 2 if options[:indent_size].to_i <= 0
12
+ options[:minimum_block_length] = 0 if options[:minimum_block_length].to_i <= 0
13
+ options.update(
14
+ {
15
+ :printable_with_syck => true,
16
+ :escape_b_specific => true,
17
+ :escape_as_utf8 => true,
18
+ }
19
+ ) if options[:syck_compatible]
20
+
21
+ @options = options
22
+ end
23
+
24
+ def _ya2yaml(obj)
25
+ raise 'set $KCODE to "UTF8".' if (RUBY_VERSION < '1.9.0') && ($KCODE != 'UTF8')
26
+ '--- ' + emit(obj, 1) + "\n"
27
+ rescue SystemStackError
28
+ raise ArgumentError, "ya2yaml can't handle circular references"
29
+ end
30
+
31
+ private
32
+
33
+ def emit(obj, level)
34
+ case obj
35
+ when Array
36
+ if (obj.length == 0)
37
+ '[]'
38
+ else
39
+ indent = "\n" + s_indent(level - 1)
40
+ obj.collect {|o|
41
+ indent + '- ' + emit(o, level + 1)
42
+ }.join('')
43
+ end
44
+ when Hash
45
+ if (obj.length == 0)
46
+ '{}'
47
+ else
48
+ indent = "\n" + s_indent(level - 1)
49
+ hash_order = @options[:hash_order]
50
+ if (hash_order && level == 1)
51
+ hash_keys = obj.keys.sort {|x, y|
52
+ x_order = hash_order.index(x) ? hash_order.index(x) : Float::MAX
53
+ y_order = hash_order.index(y) ? hash_order.index(y) : Float::MAX
54
+ o = (x_order <=> y_order)
55
+ (o != 0) ? o : (x.to_s <=> y.to_s)
56
+ }
57
+ elsif @options[:preserve_order]
58
+ hash_keys = obj.keys
59
+ else
60
+ hash_keys = obj.keys.sort {|x, y| x.to_s <=> y.to_s }
61
+ end
62
+ hash_keys.collect {|k|
63
+ key = emit(k, level + 1)
64
+ if (
65
+ is_one_plain_line?(key) ||
66
+ key =~ /\A(#{REX_BOOL}|#{REX_FLOAT}|#{REX_INT}|#{REX_NULL})\z/x
67
+ )
68
+ indent + key + ': ' + emit(obj[k], level + 1)
69
+ else
70
+ indent + '? ' + key +
71
+ indent + ': ' + emit(obj[k], level + 1)
72
+ end
73
+ }.join('')
74
+ end
75
+ when NilClass
76
+ '~'
77
+ when String
78
+ emit_string(obj, level)
79
+ when TrueClass, FalseClass
80
+ obj.to_s
81
+ when Fixnum, Bignum, Float
82
+ obj.to_s
83
+ when Date
84
+ obj.to_s
85
+ when Time
86
+ offset = obj.gmtoff
87
+ off_hm = sprintf(
88
+ '%+.2d:%.2d',
89
+ (offset / 3600.0).to_i,
90
+ (offset % 3600.0) / 60
91
+ )
92
+ u_sec = (obj.usec != 0) ? sprintf(".%.6d", obj.usec) : ''
93
+ obj.strftime("%Y-%m-%d %H:%M:%S#{u_sec} #{off_hm}")
94
+ when Symbol
95
+ '!ruby/symbol ' + emit_string(obj.to_s, level)
96
+ when Range
97
+ '!ruby/range ' + obj.to_s
98
+ when Regexp
99
+ '!ruby/regexp ' + obj.inspect
100
+ else
101
+ case
102
+ when obj.is_a?(Struct)
103
+ struct_members = {}
104
+ obj.each_pair{|k, v| struct_members[k.to_s] = v }
105
+ '!ruby/struct:' + obj.class.to_s.sub(/^(Struct::(.+)|.*)$/, '\2') + ' ' +
106
+ emit(struct_members, level + 1)
107
+ else
108
+ # serialized as a generic object
109
+ object_members = {}
110
+ obj.instance_variables.each{|k, v|
111
+ object_members[k.to_s.sub(/^@/, '')] = obj.instance_variable_get(k)
112
+ }
113
+ '!ruby/object:' + obj.class.to_s + ' ' +
114
+ emit(object_members, level + 1)
115
+ end
116
+ end
117
+ end
118
+
119
+ def emit_string(str, level)
120
+ (is_string, is_printable, is_one_line, is_one_plain_line) = string_type(str)
121
+ if is_string
122
+ if is_printable
123
+ if is_one_plain_line
124
+ emit_simple_string(str, level)
125
+ else
126
+ (is_one_line || str.length < @options[:minimum_block_length]) ?
127
+ emit_quoted_string(str, level) :
128
+ emit_block_string(str, level)
129
+ end
130
+ else
131
+ emit_quoted_string(str, level)
132
+ end
133
+ else
134
+ emit_base64_binary(str, level)
135
+ end
136
+ end
137
+
138
+ def emit_simple_string(str, level)
139
+ str
140
+ end
141
+
142
+ def emit_block_string(str, level)
143
+ str = normalize_line_break(str)
144
+
145
+ indent = s_indent(level)
146
+ indentation_indicator = (str =~ /\A /) ? indent.size.to_s : ''
147
+ str =~ /(#{REX_NORMAL_LB}*)\z/
148
+ chomping_indicator = case $1.length
149
+ when 0
150
+ '-'
151
+ when 1
152
+ ''
153
+ else
154
+ '+'
155
+ end
156
+
157
+ str.chomp!
158
+ str.gsub!(/#{REX_NORMAL_LB}/) {
159
+ $1 + indent
160
+ }
161
+ '|' + indentation_indicator + chomping_indicator + "\n" + indent + str
162
+ end
163
+
164
+ def emit_quoted_string(str, level)
165
+ str = yaml_escape(normalize_line_break(str))
166
+ if (str.length < @options[:minimum_block_length])
167
+ str.gsub!(/#{REX_NORMAL_LB}/) { ESCAPE_SEQ_LB[$1] }
168
+ else
169
+ str.gsub!(/#{REX_NORMAL_LB}$/) { ESCAPE_SEQ_LB[$1] }
170
+ str.gsub!(/(#{REX_NORMAL_LB}+)(.)/) {
171
+ trail_c = $3
172
+ $1 + trail_c.sub(/([\t ])/) { ESCAPE_SEQ_WS[$1] }
173
+ }
174
+ indent = s_indent(level)
175
+ str.gsub!(/#{REX_NORMAL_LB}/) {
176
+ ESCAPE_SEQ_LB[$1] + "\\\n" + indent
177
+ }
178
+ end
179
+ '"' + str + '"'
180
+ end
181
+
182
+ def emit_base64_binary(str, level)
183
+ indent = "\n" + s_indent(level)
184
+ base64 = [str].pack('m')
185
+ '!binary |' + indent + base64.gsub(/\n(?!\z)/, indent)
186
+ end
187
+
188
+ def string_type(str)
189
+ if str.respond_to?(:encoding) && (!str.valid_encoding? || str.encoding == Encoding::ASCII_8BIT)
190
+ return false, false, false, false
191
+ end
192
+ (ucs_codes = str.unpack('U*')) rescue (
193
+ # ArgumentError -> binary data
194
+ return false, false, false, false
195
+ )
196
+ if (
197
+ @options[:printable_with_syck] &&
198
+ str =~ /\A#{REX_ANY_LB}* | #{REX_ANY_LB}*\z|#{REX_ANY_LB}{2}\z/
199
+ )
200
+ # detour Syck bug
201
+ return true, false, nil, false
202
+ end
203
+ ucs_codes.each {|ucs_code|
204
+ return true, false, nil, false unless is_printable?(ucs_code)
205
+ }
206
+ return true, true, is_one_line?(str), is_one_plain_line?(str)
207
+ end
208
+
209
+ def is_printable?(ucs_code)
210
+ # YAML 1.1 / 4.1.1.
211
+ (
212
+ [0x09, 0x0a, 0x0d, 0x85].include?(ucs_code) ||
213
+ (ucs_code <= 0x7e && ucs_code >= 0x20) ||
214
+ (ucs_code <= 0xd7ff && ucs_code >= 0xa0) ||
215
+ (ucs_code <= 0xfffd && ucs_code >= 0xe000) ||
216
+ (ucs_code <= 0x10ffff && ucs_code >= 0x10000)
217
+ ) &&
218
+ !(
219
+ # treat LS/PS as non-printable characters
220
+ @options[:escape_b_specific] &&
221
+ (ucs_code == 0x2028 || ucs_code == 0x2029)
222
+ )
223
+ end
224
+
225
+ def is_one_line?(str)
226
+ str !~ /#{REX_ANY_LB}(?!\z)/
227
+ end
228
+
229
+ def is_one_plain_line?(str)
230
+ # YAML 1.1 / 4.6.11.
231
+ str !~ /^([\-\?:,\[\]\{\}\#&\*!\|>'"%@`\s]|---|\.\.\.)/ &&
232
+ str !~ /[:\#\s\[\]\{\},]/ &&
233
+ str !~ /#{REX_ANY_LB}/ &&
234
+ str !~ /^(#{REX_BOOL}|#{REX_FLOAT}|#{REX_INT}|#{REX_MERGE}
235
+ |#{REX_NULL}|#{REX_TIMESTAMP}|#{REX_VALUE})$/x
236
+ end
237
+
238
+ def s_indent(level)
239
+ # YAML 1.1 / 4.2.2.
240
+ ' ' * (level * @options[:indent_size])
241
+ end
242
+
243
+ def normalize_line_break(str)
244
+ # YAML 1.1 / 4.1.4.
245
+ str.gsub(/(#{REX_CRLF}|#{REX_CR}|#{REX_NEL})/, "\n")
246
+ end
247
+
248
+ def yaml_escape(str)
249
+ # YAML 1.1 / 4.1.6.
250
+ str.gsub(/[^a-zA-Z0-9]/u) {|c|
251
+ ucs_code, = (c.unpack('U') rescue [??])
252
+ case
253
+ when ESCAPE_SEQ[c]
254
+ ESCAPE_SEQ[c]
255
+ when is_printable?(ucs_code)
256
+ c
257
+ when @options[:escape_as_utf8]
258
+ c.respond_to?(:bytes) ?
259
+ c.bytes.collect {|b| '\\x%.2x' % b }.join :
260
+ '\\x' + c.unpack('H2' * c.size).join('\\x')
261
+ when ucs_code == 0x2028 || ucs_code == 0x2029
262
+ ESCAPE_SEQ_LB[c]
263
+ when ucs_code <= 0x7f
264
+ sprintf('\\x%.2x', ucs_code)
265
+ when ucs_code <= 0xffff
266
+ sprintf('\\u%.4x', ucs_code)
267
+ else
268
+ sprintf('\\U%.8x', ucs_code)
269
+ end
270
+ }
271
+ end
272
+
273
+ module Constants
274
+ UCS_0X85 = [0x85].pack('U') # c285@UTF8 Unicode next line
275
+ UCS_0XA0 = [0xa0].pack('U') # c2a0@UTF8 Unicode non-breaking space
276
+ UCS_0X2028 = [0x2028].pack('U') # e280a8@UTF8 Unicode line separator
277
+ UCS_0X2029 = [0x2029].pack('U') # e280a9@UTF8 Unicode paragraph separator
278
+
279
+ # non-break characters
280
+ ESCAPE_SEQ = {
281
+ "\x00" => '\\0',
282
+ "\x07" => '\\a',
283
+ "\x08" => '\\b',
284
+ "\x0b" => '\\v',
285
+ "\x0c" => '\\f',
286
+ "\x1b" => '\\e',
287
+ "\"" => '\\"',
288
+ "\\" => '\\\\',
289
+ }
290
+
291
+ # non-breaking space
292
+ ESCAPE_SEQ_NS = {
293
+ UCS_0XA0 => '\\_',
294
+ }
295
+
296
+ # white spaces
297
+ ESCAPE_SEQ_WS = {
298
+ "\x09" => '\\t',
299
+ " " => '\\x20',
300
+ }
301
+
302
+ # line breaks
303
+ ESCAPE_SEQ_LB ={
304
+ "\x0a" => '\\n',
305
+ "\x0d" => '\\r',
306
+ UCS_0X85 => '\\N',
307
+ UCS_0X2028 => '\\L',
308
+ UCS_0X2029 => '\\P',
309
+ }
310
+
311
+ # regexps for line breaks
312
+ REX_LF = Regexp.escape("\x0a")
313
+ REX_CR = Regexp.escape("\x0d")
314
+ REX_CRLF = Regexp.escape("\x0d\x0a")
315
+ REX_NEL = Regexp.escape(UCS_0X85)
316
+ REX_LS = Regexp.escape(UCS_0X2028)
317
+ REX_PS = Regexp.escape(UCS_0X2029)
318
+
319
+ REX_ANY_LB = /(#{REX_LF}|#{REX_CR}|#{REX_NEL}|#{REX_LS}|#{REX_PS})/
320
+ REX_NORMAL_LB = /(#{REX_LF}|#{REX_LS}|#{REX_PS})/
321
+
322
+ # regexps for language-Independent types for YAML1.1
323
+ REX_BOOL = /
324
+ y|Y|yes|Yes|YES|n|N|no|No|NO
325
+ |true|True|TRUE|false|False|FALSE
326
+ |on|On|ON|off|Off|OFF
327
+ /x
328
+ REX_FLOAT = /
329
+ [-+]?([0-9][0-9_]*)?\.[0-9.]*([eE][-+][0-9]+)? # (base 10)
330
+ |[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\.[0-9_]* # (base 60)
331
+ |[-+]?\.(inf|Inf|INF) # (infinity)
332
+ |\.(nan|NaN|NAN) # (not a number)
333
+ /x
334
+ REX_INT = /
335
+ [-+]?0b[0-1_]+ # (base 2)
336
+ |[-+]?0[0-7_]+ # (base 8)
337
+ |[-+]?(0|[1-9][0-9_]*) # (base 10)
338
+ |[-+]?0x[0-9a-fA-F_]+ # (base 16)
339
+ |[-+]?[1-9][0-9_]*(:[0-5]?[0-9])+ # (base 60)
340
+ /x
341
+ REX_MERGE = /
342
+ <<
343
+ /x
344
+ REX_NULL = /
345
+ ~ # (canonical)
346
+ |null|Null|NULL # (English)
347
+ | # (Empty)
348
+ /x
349
+ REX_TIMESTAMP = /
350
+ [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] # (ymd)
351
+ |[0-9][0-9][0-9][0-9] # (year)
352
+ -[0-9][0-9]? # (month)
353
+ -[0-9][0-9]? # (day)
354
+ ([Tt]|[ \t]+)[0-9][0-9]? # (hour)
355
+ :[0-9][0-9] # (minute)
356
+ :[0-9][0-9] # (second)
357
+ (\.[0-9]*)? # (fraction)
358
+ (([ \t]*)Z|[-+][0-9][0-9]?(:[0-9][0-9])?)? # (time zone)
359
+ /x
360
+ REX_VALUE = /
361
+ =
362
+ /x
363
+ end
364
+
365
+ include Constants
366
+
367
+ end
368
+
369
+ class Object
370
+ def ya2yaml(options = {})
371
+ Ya2YAML.new(options)._ya2yaml(self)
372
+ end
373
+ end
374
+
375
+ __END__
data/test/t.gif ADDED
Binary file
data/test/t.yaml ADDED
@@ -0,0 +1,5 @@
1
+ ---
2
+ list:
3
+ - perl
4
+ - ruby
5
+ - python
data/test/test.rb ADDED
@@ -0,0 +1,496 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # Author:: Akira FUNAI
5
+ # Copyright:: Copyright (c) 2006-2010 Akira FUNAI
6
+ # License:: MIT License
7
+
8
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
9
+
10
+ Dir.chdir(File.dirname(__FILE__))
11
+
12
+ $KCODE = 'UTF8' if RUBY_VERSION < '1.9.0'
13
+ require 'ya2yaml'
14
+
15
+ require 'yaml'
16
+ require 'test/unit'
17
+
18
+ # There's an incompatibility in how ruby handles struct dumps
19
+ # between versions that's beyond our scope.
20
+ # (One uses strings internally, the other symbols.)
21
+ # This just enables tests to pass.
22
+ class << Struct
23
+ alias yaml_new_without_indifferent_keys yaml_new
24
+ def yaml_new(klass, tag, val)
25
+ val.keys.each { |k, v| val[k.to_sym] = val.delete(k) }
26
+ yaml_new_without_indifferent_keys(klass, tag, val)
27
+ end
28
+ end if RUBY_VERSION >= "1.9"
29
+
30
+ class TC_Ya2YAML < Test::Unit::TestCase
31
+
32
+ @@struct_klass = Struct::new('Foo', :bar, :buz)
33
+ class Moo
34
+ attr_accessor :val1, :val2
35
+ def initialize(val1, val2)
36
+ @val1 = val1
37
+ @val2 = val2
38
+ end
39
+ def ==(k)
40
+ (k.class == self.class) &&
41
+ (k.val1 == self.val1) &&
42
+ (k.val2 == self.val2)
43
+ end
44
+ end
45
+ puts "test may take minutes. please wait.\n"
46
+
47
+ def setup
48
+ @text = File.open('./t.yaml', 'r') { |f| f.read }
49
+ @gif = File.open('./t.gif', 'rb') { |f| f.read }
50
+ @struct = @@struct_klass.new('barbarbar', @@struct_klass.new('baaaar', 12345))
51
+ @klass = Moo.new('boobooboo', Time.local(2009, 2, 9, 16, 44, 10))
52
+ end
53
+
54
+ def test_options
55
+ opt = {:syck_compatible => true}
56
+ 'foobar'.ya2yaml(opt)
57
+ assert_equal(
58
+ {:syck_compatible => true},
59
+ opt,
60
+ 'ya2yaml should not change the option hash'
61
+ )
62
+
63
+ [
64
+ [
65
+ {},
66
+ "--- \n- \"\\u0086\"\n- |-\n a\xe2\x80\xa8 b\xe2\x80\xa9 c\n- |4-\n abc\n xyz\n",
67
+ ],
68
+ [
69
+ {:indent_size => 4},
70
+ "--- \n- \"\\u0086\"\n- |-\n a\xe2\x80\xa8 b\xe2\x80\xa9 c\n- |8-\n abc\n xyz\n",
71
+ ],
72
+ [
73
+ {:minimum_block_length => 16},
74
+ "--- \n- \"\\u0086\"\n- \"a\\Lb\\Pc\"\n- \" abc\\nxyz\"\n",
75
+ ],
76
+ # [
77
+ # {:emit_c_document_end => true},
78
+ # "--- \n- \"\\u0086\"\n- |-\n a\xe2\x80\xa8 b\xe2\x80\xa9 c\n- |4-\n abc\n xyz\n...\n",
79
+ # ],
80
+ [
81
+ {:printable_with_syck => true},
82
+ "--- \n- \"\\u0086\"\n- |-\n a\xe2\x80\xa8 b\xe2\x80\xa9 c\n- \" abc\\n\\\n xyz\"\n",
83
+ ],
84
+ [
85
+ {:escape_b_specific => true},
86
+ "--- \n- \"\\u0086\"\n- \"a\\Lb\\Pc\"\n- |4-\n abc\n xyz\n",
87
+ ],
88
+ [
89
+ {:escape_as_utf8 => true},
90
+ "--- \n- \"\\xc2\\x86\"\n- |-\n a\xe2\x80\xa8 b\xe2\x80\xa9 c\n- |4-\n abc\n xyz\n",
91
+ ],
92
+ [
93
+ {:syck_compatible => true},
94
+ "--- \n- \"\\xc2\\x86\"\n- \"a\\xe2\\x80\\xa8b\\xe2\\x80\\xa9c\"\n- \" abc\\n\\\n xyz\"\n",
95
+ ],
96
+ ].each {|opt, yaml|
97
+ y = ["\xc2\x86", "a\xe2\x80\xa8b\xe2\x80\xa9c", " abc\nxyz"].ya2yaml(opt)
98
+ assert_equal(
99
+ yaml,
100
+ y,
101
+ "option #{opt.inspect} should be recognized"
102
+ )
103
+ }
104
+ end
105
+
106
+ def test_hash_order
107
+ [
108
+ [
109
+ nil,
110
+ "--- \na: 1\nb: 2\nc: 3\n",
111
+ ],
112
+ [
113
+ [],
114
+ "--- \na: 1\nb: 2\nc: 3\n",
115
+ ],
116
+ [
117
+ ['c', 'b', 'a'],
118
+ "--- \nc: 3\nb: 2\na: 1\n",
119
+ ],
120
+ [
121
+ ['b'],
122
+ "--- \nb: 2\na: 1\nc: 3\n",
123
+ ],
124
+ ].each {|hash_order, yaml|
125
+ y = {
126
+ 'a' => 1,
127
+ 'c' => 3,
128
+ 'b' => 2,
129
+ }.ya2yaml(
130
+ :hash_order => hash_order
131
+ )
132
+ assert_equal(
133
+ yaml,
134
+ y,
135
+ 'hash order should be kept when :hash_order provided'
136
+ )
137
+ }
138
+ end
139
+
140
+ def test_preserve_order
141
+ h = {}
142
+ h['a'] = 1
143
+ h['c'] = 3
144
+ h['b'] = 2
145
+ y = h.ya2yaml(
146
+ :preserve_order => true
147
+ )
148
+ assert_equal(
149
+ "--- \na: 1\nc: 3\nb: 2\n",
150
+ y,
151
+ 'the natural hash order should be preserved'
152
+ )
153
+ end if RUBY_VERSION >= "1.9"
154
+
155
+ def test_normalize_line_breaks
156
+ [
157
+ ["\n\n\n\n", "--- \"\\n\\n\\n\\n\"\n"],
158
+ ["\r\n\r\n\r\n", "--- \"\\n\\n\\n\"\n"],
159
+ ["\r\n\n\n", "--- \"\\n\\n\\n\"\n"],
160
+ ["\n\r\n\n", "--- \"\\n\\n\\n\"\n"],
161
+ ["\n\n\r\n", "--- \"\\n\\n\\n\"\n"],
162
+ ["\n\n\n\r", "--- \"\\n\\n\\n\\n\"\n"],
163
+ ["\r\r\n\r", "--- \"\\n\\n\\n\"\n"],
164
+ ["\r\r\r\r", "--- \"\\n\\n\\n\\n\"\n"],
165
+ ["\r\xc2\x85\r\n", "--- \"\\n\\n\\n\"\n"],
166
+ ["\r\xe2\x80\xa8\r\n", "--- \"\\n\\L\\n\"\n"],
167
+ ["\r\xe2\x80\xa9\r\n", "--- \"\\n\\P\\n\"\n"],
168
+ ].each {|src, yaml|
169
+ y = src.ya2yaml(
170
+ :minimum_block_length => 16
171
+ )
172
+ assert_equal(
173
+ yaml,
174
+ y,
175
+ 'line breaks should be normalized to fit the format.'
176
+ )
177
+ }
178
+ end
179
+
180
+ def test_structs
181
+ [
182
+ [Struct.new('Hoge', :foo).new(123), "--- !ruby/struct:Hoge \n foo: 123\n", ],
183
+ [Struct.new(:foo).new(123), "--- !ruby/struct: \n foo: 123\n", ],
184
+ ].each {|src, yaml|
185
+ y = src.ya2yaml()
186
+ assert_equal(
187
+ yaml,
188
+ y,
189
+ 'ruby struct should be serialized properly'
190
+ )
191
+ }
192
+ end
193
+
194
+ def test_roundtrip_single_byte_char
195
+ ("\x00".."\x7f").each {|c|
196
+ y = c.ya2yaml()
197
+ r = YAML.load(y)
198
+ assert_equal(
199
+ (c == "\r" ? "\n" : c), # "\r" is normalized as "\n"
200
+ r,
201
+ 'single byte characters should round-trip properly'
202
+ )
203
+ }
204
+ end
205
+
206
+ def test_roundtrip_multi_byte_char
207
+ [
208
+ 0x80,
209
+ 0x85,
210
+ 0xa0,
211
+ 0x07ff,
212
+ 0x0800,
213
+ 0x0fff,
214
+ 0x1000,
215
+ 0x2028,
216
+ 0x2029,
217
+ 0xcfff,
218
+ 0xd000,
219
+ 0xd7ff,
220
+ 0xe000,
221
+ 0xfffd,
222
+ 0x10000,
223
+ 0x3ffff,
224
+ 0x40000,
225
+ 0xfffff,
226
+ 0x100000,
227
+ 0x10ffff,
228
+ ].each {|ucs_code|
229
+ [-1, 0, 1].each {|ofs|
230
+ (c = [ucs_code + ofs].pack('U'))
231
+ next unless c.valid_encoding? if c.respond_to? :valid_encoding?
232
+ c_hex = c.unpack('H8')
233
+ y = c.ya2yaml(
234
+ :escape_b_specific => true,
235
+ :escape_as_utf8 => true
236
+ )
237
+ r = YAML.load(y)
238
+ assert_equal(
239
+ (c == "\xc2\x85" ? "\n" : c), # "\N" is normalized as "\n"
240
+ r,
241
+ "multi byte characters #{c_hex} should round-trip properly"
242
+ )
243
+ }
244
+ }
245
+ end
246
+
247
+ def test_roundtrip_ambiguous_string
248
+ [
249
+ 'true',
250
+ 'false',
251
+ 'TRUE',
252
+ 'FALSE',
253
+ 'Y',
254
+ 'N',
255
+ 'y',
256
+ 'n',
257
+ 'on',
258
+ 'off',
259
+ true,
260
+ false,
261
+ '0b0101',
262
+ '-0b0101',
263
+ 0b0101,
264
+ -0b0101,
265
+ '031',
266
+ '-031',
267
+ 031,
268
+ -031,
269
+ '123.001e-1',
270
+ '123.01',
271
+ '123',
272
+ 123.001e-1,
273
+ 123.01,
274
+ 123,
275
+ '-123.001e-1',
276
+ '-123.01',
277
+ '-123',
278
+ -123.001e-1,
279
+ -123.01,
280
+ -123,
281
+ 'INF',
282
+ 'inf',
283
+ 'NaN',
284
+ 'nan',
285
+ '0xfe2a',
286
+ '-0xfe2a',
287
+ 0xfe2a,
288
+ -0xfe2a,
289
+ '1:23:32.0200',
290
+ '1:23:32',
291
+ '-1:23:32.0200',
292
+ '-1:23:32',
293
+ '<<',
294
+ '~',
295
+ 'null',
296
+ 'nUll',
297
+ 'Null',
298
+ 'NULL',
299
+ '',
300
+ nil,
301
+ '2006-09-12',
302
+ '2006-09-11T17:28:07Z',
303
+ '2006-09-11T17:28:07+09:00',
304
+ '2006-09-11 17:28:07.662694 +09:00',
305
+ '=',
306
+ ].each {|c|
307
+ ['', 'hoge'].each {|ext|
308
+ src = (c.class == String) ? (c + ext) : c
309
+ y = src.ya2yaml(
310
+ :escape_as_utf8 => true
311
+ )
312
+ r = YAML.load(y)
313
+ assert_equal(
314
+ src,
315
+ r,
316
+ 'ambiguous elements should round-trip properly'
317
+ )
318
+ }
319
+ }
320
+ end
321
+
322
+ def test_roundtrip_string
323
+ chars = "aあ\t\-\?,\[\{\#&\*!\|>'\"\%\@\`.\\ \n\xc2\xa0\xe2\x80\xa8".split('')
324
+
325
+ chars.each {|i|
326
+ chars.each {|j|
327
+ chars.each {|k|
328
+ src = i + j + k
329
+ y = src.ya2yaml(
330
+ :printable_with_syck => true,
331
+ :escape_b_specific => true,
332
+ :escape_as_utf8 => true
333
+ )
334
+ r = YAML.load(y)
335
+ assert_equal(
336
+ src,
337
+ r,
338
+ 'string of special characters should round-trip properly'
339
+ )
340
+ }
341
+ }
342
+ }
343
+ end
344
+
345
+ # patch by pawel.j.radecki at gmail.com. thanks!
346
+ def test_roundtrip_symbols
347
+ symbol1 = :"Batman: The Dark Knight - Why So Serious?!"
348
+ result_symbol1 = YAML.load(symbol1.ya2yaml)
349
+ assert_equal(symbol1, result_symbol1)
350
+
351
+ symbol2 = :"Batman: The Dark Knight - \"Why So Serious?!\""
352
+ result_symbol2 = YAML.load(symbol2.ya2yaml)
353
+ assert_equal(symbol2, result_symbol2)
354
+
355
+ # # YAML.load problem: the quotes within the symbol are lost here
356
+ # symbol3 = :"\"Batman: The Dark Knight - Why So Serious?!\""
357
+ # result_symbol3 = YAML.load(symbol3.ya2yaml)
358
+ # assert_equal(symbol3, result_symbol3)
359
+ end
360
+
361
+ def test_roundtrip_types
362
+ objects = [
363
+ [],
364
+ [1],
365
+ {},
366
+ {'foo' => 'bar'},
367
+ nil,
368
+ 'hoge',
369
+ "abc\nxyz\n",
370
+ (s = "\xff\xff"),
371
+ true,
372
+ false,
373
+ 1000,
374
+ 1000.1,
375
+ -1000,
376
+ -1000.1,
377
+ Date.new(2009, 2, 9),
378
+ Time.local(2009, 2, 9, 16, 35, 22),
379
+ :foo,
380
+ 1..10,
381
+ /abc\nxyz/i,
382
+ @struct,
383
+ @klass,
384
+ ]
385
+ s.force_encoding("BINARY") if s.respond_to? :force_encoding
386
+
387
+ objects.each {|obj|
388
+ src = case obj.class.to_s
389
+ when 'Array'
390
+ (obj.length) == 0 ? [] : objects
391
+ when 'Hash'
392
+ if (obj.length) == 0
393
+ {}
394
+ else
395
+ h = {}
396
+ c = 0
397
+ objects.each {|val|
398
+ h[c] = {}
399
+ objects.each {|key|
400
+ h[c][key] = val unless (key.class == Hash || key.class == Moo)
401
+ }
402
+ c += 1
403
+ }
404
+ h
405
+ end
406
+ else
407
+ obj
408
+ end
409
+ y = src.ya2yaml(
410
+ :syck_compatible => true
411
+ )
412
+
413
+ r = YAML.load(y)
414
+ assert_equal(
415
+ src,
416
+ r,
417
+ 'types other than String should round-trip properly'
418
+ )
419
+ }
420
+ end
421
+
422
+ def test_roundtrip_various
423
+ [
424
+ [1, 2, ['c', 'd', [[['e']], []], 'f'], 3, Time.local(2009, 2, 9, 17, 9), [[:foo]], nil, true, false, [], {}, {[123, 223]=>456}, {[1]=>2, 'a'=>'b', 'c' => [9, 9, 9], Time.local(2009, 2, 9, 17, 10) => 'hoge'}, ],
425
+ [],
426
+ {[123, 223]=>456},
427
+ {},
428
+ {'foo' => {1 => {2=>3, 4=>5}, 6 => [7, 8]}},
429
+ "abc",
430
+ " abc\n def\ndef\ndef\ndef\ndef\n",
431
+ "abc\n def\ndef\n",
432
+ "abc\n def\ndef\n\n",
433
+ "abc\n def\ndef\n\n ",
434
+ "abc\n def\ndef\n \n",
435
+ "abc\n def\ndef \n \n",
436
+ "abc\n def\ndef \n \n ",
437
+ ' ほげほげほげ',
438
+ {"ほげ\nほげ\n ほげ" => 123},
439
+ [["ほげ\nほげ\n ほげ"]],
440
+ "ほげh\x4fge\nほげ\nほげ",
441
+ [{'ほげ'=>'abc', "ほげ\nほげ"=>'ほげ'}, 'ほげ', @text],
442
+ [Date.today, -9.011, 0.023, 4, -5, {1=>-2, -1=>@text, '_foo'=>'bar', 'ぬお-ぬお'=>321}],
443
+ {1=>-2, -1=>@gif, '_foo'=>'bar', 'ぬお-ぬお'=>321},
444
+ ].each {|src|
445
+ y = src.ya2yaml(
446
+ :syck_compatible => true
447
+ )
448
+
449
+ r = YAML.load(y)
450
+ assert_equal(
451
+ src,
452
+ r,
453
+ 'various types should round-trip properly'
454
+ )
455
+ }
456
+ end
457
+
458
+ def test_circular_reference
459
+ a = []
460
+ a << a
461
+ assert_raise(
462
+ ArgumentError,
463
+ 'Object#ya2yaml should raise ArgumentError when the object includes a circular reference'
464
+ ) {
465
+ a.ya2yaml
466
+ }
467
+ end
468
+
469
+ def test_binary
470
+ return if RUBY_VERSION < '1.9.0'
471
+
472
+ y = nil
473
+ assert_nothing_raised(
474
+ "Ya2YAML#string_type should dump 'ASCII-8BIT' strings as '!binary'"
475
+ ) {
476
+ y = '日本語'.force_encoding('ASCII-8BIT').ya2yaml
477
+ }
478
+ assert_equal(
479
+ "--- !binary |\n 5pel5pys6Kqe\n\n",
480
+ y,
481
+ "Ya2YAML#string_type should dump 'ASCII-8BIT' strings as '!binary'"
482
+ )
483
+
484
+ assert_nothing_raised(
485
+ "Ya2YAML#string_type should dump strings with invalid encodings as '!binary'"
486
+ ) {
487
+ y = '日本語'.encode('EUC-JP').force_encoding('UTF-8').ya2yaml
488
+ }
489
+ assert_equal(
490
+ "--- !binary |\n xvzL3Ljs\n\n",
491
+ y,
492
+ "Ya2YAML#string_type should dump strings with invalid encodings as '!binary'"
493
+ )
494
+ end
495
+
496
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: edouard-ya2yaml
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.30.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Akira FUNAI
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2010-08-28 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! 'Ya2YAML is "yet another to_yaml". It emits YAML document with complete
15
+ UTF8 support (string/binary detection, "\u" escape sequences and Unicode specific
16
+ line breaks).
17
+
18
+ '
19
+ email: akira@funai.com
20
+ executables: []
21
+ extensions: []
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - lib/ya2yaml.rb
26
+ - README.rdoc
27
+ - LICENSE
28
+ - test/t.gif
29
+ - test/t.yaml
30
+ - test/test.rb
31
+ homepage: http://rubyforge.org/projects/ya2yaml/
32
+ licenses: []
33
+ post_install_message:
34
+ rdoc_options:
35
+ - --main
36
+ - README.rdoc
37
+ - --charset
38
+ - UTF8
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>'
45
+ - !ruby/object:Gem::Version
46
+ version: 0.0.0
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project: ya2yaml
55
+ rubygems_version: 1.8.15
56
+ signing_key:
57
+ specification_version: 1
58
+ summary: An UTF8 safe YAML dumper.
59
+ test_files:
60
+ - test/test.rb