kch-ya2yaml 0.29.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 +114 -0
  3. data/lib/ya2yaml.rb +371 -0
  4. data/test/t.gif +0 -0
  5. data/test/t.yaml +5 -0
  6. data/test/test.rb +413 -0
  7. metadata +62 -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 ADDED
@@ -0,0 +1,114 @@
1
+ = Ya2YAML - An UTF8 safe YAML dumper
2
+
3
+ Project Contact: Akira FUNAI <mailto:funai.akira@gmail.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
+ )
71
+
72
+ # or simply set this for a safe roundtrip with Syck.
73
+ obj.ya2yaml(:syck_compatible => true)
74
+
75
+ **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.
76
+
77
+ - :indent_size
78
+ - default: 2
79
+ - Number of indentation spaces for a level.
80
+
81
+ - :hash_order
82
+ - default: nil
83
+ - Array of hash keys indicating sort order (this option only works on a top-level hash).
84
+
85
+ - :minimum_block_length
86
+ - default: 0
87
+ - 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.
88
+
89
+ - :printable_with_syck
90
+ - default: false
91
+ - When set to true, Ya2YAML will regard some kind of strings (which cause Syck roundtrip failures) as "non-printable" and double-quote them.
92
+
93
+ - :escape_b_specific
94
+ - default: false
95
+ - When set to true, Ya2YAML will regard Unicode specific line breaks (LS and PS) as "non-printable" and escape them.
96
+
97
+ - :escape_as_utf8
98
+ - default: false
99
+ - 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).
100
+
101
+ - :syck_compatible
102
+ - default: false
103
+ - When set to true, These options are set to true at once. You have to set this to false when you set them individually.
104
+ - :printable_with_syck
105
+ - :escape_b_specific
106
+ - :escape_as_utf8
107
+
108
+ == More information
109
+
110
+ Visit http://rubyforge.org/projects/ya2yaml
111
+
112
+ == License
113
+
114
+ Ya2YAML is distributed with a MIT license, which can be found in the file LICENSE.
@@ -0,0 +1,371 @@
1
+ # encoding: UTF-8
2
+
3
+ # $Id: ya2yaml.rb,v 0.29 2009/02/09 09:01:30 funai Exp funai $
4
+ #
5
+ # Author:: Akira FUNAI
6
+ # Copyright:: Copyright (c) 2006 Akira FUNAI
7
+ # License:: MIT License
8
+
9
+ class Ya2YAML
10
+
11
+ def initialize(opts = {})
12
+ options = opts.dup
13
+ options[:indent_size] = 2 if options[:indent_size].to_i <= 0
14
+ options[:minimum_block_length] = 0 if options[:minimum_block_length].to_i <= 0
15
+ options.update(
16
+ {
17
+ :printable_with_syck => true,
18
+ :escape_b_specific => true,
19
+ :escape_as_utf8 => true,
20
+ }
21
+ ) if options[:syck_compatible]
22
+
23
+ @options = options
24
+ end
25
+
26
+ def _ya2yaml(obj)
27
+ throw 'set $KCODE to "UTF8".' if $KCODE != 'UTF8' unless RUBY_VERSION >= '1.9.0'
28
+ '--- ' + emit(obj,1) + "\n"
29
+ end
30
+
31
+ private
32
+
33
+ def emit(obj,level)
34
+ case obj.class.to_s
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
+ else
58
+ hash_keys = obj.keys.sort {|x,y| x.to_s <=> y.to_s }
59
+ end
60
+ hash_keys.collect {|k|
61
+ key = emit(k,level + 1)
62
+ if (
63
+ is_one_plain_line?(key) ||
64
+ key =~ /\A(#{REX_BOOL}|#{REX_FLOAT}|#{REX_INT}|#{REX_NULL})\z/x
65
+ )
66
+ indent + key + ': ' + emit(obj[k],level + 1)
67
+ else
68
+ indent + '? ' + key +
69
+ indent + ': ' + emit(obj[k],level + 1)
70
+ end
71
+ }.join('')
72
+ end
73
+ when 'NilClass'
74
+ '~'
75
+ when 'String'
76
+ emit_string(obj,level)
77
+ when 'TrueClass','FalseClass'
78
+ obj.to_s
79
+ when 'Fixnum','Bignum','Float'
80
+ obj.to_s
81
+ when 'Date'
82
+ obj.to_s
83
+ when 'Time'
84
+ offset = obj.gmtoff
85
+ off_hm = sprintf(
86
+ '%+.2d:%.2d',
87
+ (offset / 3600.0).to_i,
88
+ (offset % 3600.0) / 60
89
+ )
90
+ u_sec = (obj.usec != 0) ? sprintf(".%.6d",obj.usec) : ''
91
+ obj.strftime("%Y-%m-%d %H:%M:%S#{u_sec} #{off_hm}")
92
+ when 'Symbol'
93
+ '!ruby/symbol ' + emit_string(obj.to_s,level)
94
+ when 'Range'
95
+ '!ruby/range ' + obj.to_s
96
+ when 'Regexp'
97
+ '!ruby/regexp ' + obj.inspect
98
+ else
99
+ case
100
+ when obj.is_a?(Struct)
101
+ struct_members = {}
102
+ obj.each_pair{|k,v| struct_members[k.to_s] = v }
103
+ '!ruby/struct:' + obj.class.to_s.sub(/^(Struct::(.+)|.*)$/,'\2') + ' ' +
104
+ emit(struct_members,level + 1)
105
+ else
106
+ # serialized as a generic object
107
+ object_members = {}
108
+ obj.instance_variables.each{|k,v|
109
+ object_members[k.to_s.sub(/^@/,'')] = obj.instance_variable_get(k)
110
+ }
111
+ '!ruby/object:' + obj.class.to_s + ' ' +
112
+ emit(object_members,level + 1)
113
+ end
114
+ end
115
+ end
116
+
117
+ def emit_string(str,level)
118
+ (is_string,is_printable,is_one_line,is_one_plain_line) = string_type(str)
119
+ if is_string
120
+ if is_printable
121
+ if is_one_plain_line
122
+ emit_simple_string(str,level)
123
+ else
124
+ (is_one_line || str.length < @options[:minimum_block_length]) ?
125
+ emit_quoted_string(str,level) :
126
+ emit_block_string(str,level)
127
+ end
128
+ else
129
+ emit_quoted_string(str,level)
130
+ end
131
+ else
132
+ emit_base64_binary(str,level)
133
+ end
134
+ end
135
+
136
+ def emit_simple_string(str,level)
137
+ str
138
+ end
139
+
140
+ def emit_block_string(str,level)
141
+ str = normalize_line_break(str)
142
+
143
+ indent = s_indent(level)
144
+ indentation_indicator = (str =~ /\A /) ? indent.size.to_s : ''
145
+ str =~ /(#{REX_NORMAL_LB}*)\z/
146
+ chomping_indicator = case $1.length
147
+ when 0
148
+ '-'
149
+ when 1
150
+ ''
151
+ else
152
+ '+'
153
+ end
154
+
155
+ str.chomp!
156
+ str.gsub!(/#{REX_NORMAL_LB}/) {
157
+ $1 + indent
158
+ }
159
+ '|' + indentation_indicator + chomping_indicator + "\n" + indent + str
160
+ end
161
+
162
+ def emit_quoted_string(str,level)
163
+ str = yaml_escape(normalize_line_break(str))
164
+ if (str.length < @options[:minimum_block_length])
165
+ str.gsub!(/#{REX_NORMAL_LB}/) { ESCAPE_SEQ_LB[$1] }
166
+ else
167
+ str.gsub!(/#{REX_NORMAL_LB}$/) { ESCAPE_SEQ_LB[$1] }
168
+ str.gsub!(/(#{REX_NORMAL_LB}+)(.)/) {
169
+ trail_c = $3
170
+ $1 + trail_c.sub(/([\t ])/) { ESCAPE_SEQ_WS[$1] }
171
+ }
172
+ indent = s_indent(level)
173
+ str.gsub!(/#{REX_NORMAL_LB}/) {
174
+ ESCAPE_SEQ_LB[$1] + "\\\n" + indent
175
+ }
176
+ end
177
+ '"' + str + '"'
178
+ end
179
+
180
+ def emit_base64_binary(str,level)
181
+ indent = "\n" + s_indent(level)
182
+ base64 = [str].pack('m')
183
+ '!binary |' + indent + base64.gsub(/\n(?!\z)/,indent)
184
+ end
185
+
186
+ def string_type(str)
187
+ if str.respond_to?(:valid_encoding?) && !str.valid_encoding?
188
+ return false,false,false,false
189
+ end
190
+ (ucs_codes = str.unpack('U*')) rescue (
191
+ # ArgumentError -> binary data
192
+ return false,false,false,false
193
+ )
194
+ if (
195
+ @options[:printable_with_syck] &&
196
+ str =~ /\A#{REX_ANY_LB}* | #{REX_ANY_LB}*\z|#{REX_ANY_LB}{2}\z/
197
+ )
198
+ # detour Syck bug
199
+ return true,false,nil,false
200
+ end
201
+ ucs_codes.each {|ucs_code|
202
+ return true,false,nil,false unless is_printable?(ucs_code)
203
+ }
204
+ return true,true,is_one_line?(str),is_one_plain_line?(str)
205
+ end
206
+
207
+ def is_printable?(ucs_code)
208
+ # YAML 1.1 / 4.1.1.
209
+ (
210
+ [0x09,0x0a,0x0d,0x85].include?(ucs_code) ||
211
+ (ucs_code <= 0x7e && ucs_code >= 0x20) ||
212
+ (ucs_code <= 0xd7ff && ucs_code >= 0xa0) ||
213
+ (ucs_code <= 0xfffd && ucs_code >= 0xe000) ||
214
+ (ucs_code <= 0x10ffff && ucs_code >= 0x10000)
215
+ ) &&
216
+ !(
217
+ # treat LS/PS as non-printable characters
218
+ @options[:escape_b_specific] &&
219
+ (ucs_code == 0x2028 || ucs_code == 0x2029)
220
+ )
221
+ end
222
+
223
+ def is_one_line?(str)
224
+ str !~ /#{REX_ANY_LB}(?!\z)/
225
+ end
226
+
227
+ def is_one_plain_line?(str)
228
+ # YAML 1.1 / 4.6.11.
229
+ str !~ /^([\-\?:,\[\]\{\}\#&\*!\|>'"%@`\s]|---|\.\.\.)/ &&
230
+ str !~ /[:\#\s\[\]\{\},]/ &&
231
+ str !~ /#{REX_ANY_LB}/ &&
232
+ str !~ /^(#{REX_BOOL}|#{REX_FLOAT}|#{REX_INT}|#{REX_MERGE}
233
+ |#{REX_NULL}|#{REX_TIMESTAMP}|#{REX_VALUE})$/x
234
+ end
235
+
236
+ def s_indent(level)
237
+ # YAML 1.1 / 4.2.2.
238
+ ' ' * (level * @options[:indent_size])
239
+ end
240
+
241
+ def normalize_line_break(str)
242
+ # YAML 1.1 / 4.1.4.
243
+ str.gsub(/(#{REX_CRLF}|#{REX_CR}|#{REX_NEL})/,"\n")
244
+ end
245
+
246
+ def yaml_escape(str)
247
+ # YAML 1.1 / 4.1.6.
248
+ str.gsub(/[^a-zA-Z0-9]/u) {|c|
249
+ ucs_code, = (c.unpack('U') rescue [??])
250
+ case
251
+ when ESCAPE_SEQ[c]
252
+ ESCAPE_SEQ[c]
253
+ when is_printable?(ucs_code)
254
+ c
255
+ when @options[:escape_as_utf8]
256
+ c.bytes.collect {|b| '\\x%.2x' % b }.join
257
+ when ucs_code == 0x2028 || ucs_code == 0x2029
258
+ ESCAPE_SEQ_LB[c]
259
+ when ucs_code <= 0x7f
260
+ sprintf('\\x%.2x',ucs_code)
261
+ when ucs_code <= 0xffff
262
+ sprintf('\\u%.4x',ucs_code)
263
+ else
264
+ sprintf('\\U%.8x',ucs_code)
265
+ end
266
+ }
267
+ end
268
+
269
+ module Constants
270
+ UCS_0X85 = [0x85].pack('U') # c285@UTF8 Unicode next line
271
+ UCS_0XA0 = [0xa0].pack('U') # c2a0@UTF8 Unicode non-breaking space
272
+ UCS_0X2028 = [0x2028].pack('U') # e280a8@UTF8 Unicode line separator
273
+ UCS_0X2029 = [0x2029].pack('U') # e280a9@UTF8 Unicode paragraph separator
274
+
275
+ # non-break characters
276
+ ESCAPE_SEQ = {
277
+ "\x00" => '\\0',
278
+ "\x07" => '\\a',
279
+ "\x08" => '\\b',
280
+ "\x0b" => '\\v',
281
+ "\x0c" => '\\f',
282
+ "\x1b" => '\\e',
283
+ "\"" => '\\"',
284
+ "\\" => '\\\\',
285
+ }
286
+
287
+ # non-breaking space
288
+ ESCAPE_SEQ_NS = {
289
+ UCS_0XA0 => '\\_',
290
+ }
291
+
292
+ # white spaces
293
+ ESCAPE_SEQ_WS = {
294
+ "\x09" => '\\t',
295
+ " " => '\\x20',
296
+ }
297
+
298
+ # line breaks
299
+ ESCAPE_SEQ_LB ={
300
+ "\x0a" => '\\n',
301
+ "\x0d" => '\\r',
302
+ UCS_0X85 => '\\N',
303
+ UCS_0X2028 => '\\L',
304
+ UCS_0X2029 => '\\P',
305
+ }
306
+
307
+ # regexps for line breaks
308
+ REX_LF = Regexp.escape("\x0a")
309
+ REX_CR = Regexp.escape("\x0d")
310
+ REX_CRLF = Regexp.escape("\x0d\x0a")
311
+ REX_NEL = Regexp.escape(UCS_0X85)
312
+ REX_LS = Regexp.escape(UCS_0X2028)
313
+ REX_PS = Regexp.escape(UCS_0X2029)
314
+
315
+ REX_ANY_LB = /(#{REX_LF}|#{REX_CR}|#{REX_NEL}|#{REX_LS}|#{REX_PS})/
316
+ REX_NORMAL_LB = /(#{REX_LF}|#{REX_LS}|#{REX_PS})/
317
+
318
+ # regexps for language-Independent types for YAML1.1
319
+ REX_BOOL = /
320
+ y|Y|yes|Yes|YES|n|N|no|No|NO
321
+ |true|True|TRUE|false|False|FALSE
322
+ |on|On|ON|off|Off|OFF
323
+ /x
324
+ REX_FLOAT = /
325
+ [-+]?([0-9][0-9_]*)?\.[0-9.]*([eE][-+][0-9]+)? # (base 10)
326
+ |[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\.[0-9_]* # (base 60)
327
+ |[-+]?\.(inf|Inf|INF) # (infinity)
328
+ |\.(nan|NaN|NAN) # (not a number)
329
+ /x
330
+ REX_INT = /
331
+ [-+]?0b[0-1_]+ # (base 2)
332
+ |[-+]?0[0-7_]+ # (base 8)
333
+ |[-+]?(0|[1-9][0-9_]*) # (base 10)
334
+ |[-+]?0x[0-9a-fA-F_]+ # (base 16)
335
+ |[-+]?[1-9][0-9_]*(:[0-5]?[0-9])+ # (base 60)
336
+ /x
337
+ REX_MERGE = /
338
+ <<
339
+ /x
340
+ REX_NULL = /
341
+ ~ # (canonical)
342
+ |null|Null|NULL # (English)
343
+ | # (Empty)
344
+ /x
345
+ REX_TIMESTAMP = /
346
+ [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] # (ymd)
347
+ |[0-9][0-9][0-9][0-9] # (year)
348
+ -[0-9][0-9]? # (month)
349
+ -[0-9][0-9]? # (day)
350
+ ([Tt]|[ \t]+)[0-9][0-9]? # (hour)
351
+ :[0-9][0-9] # (minute)
352
+ :[0-9][0-9] # (second)
353
+ (\.[0-9]*)? # (fraction)
354
+ (([ \t]*)Z|[-+][0-9][0-9]?(:[0-9][0-9])?)? # (time zone)
355
+ /x
356
+ REX_VALUE = /
357
+ =
358
+ /x
359
+ end
360
+
361
+ include Constants
362
+
363
+ end
364
+
365
+ class Object
366
+ def ya2yaml(options = {})
367
+ Ya2YAML.new(options)._ya2yaml(self)
368
+ end
369
+ end
370
+
371
+ __END__
Binary file
@@ -0,0 +1,5 @@
1
+ ---
2
+ list:
3
+ - perl
4
+ - ruby
5
+ - python
@@ -0,0 +1,413 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # $Id: test.rb,v 1.6 2009/02/09 09:00:57 funai Exp funai $
5
+
6
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
7
+
8
+ Dir.chdir(File.dirname(__FILE__))
9
+
10
+ $KCODE = 'UTF8' if RUBY_VERSION < '1.9.0'
11
+ require 'ya2yaml'
12
+
13
+ require 'yaml'
14
+ require 'test/unit'
15
+
16
+ # There's an incompatibility in how ruby handles struct dumps
17
+ # between versions that's beyond our scope.
18
+ # (One uses strings internally, the other symbols.)
19
+ # This just enables tests to pass.
20
+ class << Struct
21
+ alias yaml_new_without_indifferent_keys yaml_new
22
+ def yaml_new(klass, tag, val)
23
+ val.keys.each { |k, v| val[k.to_sym] = val.delete(k) }
24
+ yaml_new_without_indifferent_keys(klass, tag, val)
25
+ end
26
+ end if RUBY_VERSION >= "1.9"
27
+
28
+ class TC_Ya2YAML < Test::Unit::TestCase
29
+
30
+ @@struct_klass = Struct::new('Foo',:bar,:buz)
31
+ class Moo
32
+ attr_accessor :val1,:val2
33
+ def initialize(val1,val2)
34
+ @val1 = val1
35
+ @val2 = val2
36
+ end
37
+ def ==(k)
38
+ (k.class == self.class) &&
39
+ (k.val1 == self.val1) &&
40
+ (k.val2 == self.val2)
41
+ end
42
+ end
43
+ puts "test may take minutes. please wait.\n"
44
+
45
+ def setup
46
+ @text = File.open('./t.yaml', 'r') { |f| f.read }
47
+ @gif = File.open('./t.gif', 'rb') { |f| f.read }
48
+ @struct = @@struct_klass.new('barbarbar', @@struct_klass.new('baaaar', 12345))
49
+ @klass = Moo.new('boobooboo', Time.local(2009,2,9,16,44,10))
50
+ end
51
+
52
+ def test_options
53
+ opt = {:syck_compatible => true}
54
+ 'foobar'.ya2yaml(opt)
55
+ assert_equal(
56
+ {:syck_compatible => true},
57
+ opt,
58
+ 'ya2yaml should not change the option hash'
59
+ )
60
+
61
+ [
62
+ [
63
+ {},
64
+ "--- \n- \"\\u0086\"\n- |-\n a\xe2\x80\xa8 b\xe2\x80\xa9 c\n- |4-\n abc\n xyz\n",
65
+ ],
66
+ [
67
+ {:indent_size => 4},
68
+ "--- \n- \"\\u0086\"\n- |-\n a\xe2\x80\xa8 b\xe2\x80\xa9 c\n- |8-\n abc\n xyz\n",
69
+ ],
70
+ [
71
+ {:minimum_block_length => 16},
72
+ "--- \n- \"\\u0086\"\n- \"a\\Lb\\Pc\"\n- \" abc\\nxyz\"\n",
73
+ ],
74
+ # [
75
+ # {:emit_c_document_end => true},
76
+ # "--- \n- \"\\u0086\"\n- |-\n a\xe2\x80\xa8 b\xe2\x80\xa9 c\n- |4-\n abc\n xyz\n...\n",
77
+ # ],
78
+ [
79
+ {:printable_with_syck => true},
80
+ "--- \n- \"\\u0086\"\n- |-\n a\xe2\x80\xa8 b\xe2\x80\xa9 c\n- \" abc\\n\\\n xyz\"\n",
81
+ ],
82
+ [
83
+ {:escape_b_specific => true},
84
+ "--- \n- \"\\u0086\"\n- \"a\\Lb\\Pc\"\n- |4-\n abc\n xyz\n",
85
+ ],
86
+ [
87
+ {:escape_as_utf8 => true},
88
+ "--- \n- \"\\xc2\\x86\"\n- |-\n a\xe2\x80\xa8 b\xe2\x80\xa9 c\n- |4-\n abc\n xyz\n",
89
+ ],
90
+ [
91
+ {:syck_compatible => true},
92
+ "--- \n- \"\\xc2\\x86\"\n- \"a\\xe2\\x80\\xa8b\\xe2\\x80\\xa9c\"\n- \" abc\\n\\\n xyz\"\n",
93
+ ],
94
+ ].each {|opt,yaml|
95
+ y = ["\xc2\x86","a\xe2\x80\xa8b\xe2\x80\xa9c"," abc\nxyz"].ya2yaml(opt)
96
+ assert_equal(
97
+ yaml,
98
+ y,
99
+ "option #{opt.inspect} should be recognized"
100
+ )
101
+ }
102
+ end
103
+
104
+ def test_hash_order
105
+ [
106
+ [
107
+ nil,
108
+ "--- \na: 1\nb: 2\nc: 3\n",
109
+ ],
110
+ [
111
+ [],
112
+ "--- \na: 1\nb: 2\nc: 3\n",
113
+ ],
114
+ [
115
+ ['c','b','a'],
116
+ "--- \nc: 3\nb: 2\na: 1\n",
117
+ ],
118
+ [
119
+ ['b'],
120
+ "--- \nb: 2\na: 1\nc: 3\n",
121
+ ],
122
+ ].each {|hash_order,yaml|
123
+ y = {
124
+ 'a' => 1,
125
+ 'c' => 3,
126
+ 'b' => 2,
127
+ }.ya2yaml(
128
+ :hash_order => hash_order
129
+ )
130
+ assert_equal(
131
+ yaml,
132
+ y,
133
+ 'hash order should be kept when :hash_order provided'
134
+ )
135
+ }
136
+ end
137
+
138
+ def test_normalize_line_breaks
139
+ [
140
+ ["\n\n\n\n", "--- \"\\n\\n\\n\\n\"\n",],
141
+ ["\r\n\r\n\r\n", "--- \"\\n\\n\\n\"\n",],
142
+ ["\r\n\n\n", "--- \"\\n\\n\\n\"\n",],
143
+ ["\n\r\n\n", "--- \"\\n\\n\\n\"\n",],
144
+ ["\n\n\r\n", "--- \"\\n\\n\\n\"\n",],
145
+ ["\n\n\n\r", "--- \"\\n\\n\\n\\n\"\n",],
146
+ ["\r\r\n\r", "--- \"\\n\\n\\n\"\n",],
147
+ ["\r\r\r\r", "--- \"\\n\\n\\n\\n\"\n",],
148
+ ["\r\xc2\x85\r\n", "--- \"\\n\\n\\n\"\n",],
149
+ ["\r\xe2\x80\xa8\r\n","--- \"\\n\\L\\n\"\n",],
150
+ ["\r\xe2\x80\xa9\r\n","--- \"\\n\\P\\n\"\n",],
151
+ ].each {|src,yaml|
152
+ y = src.ya2yaml(
153
+ :minimum_block_length => 16
154
+ )
155
+ assert_equal(
156
+ yaml,
157
+ y,
158
+ 'line breaks should be normalized to fit the format.'
159
+ )
160
+ }
161
+ end
162
+
163
+ def test_structs
164
+ [
165
+ [Struct.new('Hoge',:foo).new(123),"--- !ruby/struct:Hoge \n foo: 123\n",],
166
+ [Struct.new(:foo).new(123), "--- !ruby/struct: \n foo: 123\n",],
167
+ ].each {|src,yaml|
168
+ y = src.ya2yaml()
169
+ assert_equal(
170
+ yaml,
171
+ y,
172
+ 'ruby struct should be serialized properly'
173
+ )
174
+ }
175
+ end
176
+
177
+ def test_roundtrip_single_byte_char
178
+ ("\x00".."\x7f").each {|c|
179
+ y = c.ya2yaml()
180
+ r = YAML.load(y)
181
+ assert_equal(
182
+ (c == "\r" ? "\n" : c), # "\r" is normalized as "\n"
183
+ r,
184
+ 'single byte characters should round-trip properly'
185
+ )
186
+ }
187
+ end
188
+
189
+ def test_roundtrip_multi_byte_char
190
+ [ 0x000080, 0x000085, 0x0000a0, 0x0007ff, 0x000800, 0x000fff, 0x001000,
191
+ 0x002028, 0x002029, 0x00cfff, 0x00d000, 0x00d7ff, 0x00e000, 0x00fffd,
192
+ 0x010000, 0x03ffff, 0x040000, 0x0fffff, 0x100000, 0x10ffff,
193
+ ].each do |ucs_code|
194
+ [-1, 0, 1].each do |ofs|
195
+ (c = [ucs_code + ofs].pack('U'))
196
+ next unless c.valid_encoding? if c.respond_to? :valid_encoding?
197
+ c_hex = c.unpack('H8')
198
+ y = c.ya2yaml(
199
+ :escape_b_specific => true,
200
+ :escape_as_utf8 => true)
201
+ r = YAML.load(y)
202
+ assert_equal(
203
+ (c == "\xc2\x85" ? "\n" : c), # "\N" is normalized as "\n"
204
+ r,
205
+ "multi byte characters #{c_hex} should round-trip properly"
206
+ )
207
+ end
208
+ end
209
+ end
210
+
211
+ def test_roundtrip_ambiguous_string
212
+ [
213
+ 'true',
214
+ 'false',
215
+ 'TRUE',
216
+ 'FALSE',
217
+ 'Y',
218
+ 'N',
219
+ 'y',
220
+ 'n',
221
+ 'on',
222
+ 'off',
223
+ true,
224
+ false,
225
+ '0b0101',
226
+ '-0b0101',
227
+ 0b0101,
228
+ -0b0101,
229
+ '031',
230
+ '-031',
231
+ 031,
232
+ -031,
233
+ '123.001e-1',
234
+ '123.01',
235
+ '123',
236
+ 123.001e-1,
237
+ 123.01,
238
+ 123,
239
+ '-123.001e-1',
240
+ '-123.01',
241
+ '-123',
242
+ -123.001e-1,
243
+ -123.01,
244
+ -123,
245
+ 'INF',
246
+ 'inf',
247
+ 'NaN',
248
+ 'nan',
249
+ '0xfe2a',
250
+ '-0xfe2a',
251
+ 0xfe2a,
252
+ -0xfe2a,
253
+ '1:23:32.0200',
254
+ '1:23:32',
255
+ '-1:23:32.0200',
256
+ '-1:23:32',
257
+ '<<',
258
+ '~',
259
+ 'null',
260
+ 'nUll',
261
+ 'Null',
262
+ 'NULL',
263
+ '',
264
+ nil,
265
+ '2006-09-12',
266
+ '2006-09-11T17:28:07Z',
267
+ '2006-09-11T17:28:07+09:00',
268
+ '2006-09-11 17:28:07.662694 +09:00',
269
+ '=',
270
+ ].each {|c|
271
+ ['','hoge'].each {|ext|
272
+ src = (c.class == String) ? (c + ext) : c
273
+ y = src.ya2yaml(
274
+ :escape_as_utf8 => true
275
+ )
276
+ r = YAML.load(y)
277
+ assert_equal(
278
+ src,
279
+ r,
280
+ 'ambiguous elements should round-trip properly'
281
+ )
282
+ }
283
+ }
284
+ end
285
+
286
+ def test_roundtrip_string
287
+ chars = "aあ\t\-\?,\[\{\#&\*!\|>'\"\%\@\`.\\ \n\xc2\xa0\xe2\x80\xa8".split('')
288
+ f = lambda { |r| chars.map { |c| r.map { |rc| c + rc } }.flatten }
289
+ f[f[f[[""]]]].each do |src|
290
+ y = src.ya2yaml(
291
+ :printable_with_syck => true,
292
+ :escape_b_specific => true,
293
+ :escape_as_utf8 => true)
294
+ r = YAML.load(y)
295
+ assert_equal(src, r, 'string of special characters should round-trip properly')
296
+ end
297
+ end
298
+
299
+ # patch by pawel.j.radecki at gmail.com. thanks!
300
+ def test_roundtrip_symbols
301
+ symbol1 = :"Batman: The Dark Knight - Why So Serious?!"
302
+ result_symbol1 = YAML.load(symbol1.ya2yaml)
303
+ assert_equal(symbol1,result_symbol1)
304
+
305
+ symbol2 = :"Batman: The Dark Knight - \"Why So Serious?!\""
306
+ result_symbol2 = YAML.load(symbol2.ya2yaml)
307
+ assert_equal(symbol2,result_symbol2)
308
+
309
+ # # YAML.load problem: the quotes within the symbol are lost here
310
+ # symbol3 = :"\"Batman: The Dark Knight - Why So Serious?!\""
311
+ # result_symbol3 = YAML.load(symbol3.ya2yaml)
312
+ # assert_equal(symbol3,result_symbol3)
313
+ end
314
+
315
+ def test_roundtrip_types
316
+ objects = [
317
+ [],
318
+ [1],
319
+ {},
320
+ {'foo' => 'bar'},
321
+ nil,
322
+ 'hoge',
323
+ "abc\nxyz\n",
324
+ (s = "\xff\xff"),
325
+ true,
326
+ false,
327
+ 1000,
328
+ 1000.1,
329
+ -1000,
330
+ -1000.1,
331
+ Date.new(2009,2,9),
332
+ Time.local(2009,2,9,16,35,22),
333
+ :foo,
334
+ 1..10,
335
+ /abc\nxyz/i,
336
+ @struct,
337
+ @klass,
338
+ ]
339
+ s.force_encoding("BINARY") if s.respond_to? :force_encoding
340
+
341
+ objects.each {|obj|
342
+ src = case obj.class.to_s
343
+ when 'Array'
344
+ (obj.length) == 0 ? [] : objects
345
+ when 'Hash'
346
+ if (obj.length) == 0
347
+ {}
348
+ else
349
+ h = {}
350
+ c = 0
351
+ objects.each {|val|
352
+ h[c] = {}
353
+ objects.each {|key|
354
+ h[c][key] = val unless (key.class == Hash || key.class == Moo)
355
+ }
356
+ c += 1
357
+ }
358
+ h
359
+ end
360
+ else
361
+ obj
362
+ end
363
+ y = src.ya2yaml(
364
+ :syck_compatible => true
365
+ )
366
+
367
+ r = YAML.load(y)
368
+ assert_equal(
369
+ src,
370
+ r,
371
+ 'types other than String should round-trip properly'
372
+ )
373
+ }
374
+ end
375
+
376
+ def test_roundtrip_various
377
+ [
378
+ [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'},],
379
+ [],
380
+ {[123,223]=>456},
381
+ {},
382
+ {'foo' => {1 => {2=>3,4=>5},6 => [7,8]}},
383
+ "abc",
384
+ " abc\n def\ndef\ndef\ndef\ndef\n",
385
+ "abc\n def\ndef\n",
386
+ "abc\n def\ndef\n\n",
387
+ "abc\n def\ndef\n\n ",
388
+ "abc\n def\ndef\n \n",
389
+ "abc\n def\ndef \n \n",
390
+ "abc\n def\ndef \n \n ",
391
+ ' ほげほげほげ',
392
+ {"ほげ\nほげ\n ほげ" => 123},
393
+ [["ほげ\nほげ\n ほげ"]],
394
+ "ほげh\x4fge\nほげ\nほげ",
395
+ [{'ほげ'=>'abc',"ほげ\nほげ"=>'ほげ'},'ほげ',@text],
396
+ [Date.today,-9.011,0.023,4,-5,{1=>-2,-1=>@text,'_foo'=>'bar','ぬお-ぬお'=>321}],
397
+ {1=>-2,-1=>@gif,'_foo'=>'bar','ぬお-ぬお'=>321},
398
+ ].each {|src|
399
+ y = src.ya2yaml(
400
+ :syck_compatible => true
401
+ )
402
+
403
+ r = YAML.load(y)
404
+ assert_equal(
405
+ src,
406
+ r,
407
+ 'various types should round-trip properly'
408
+ )
409
+ }
410
+ end
411
+
412
+ end
413
+
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kch-ya2yaml
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.29.1
5
+ platform: ruby
6
+ authors:
7
+ - Akira FUNAI
8
+ autorequire: ya2yaml
9
+ bindir: bin
10
+ cert_chain:
11
+ date: 2010-01-11 00:00:00 -02:00
12
+ default_executable:
13
+ dependencies: []
14
+
15
+ description:
16
+ email: funai.akira@gmail.com
17
+ executables: []
18
+
19
+ extensions: []
20
+
21
+ extra_rdoc_files:
22
+ - README
23
+ files:
24
+ - lib/ya2yaml.rb
25
+ - README
26
+ - LICENSE
27
+ - test/t.gif
28
+ - test/t.yaml
29
+ - test/test.rb
30
+ has_rdoc: true
31
+ homepage: http://rubyforge.org/projects/ya2yaml/
32
+ licenses: []
33
+
34
+ post_install_message:
35
+ rdoc_options:
36
+ - --main
37
+ - README
38
+ - --charset
39
+ - UTF8
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">"
45
+ - !ruby/object:Gem::Version
46
+ version: 0.0.0
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.3.5
58
+ signing_key:
59
+ specification_version: 1
60
+ summary: An UTF8 safe YAML dumper.
61
+ test_files:
62
+ - test/test.rb