syck 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.autotest.erb +8 -0
  2. data/.gemtest +0 -0
  3. data/CHANGELOG.rdoc +6 -0
  4. data/Manifest.txt +52 -0
  5. data/README.rdoc +51 -0
  6. data/Rakefile +32 -0
  7. data/ext/syck/bytecode.c +1165 -0
  8. data/ext/syck/emitter.c +1247 -0
  9. data/ext/syck/extconf.h +3 -0
  10. data/ext/syck/extconf.rb +5 -0
  11. data/ext/syck/gram.c +1894 -0
  12. data/ext/syck/gram.h +79 -0
  13. data/ext/syck/handler.c +173 -0
  14. data/ext/syck/implicit.c +2990 -0
  15. data/ext/syck/node.c +407 -0
  16. data/ext/syck/rubyext.c +2328 -0
  17. data/ext/syck/syck.c +524 -0
  18. data/ext/syck/syck.h +453 -0
  19. data/ext/syck/token.c +2724 -0
  20. data/ext/syck/yaml2byte.c +259 -0
  21. data/ext/syck/yamlbyte.h +171 -0
  22. data/lib/syck.bundle +0 -0
  23. data/lib/syck.rb +447 -0
  24. data/lib/syck/baseemitter.rb +242 -0
  25. data/lib/syck/basenode.rb +222 -0
  26. data/lib/syck/constants.rb +45 -0
  27. data/lib/syck/encoding.rb +35 -0
  28. data/lib/syck/error.rb +34 -0
  29. data/lib/syck/loader.rb +14 -0
  30. data/lib/syck/rubytypes.rb +450 -0
  31. data/lib/syck/stream.rb +41 -0
  32. data/lib/syck/stringio.rb +85 -0
  33. data/lib/syck/syck.rb +16 -0
  34. data/lib/syck/tag.rb +95 -0
  35. data/lib/syck/types.rb +192 -0
  36. data/lib/syck/yamlnode.rb +54 -0
  37. data/lib/syck/ypath.rb +54 -0
  38. data/lib/yaml/syck.rb +14 -0
  39. data/test/helper.rb +2 -0
  40. data/test/test_array.rb +13 -0
  41. data/test/test_boolean.rb +36 -0
  42. data/test/test_class.rb +11 -0
  43. data/test/test_exception.rb +45 -0
  44. data/test/test_hash.rb +24 -0
  45. data/test/test_null.rb +19 -0
  46. data/test/test_omap.rb +55 -0
  47. data/test/test_set.rb +30 -0
  48. data/test/test_string.rb +44 -0
  49. data/test/test_struct.rb +32 -0
  50. data/test/test_symbol.rb +21 -0
  51. data/test/test_time.rb +23 -0
  52. data/test/test_yaml.rb +1403 -0
  53. data/test/test_yaml_properties.rb +63 -0
  54. metadata +187 -0
@@ -0,0 +1,54 @@
1
+ #
2
+ # YAML::YPath
3
+ #
4
+
5
+ warn "#{caller[0]}: YAML::YPath is deprecated" if $VERBOSE
6
+
7
+ module Syck
8
+
9
+ class YPath
10
+ attr_accessor :segments, :predicates, :flags
11
+ def initialize( str )
12
+ @segments = []
13
+ @predicates = []
14
+ @flags = nil
15
+ while str =~ /^\/?(\/|[^\/\[]+)(?:\[([^\]]+)\])?/
16
+ @segments.push $1
17
+ @predicates.push $2
18
+ str = $'
19
+ end
20
+ unless str.to_s.empty?
21
+ @segments += str.split( "/" )
22
+ end
23
+ if @segments.length == 0
24
+ @segments.push "."
25
+ end
26
+ end
27
+ def self.each_path( str )
28
+ #
29
+ # Find choices
30
+ #
31
+ paths = []
32
+ str = "(#{ str })"
33
+ while str.sub!( /\(([^()]+)\)/, "\n#{ paths.length }\n" )
34
+ paths.push $1.split( '|' )
35
+ end
36
+
37
+ #
38
+ # Construct all possible paths
39
+ #
40
+ all = [ str ]
41
+ ( paths.length - 1 ).downto( 0 ) do |i|
42
+ all = all.collect do |a|
43
+ paths[i].collect do |p|
44
+ a.gsub( /\n#{ i }\n/, p )
45
+ end
46
+ end.flatten.uniq
47
+ end
48
+ all.collect do |path|
49
+ yield YPath.new( path )
50
+ end
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,14 @@
1
+ # $Id$
2
+ #
3
+ # = yaml/syck.rb:
4
+ #
5
+
6
+ require 'stringio'
7
+ require 'syck.so'
8
+ require 'syck/error'
9
+ require 'syck/syck'
10
+ require 'syck/tag'
11
+ require 'syck/stream'
12
+ require 'syck/constants'
13
+ require 'syck/rubytypes'
14
+ require 'syck/types'
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require 'syck'
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ module Syck
4
+ class TestArray < Test::Unit::TestCase
5
+ def setup
6
+ @list = [{ :a => 'b' }, 'foo']
7
+ end
8
+
9
+ def test_dump
10
+ assert_equal @list, Syck.load(Syck.dump(@list))
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,36 @@
1
+ require 'helper'
2
+
3
+ module Syck
4
+ ###
5
+ # Test booleans from YAML spec:
6
+ # http://yaml.org/type/bool.html
7
+ class TestBoolean < Test::Unit::TestCase
8
+ %w{ yes Yes YES true True TRUE on On ON }.each do |truth|
9
+ define_method(:"test_#{truth}") do
10
+ assert_equal true, Syck.load("--- #{truth}")
11
+ end
12
+ end
13
+
14
+ %w{ no No NO false False FALSE off Off OFF }.each do |truth|
15
+ define_method(:"test_#{truth}") do
16
+ assert_equal false, Syck.load("--- #{truth}")
17
+ end
18
+ end
19
+
20
+ ###
21
+ # YAML spec says "y" and "Y" may be used as true, but Syck treats them
22
+ # as literal strings
23
+ def test_y
24
+ assert_equal "y", Syck.load("--- y")
25
+ assert_equal "Y", Syck.load("--- Y")
26
+ end
27
+
28
+ ###
29
+ # YAML spec says "n" and "N" may be used as false, but Syck treats them
30
+ # as literal strings
31
+ def test_n
32
+ assert_equal "n", Syck.load("--- n")
33
+ assert_equal "N", Syck.load("--- N")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ require 'helper'
2
+
3
+ module Syck
4
+ class TestClass < Test::Unit::TestCase
5
+ def test_dump
6
+ assert_raises(::TypeError) do
7
+ Syck.dump TestClass
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,45 @@
1
+ require 'helper'
2
+
3
+ module Syck
4
+ class TestException < Test::Unit::TestCase
5
+ class Wups < Exception
6
+ attr_reader :foo, :bar
7
+ def initialize *args
8
+ super
9
+ @foo = 1
10
+ @bar = 2
11
+ end
12
+ end
13
+
14
+ def setup
15
+ @wups = Wups.new('test_message')
16
+ end
17
+
18
+ def test_to_yaml
19
+ w = Syck.load(Syck.dump(@wups))
20
+ assert_equal @wups, w
21
+ assert_equal 1, w.foo
22
+ assert_equal 2, w.bar
23
+ end
24
+
25
+ def test_dump
26
+ w = Syck.load(Syck.dump(@wups))
27
+ assert_equal @wups, w
28
+ assert_equal 1, w.foo
29
+ assert_equal 2, w.bar
30
+ end
31
+
32
+ def test_to_yaml_properties
33
+ class << @wups
34
+ def to_yaml_properties
35
+ [:@foo]
36
+ end
37
+ end
38
+
39
+ w = Syck.load(Syck.dump(@wups))
40
+ assert_equal @wups, w
41
+ assert_equal 1, w.foo
42
+ assert_nil w.bar
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,24 @@
1
+ require 'helper'
2
+
3
+ module Syck
4
+ class TestHash < Test::Unit::TestCase
5
+ def setup
6
+ @hash = { :a => 'b' }
7
+ end
8
+
9
+ def test_dump
10
+ assert_equal @hash, Syck.load(Syck.dump(@hash))
11
+ end
12
+
13
+ def test_ref_append
14
+ hash = Syck.load(<<-eoyml)
15
+ ---
16
+ foo: &foo
17
+ hello: world
18
+ bar:
19
+ <<: *foo
20
+ eoyml
21
+ assert_equal({"foo"=>{"hello"=>"world"}, "bar"=>{"hello"=>"world"}}, hash)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ require 'helper'
2
+
3
+ module Syck
4
+ ###
5
+ # Test null from YAML spec:
6
+ # http://yaml.org/type/null.html
7
+ class TestNull < Test::Unit::TestCase
8
+ def test_null_list
9
+ assert_equal [nil] * 5, Syck.load(<<-eoyml)
10
+ ---
11
+ - ~
12
+ - null
13
+ -
14
+ - Null
15
+ - NULL
16
+ eoyml
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,55 @@
1
+ require 'helper'
2
+
3
+ module Syck
4
+ class TestOmap < Test::Unit::TestCase
5
+ def test_keys
6
+ map = Syck::Omap.new
7
+ map['foo'] = 'bar'
8
+ assert_equal 'bar', map['foo']
9
+ end
10
+
11
+ def test_order
12
+ map = Syck::Omap.new
13
+ map['a'] = 'b'
14
+ map['b'] = 'c'
15
+ assert_equal [%w{a b}, %w{b c}], map.to_a
16
+ end
17
+
18
+ def test_square
19
+ list = [["a", "b"], ["b", "c"]]
20
+ map = Syck::Omap[*list.flatten]
21
+ assert_equal list, map.to_a
22
+ assert_equal 'b', map['a']
23
+ assert_equal 'c', map['b']
24
+ end
25
+
26
+ def test_to_yaml
27
+ map = Syck::Omap['a', 'b', 'c', 'd']
28
+ yaml = map.to_yaml
29
+ assert_match('!omap', yaml)
30
+ assert_match('- a: b', yaml)
31
+ assert_match('- c: d', yaml)
32
+ end
33
+
34
+ def test_round_trip
35
+ list = [["a", "b"], ["b", "c"]]
36
+ map = Syck::Omap[*list.flatten]
37
+ loaded = Syck.load(Syck.dump(map))
38
+
39
+ assert_equal map, loaded
40
+ assert_equal list, loaded.to_a
41
+ end
42
+
43
+ ###
44
+ # FIXME: Syck should also support !!omap as shorthand
45
+ def test_load
46
+ list = [["a", "b"], ["c", "d"]]
47
+ map = Syck.load(<<-eoyml)
48
+ --- !omap
49
+ - a: b
50
+ - c: d
51
+ eoyml
52
+ assert_equal list, map.to_a
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,30 @@
1
+ require 'helper'
2
+
3
+ module Syck
4
+ class TestSet < Test::Unit::TestCase
5
+ def setup
6
+ @set = Syck::Set.new
7
+ @set['foo'] = 'bar'
8
+ @set['bar'] = 'baz'
9
+ end
10
+
11
+ def test_to_yaml
12
+ assert_match(/!set/, @set.to_yaml)
13
+ end
14
+
15
+ def test_roundtrip
16
+ assert_equal(@set, Syck.load(Syck.dump(@set)))
17
+ end
18
+
19
+ ###
20
+ # FIXME: Syck should also support !!set as shorthand
21
+ def test_load_from_yaml
22
+ loaded = Syck.load(<<-eoyml)
23
+ --- !set
24
+ foo: bar
25
+ bar: baz
26
+ eoyml
27
+ assert_equal(@set, loaded)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,44 @@
1
+ require 'helper'
2
+
3
+ module Syck
4
+ class TestString < Test::Unit::TestCase
5
+ def test_binary_string_null
6
+ string = "\x00"
7
+ yml = Syck.dump string
8
+ assert_match(/binary/, yml)
9
+ assert_equal string, Syck.load(yml)
10
+ end
11
+
12
+ def test_binary_string
13
+ string = binary_string
14
+ yml = Syck.dump string
15
+ assert_match(/binary/, yml)
16
+ assert_equal string, Syck.load(yml)
17
+ end
18
+
19
+ def test_non_binary_string
20
+ string = binary_string(0.29)
21
+ yml = Syck.dump string
22
+ assert_not_match(/binary/, yml)
23
+ assert_equal string, Syck.load(yml)
24
+ end
25
+
26
+ def test_string_with_ivars
27
+ food = "is delicious"
28
+ ivar = "on rock and roll"
29
+ food.instance_variable_set(:@we_built_this_city, ivar)
30
+
31
+ str = Syck.load Syck.dump food
32
+ assert_equal ivar, food.instance_variable_get(:@we_built_this_city)
33
+ end
34
+
35
+ def binary_string percentage = 0.31, length = 100
36
+ string = ''
37
+ (percentage * length).to_i.times do |i|
38
+ string << "\b"
39
+ end
40
+ string << 'a' * (length - string.length)
41
+ string
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,32 @@
1
+ require 'helper'
2
+
3
+ class StructWithIvar < Struct.new(:foo)
4
+ attr_reader :bar
5
+ def initialize *args
6
+ super
7
+ @bar = 'hello'
8
+ end
9
+ end
10
+
11
+ module Syck
12
+ class TestStruct < MiniTest::Unit::TestCase
13
+ def test_roundtrip
14
+ thing = StructWithIvar.new('bar')
15
+ struct = Syck.load(Syck.dump(thing))
16
+
17
+ assert_equal 'hello', struct.bar
18
+ assert_equal 'bar', struct.foo
19
+ end
20
+
21
+ def test_load
22
+ obj = Syck.load(<<-eoyml)
23
+ --- !ruby/struct:StructWithIvar
24
+ foo: bar
25
+ @bar: hello
26
+ eoyml
27
+
28
+ assert_equal 'hello', obj.bar
29
+ assert_equal 'bar', obj.foo
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ require 'helper'
2
+
3
+ module Syck
4
+ class TestSymbol < Test::Unit::TestCase
5
+ def test_to_yaml
6
+ assert_equal :a, Syck.load(:a.to_yaml)
7
+ end
8
+
9
+ def test_dump
10
+ assert_equal :a, Syck.load(Syck.dump(:a))
11
+ end
12
+
13
+ def test_stringy
14
+ assert_equal :"1", Syck.load(Syck.dump(:"1"))
15
+ end
16
+
17
+ def test_load_quoted
18
+ assert_equal :"1", Syck.load("--- :'1'\n")
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ require 'helper'
2
+
3
+ module Syck
4
+ class TestString < Test::Unit::TestCase
5
+ def test_usec_long
6
+ bug4571 = '[ruby-core:35713]'
7
+ assert_equal(34, Syck.load("2011-03-22t23:32:11.0000342222+01:00").usec, bug4571)
8
+ end
9
+
10
+ def test_usec_very_long
11
+ t = "2011-03-22t23:32:11.0000342"+"0"*1000+"1+01:00"
12
+ assert_equal(34, Syck.load(t).usec)
13
+ end
14
+
15
+ def test_usec_full
16
+ assert_equal(342222, Syck.load("2011-03-22t23:32:11.342222+01:00").usec)
17
+ end
18
+
19
+ def test_usec_short
20
+ assert_equal(330000, Syck.load("2011-03-22t23:32:11.33+01:00").usec)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,1403 @@
1
+ # -*- mode: ruby; ruby-indent-level: 4; tab-width: 4; indent-tabs-mode: t -*-
2
+ # vim:sw=4:ts=4
3
+ # $Id$
4
+ #
5
+ require 'helper'
6
+ require 'syck/ypath'
7
+
8
+ # [ruby-core:01946]
9
+ module YAML_Tests
10
+ StructTest = Struct::new( :c )
11
+ end
12
+
13
+ module Syck
14
+ class YAML_Unit_Tests < Test::Unit::TestCase
15
+ # [ruby-core:34969]
16
+ def test_regexp_with_n
17
+ assert_cycle(Regexp.new('',0,'n'))
18
+ end
19
+
20
+ #
21
+ # Convert between YAML and the object to verify correct parsing and
22
+ # emitting
23
+ #
24
+ def assert_to_yaml( obj, yaml, msg = nil )
25
+ assert_equal( obj, Syck::load( yaml ), msg )
26
+ assert_equal( obj, Syck::parse( yaml ).transform, msg )
27
+ assert_equal( obj, Syck::load( obj.to_yaml ), msg )
28
+ assert_equal( obj, Syck::parse( obj.to_yaml ).transform, msg )
29
+ assert_equal( obj, Syck::load(
30
+ obj.to_yaml( :UseVersion => true, :UseHeader => true, :SortKeys => true )
31
+ ), msg )
32
+ end
33
+
34
+ #
35
+ # Test parser only
36
+ #
37
+ def assert_parse_only( obj, yaml, msg = nil )
38
+ assert_equal( obj, Syck::load( yaml ), msg )
39
+ assert_equal( obj, Syck::parse( yaml ).transform, msg )
40
+ end
41
+
42
+ def assert_cycle( obj, msg = nil )
43
+ assert_equal( obj, Syck::load( obj.to_yaml ), msg )
44
+ end
45
+
46
+ def assert_path_segments( path, segments, msg = nil )
47
+ Syck::YPath.each_path( path ) { |choice|
48
+ assert_equal( choice.segments, segments.shift, msg )
49
+ }
50
+ assert_equal( segments.length, 0, "Some segments leftover: #{ segments.inspect }" )
51
+ end
52
+
53
+ #
54
+ # Make a time with the time zone
55
+ #
56
+ def mktime( year, mon, day, hour, min, sec, usec, zone = "Z" )
57
+ usec = Rational(usec.to_s) * 1000000
58
+ val = Time::utc( year.to_i, mon.to_i, day.to_i, hour.to_i, min.to_i, sec.to_i, usec )
59
+ if zone != "Z"
60
+ hour = zone[0,3].to_i * 3600
61
+ min = zone[3,2].to_i * 60
62
+ ofs = (hour + min)
63
+ val = Time.at( val.tv_sec - ofs, val.tv_nsec / 1000.0 )
64
+ end
65
+ return val
66
+ end
67
+
68
+ #
69
+ # Tests modified from 00basic.t in YAML.pm
70
+ #
71
+ def test_basic_map
72
+ # Simple map
73
+ assert_parse_only(
74
+ { 'one' => 'foo', 'three' => 'baz', 'two' => 'bar' }, <<EOY
75
+ one: foo
76
+ two: bar
77
+ three: baz
78
+ EOY
79
+ )
80
+ end
81
+
82
+ def test_basic_strings
83
+ # Common string types
84
+ assert_cycle("x")
85
+ assert_cycle(":x")
86
+ assert_cycle(":")
87
+ assert_parse_only(
88
+ { 1 => 'simple string', 2 => 42, 3 => '1 Single Quoted String',
89
+ 4 => 'YAML\'s Double "Quoted" String', 5 => "A block\n with several\n lines.\n",
90
+ 6 => "A \"chomped\" block", 7 => "A folded\n string\n", 8 => ": started string" },
91
+ <<EOY
92
+ 1: simple string
93
+ 2: 42
94
+ 3: '1 Single Quoted String'
95
+ 4: "YAML's Double \\\"Quoted\\\" String"
96
+ 5: |
97
+ A block
98
+ with several
99
+ lines.
100
+ 6: |-
101
+ A "chomped" block
102
+ 7: >
103
+ A
104
+ folded
105
+ string
106
+ 8: ": started string"
107
+ EOY
108
+ )
109
+ end
110
+
111
+ #
112
+ # Test the specification examples
113
+ # - Many examples have been changes because of whitespace problems that
114
+ # caused the two to be inequivalent, or keys to be sorted wrong
115
+ #
116
+
117
+ def test_spec_simple_implicit_sequence
118
+ # Simple implicit sequence
119
+ assert_to_yaml(
120
+ [ 'Mark McGwire', 'Sammy Sosa', 'Ken Griffey' ], <<EOY
121
+ - Mark McGwire
122
+ - Sammy Sosa
123
+ - Ken Griffey
124
+ EOY
125
+ )
126
+ end
127
+
128
+ def test_spec_simple_implicit_map
129
+ # Simple implicit map
130
+ assert_to_yaml(
131
+ { 'hr' => 65, 'avg' => 0.278, 'rbi' => 147 }, <<EOY
132
+ avg: 0.278
133
+ hr: 65
134
+ rbi: 147
135
+ EOY
136
+ )
137
+ end
138
+
139
+ def test_spec_simple_map_with_nested_sequences
140
+ # Simple mapping with nested sequences
141
+ assert_to_yaml(
142
+ { 'american' =>
143
+ [ 'Boston Red Sox', 'Detroit Tigers', 'New York Yankees' ],
144
+ 'national' =>
145
+ [ 'New York Mets', 'Chicago Cubs', 'Atlanta Braves' ] }, <<EOY
146
+ american:
147
+ - Boston Red Sox
148
+ - Detroit Tigers
149
+ - New York Yankees
150
+ national:
151
+ - New York Mets
152
+ - Chicago Cubs
153
+ - Atlanta Braves
154
+ EOY
155
+ )
156
+ end
157
+
158
+ def test_spec_simple_sequence_with_nested_map
159
+ # Simple sequence with nested map
160
+ assert_to_yaml(
161
+ [
162
+ {'name' => 'Mark McGwire', 'hr' => 65, 'avg' => 0.278},
163
+ {'name' => 'Sammy Sosa', 'hr' => 63, 'avg' => 0.288}
164
+ ], <<EOY
165
+ -
166
+ avg: 0.278
167
+ hr: 65
168
+ name: Mark McGwire
169
+ -
170
+ avg: 0.288
171
+ hr: 63
172
+ name: Sammy Sosa
173
+ EOY
174
+ )
175
+ end
176
+
177
+ def test_spec_sequence_of_sequences
178
+ # Simple sequence with inline sequences
179
+ assert_parse_only(
180
+ [
181
+ [ 'name', 'hr', 'avg' ],
182
+ [ 'Mark McGwire', 65, 0.278 ],
183
+ [ 'Sammy Sosa', 63, 0.288 ]
184
+ ], <<EOY
185
+ - [ name , hr , avg ]
186
+ - [ Mark McGwire , 65 , 0.278 ]
187
+ - [ Sammy Sosa , 63 , 0.288 ]
188
+ EOY
189
+ )
190
+ end
191
+
192
+ def test_spec_mapping_of_mappings
193
+ # Simple map with inline maps
194
+ assert_parse_only(
195
+ { 'Mark McGwire' =>
196
+ { 'hr' => 65, 'avg' => 0.278 },
197
+ 'Sammy Sosa' =>
198
+ { 'hr' => 63, 'avg' => 0.288 }
199
+ }, <<EOY
200
+ Mark McGwire: {hr: 65, avg: 0.278}
201
+ Sammy Sosa: {hr: 63,
202
+ avg: 0.288}
203
+ EOY
204
+ )
205
+ end
206
+
207
+ def test_ambiguous_comments
208
+ # [ruby-talk:88012]
209
+ assert_to_yaml( "Call the method #dave", <<EOY )
210
+ --- "Call the method #dave"
211
+ EOY
212
+ end
213
+
214
+ def test_spec_nested_comments
215
+ # Map and sequences with comments
216
+ assert_parse_only(
217
+ { 'hr' => [ 'Mark McGwire', 'Sammy Sosa' ],
218
+ 'rbi' => [ 'Sammy Sosa', 'Ken Griffey' ] }, <<EOY
219
+ hr: # 1998 hr ranking
220
+ - Mark McGwire
221
+ - Sammy Sosa
222
+ rbi:
223
+ # 1998 rbi ranking
224
+ - Sammy Sosa
225
+ - Ken Griffey
226
+ EOY
227
+ )
228
+ end
229
+
230
+ def test_spec_anchors_and_aliases
231
+ # Anchors and aliases
232
+ assert_parse_only(
233
+ { 'hr' =>
234
+ [ 'Mark McGwire', 'Sammy Sosa' ],
235
+ 'rbi' =>
236
+ [ 'Sammy Sosa', 'Ken Griffey' ] }, <<EOY
237
+ hr:
238
+ - Mark McGwire
239
+ # Name "Sammy Sosa" scalar SS
240
+ - &SS Sammy Sosa
241
+ rbi:
242
+ # So it can be referenced later.
243
+ - *SS
244
+ - Ken Griffey
245
+ EOY
246
+ )
247
+
248
+ assert_to_yaml(
249
+ [{"arrival"=>"EDI", "departure"=>"LAX", "fareref"=>"DOGMA", "currency"=>"GBP"}, {"arrival"=>"MEL", "departure"=>"SYD", "fareref"=>"MADF", "currency"=>"AUD"}, {"arrival"=>"MCO", "departure"=>"JFK", "fareref"=>"DFSF", "currency"=>"USD"}], <<EOY
250
+ -
251
+ &F fareref: DOGMA
252
+ &C currency: GBP
253
+ &D departure: LAX
254
+ &A arrival: EDI
255
+ - { *F: MADF, *C: AUD, *D: SYD, *A: MEL }
256
+ - { *F: DFSF, *C: USD, *D: JFK, *A: MCO }
257
+ EOY
258
+ )
259
+
260
+ assert_to_yaml(
261
+ {"ALIASES"=>["fareref", "currency", "departure", "arrival"], "FARES"=>[{"arrival"=>"EDI", "departure"=>"LAX", "fareref"=>"DOGMA", "currency"=>"GBP"}, {"arrival"=>"MEL", "departure"=>"SYD", "fareref"=>"MADF", "currency"=>"AUD"}, {"arrival"=>"MCO", "departure"=>"JFK", "fareref"=>"DFSF", "currency"=>"USD"}]}, <<EOY
262
+ ---
263
+ ALIASES: [&f fareref, &c currency, &d departure, &a arrival]
264
+ FARES:
265
+ - *f: DOGMA
266
+ *c: GBP
267
+ *d: LAX
268
+ *a: EDI
269
+
270
+ - *f: MADF
271
+ *c: AUD
272
+ *d: SYD
273
+ *a: MEL
274
+
275
+ - *f: DFSF
276
+ *c: USD
277
+ *d: JFK
278
+ *a: MCO
279
+
280
+ EOY
281
+ )
282
+
283
+ end
284
+
285
+ def test_spec_mapping_between_sequences
286
+ # Complex key #1
287
+ dj = Date.new( 2001, 7, 23 )
288
+ assert_parse_only(
289
+ { [ 'Detroit Tigers', 'Chicago Cubs' ] => [ Date.new( 2001, 7, 23 ) ],
290
+ [ 'New York Yankees', 'Atlanta Braves' ] => [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ), Date.new( 2001, 8, 14 ) ] }, <<EOY
291
+ ? # PLAY SCHEDULE
292
+ - Detroit Tigers
293
+ - Chicago Cubs
294
+ :
295
+ - 2001-07-23
296
+
297
+ ? [ New York Yankees,
298
+ Atlanta Braves ]
299
+ : [ 2001-07-02, 2001-08-12,
300
+ 2001-08-14 ]
301
+ EOY
302
+ )
303
+
304
+ # Complex key #2
305
+ assert_parse_only(
306
+ { [ 'New York Yankees', 'Atlanta Braves' ] =>
307
+ [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ),
308
+ Date.new( 2001, 8, 14 ) ],
309
+ [ 'Detroit Tigers', 'Chicago Cubs' ] =>
310
+ [ Date.new( 2001, 7, 23 ) ]
311
+ }, <<EOY
312
+ ?
313
+ - New York Yankees
314
+ - Atlanta Braves
315
+ :
316
+ - 2001-07-02
317
+ - 2001-08-12
318
+ - 2001-08-14
319
+ ?
320
+ - Detroit Tigers
321
+ - Chicago Cubs
322
+ :
323
+ - 2001-07-23
324
+ EOY
325
+ )
326
+ end
327
+
328
+ def test_spec_sequence_key_shortcut
329
+ # Shortcut sequence map
330
+ assert_parse_only(
331
+ { 'invoice' => 34843, 'date' => Date.new( 2001, 1, 23 ),
332
+ 'bill-to' => 'Chris Dumars', 'product' =>
333
+ [ { 'item' => 'Super Hoop', 'quantity' => 1 },
334
+ { 'item' => 'Basketball', 'quantity' => 4 },
335
+ { 'item' => 'Big Shoes', 'quantity' => 1 } ] }, <<EOY
336
+ invoice: 34843
337
+ date : 2001-01-23
338
+ bill-to: Chris Dumars
339
+ product:
340
+ - item : Super Hoop
341
+ quantity: 1
342
+ - item : Basketball
343
+ quantity: 4
344
+ - item : Big Shoes
345
+ quantity: 1
346
+ EOY
347
+ )
348
+ end
349
+
350
+ def test_spec_sequence_in_sequence_shortcut
351
+ # Seq-in-seq
352
+ assert_parse_only( [ [ [ 'one', 'two', 'three' ] ] ], <<EOY )
353
+ - - - one
354
+ - two
355
+ - three
356
+ EOY
357
+ end
358
+
359
+ def test_spec_sequence_shortcuts
360
+ # Sequence shortcuts combined
361
+ assert_parse_only(
362
+ [
363
+ [
364
+ [ [ 'one' ] ],
365
+ [ 'two', 'three' ],
366
+ { 'four' => nil },
367
+ [ { 'five' => [ 'six' ] } ],
368
+ [ 'seven' ]
369
+ ],
370
+ [ 'eight', 'nine' ]
371
+ ], <<EOY )
372
+ - - - - one
373
+ - - two
374
+ - three
375
+ - four:
376
+ - - five:
377
+ - six
378
+ - - seven
379
+ - - eight
380
+ - nine
381
+ EOY
382
+ end
383
+
384
+ def test_spec_single_literal
385
+ # Literal scalar block
386
+ assert_parse_only( [ "\\/|\\/|\n/ | |_\n" ], <<EOY )
387
+ - |
388
+ \\/|\\/|
389
+ / | |_
390
+ EOY
391
+ end
392
+
393
+ def test_spec_single_folded
394
+ # Folded scalar block
395
+ assert_parse_only(
396
+ [ "Mark McGwire's year was crippled by a knee injury.\n" ], <<EOY
397
+ - >
398
+ Mark McGwire\'s
399
+ year was crippled
400
+ by a knee injury.
401
+ EOY
402
+ )
403
+ end
404
+
405
+ def test_spec_preserve_indent
406
+ # Preserve indented spaces
407
+ assert_parse_only(
408
+ "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n", <<EOY
409
+ --- >
410
+ Sammy Sosa completed another
411
+ fine season with great stats.
412
+
413
+ 63 Home Runs
414
+ 0.288 Batting Average
415
+
416
+ What a year!
417
+ EOY
418
+ )
419
+ end
420
+
421
+ def test_spec_indentation_determines_scope
422
+ assert_parse_only(
423
+ { 'name' => 'Mark McGwire', 'accomplishment' => "Mark set a major league home run record in 1998.\n",
424
+ 'stats' => "65 Home Runs\n0.278 Batting Average\n" }, <<EOY
425
+ name: Mark McGwire
426
+ accomplishment: >
427
+ Mark set a major league
428
+ home run record in 1998.
429
+ stats: |
430
+ 65 Home Runs
431
+ 0.278 Batting Average
432
+ EOY
433
+ )
434
+ end
435
+
436
+ #
437
+ # Reports from N.Easterly & J.Trupiano : Tests with patch from daz
438
+ # [ruby-core:23006] [Bug #1311] http://bugs.ruby-lang.org/issues/show/1311
439
+ #
440
+ def test_scan_scalar_nl
441
+ bug1311 = '[ruby-core:23006]'
442
+ assert_cycle(<<EoY, bug1311)
443
+
444
+ a
445
+ b
446
+ EoY
447
+ assert_cycle(<<EoY, bug1311)
448
+
449
+ a
450
+ b
451
+ c
452
+ EoY
453
+ assert_cycle(<<EoY, bug1311)
454
+
455
+ a
456
+ b
457
+ EoY
458
+ assert_cycle(" Do I work?\nNo indent", bug1311)
459
+ assert_cycle(" \n Do I work?\nNo indent", bug1311)
460
+ assert_cycle("\n Do I work?\nNo indent", bug1311)
461
+ assert_cycle("\n", bug1311)
462
+ assert_cycle("\n\n", bug1311)
463
+ assert_cycle("\r\n", bug1311)
464
+
465
+ assert_cycle <<EoY, '[ruby-core:28777]'
466
+ Domain name:
467
+ ckgteam.co.uk
468
+
469
+ Registrant:
470
+ James Gregory
471
+
472
+ Registrant type:
473
+ UK Individual
474
+
475
+ Registrant's address:
476
+ The registrant is a non-trading individual who has opted to have their
477
+ address omitted from the WHOIS service.
478
+
479
+ Registrar:
480
+ Webfusion Ltd t/a 123-Reg.co.uk [Tag = 123-REG]
481
+ URL: http://www.123-reg.co.uk
482
+
483
+ Relevant dates:
484
+ Registered on: 16-Nov-2009
485
+ Renewal date: 16-Nov-2011
486
+ Last updated: 25-Nov-2009
487
+
488
+ Registration status:
489
+ Registered until renewal date.
490
+
491
+ Name servers:
492
+ ns1.slicehost.net
493
+ ns2.slicehost.net
494
+ ns3.slicehost.net
495
+
496
+ WHOIS lookup made at 11:56:46 19-Mar-2010
497
+
498
+ --
499
+ This WHOIS information is provided for free by Nominet UK the central registry
500
+ for .uk domain names. This information and the .uk WHOIS are:
501
+
502
+ Copyright Nominet UK 1996 - 2010.
503
+
504
+ You may not access the .uk WHOIS or use any data from it except as permitted
505
+ by the terms of use available in full at http://www.nominet.org.uk/whois, which
506
+ includes restrictions on: (A) use of the data for advertising, or its
507
+ repackaging, recompilation, redistribution or reuse (B) obscuring, removing
508
+ or hiding any or all of this notice and (C) exceeding query rate or volume
509
+ limits. The data is provided on an 'as-is' basis and may lag behind the
510
+ register. Access may be withdrawn or restricted at any time.
511
+ EoY
512
+
513
+ end
514
+
515
+ def test_spec_multiline_scalars
516
+ # Multiline flow scalars
517
+ assert_parse_only(
518
+ { 'plain' => 'This unquoted scalar spans many lines.',
519
+ 'quoted' => "So does this quoted scalar.\n" }, <<EOY
520
+ plain: This unquoted
521
+ scalar spans
522
+ many lines.
523
+ quoted: "\\
524
+ So does this quoted
525
+ scalar.\\n"
526
+ EOY
527
+ )
528
+ end
529
+
530
+ def test_spec_type_int
531
+ assert_parse_only(
532
+ { 'canonical' => 12345, 'decimal' => 12345, 'octal' => '014'.oct, 'hexadecimal' => '0xC'.hex }, <<EOY
533
+ canonical: 12345
534
+ decimal: +12,345
535
+ octal: 014
536
+ hexadecimal: 0xC
537
+ EOY
538
+ )
539
+ assert_parse_only(
540
+ { 'canonical' => 685230, 'decimal' => 685230, 'octal' => 02472256, 'hexadecimal' => 0x0A74AE, 'sexagesimal' => 685230 }, <<EOY)
541
+ canonical: 685230
542
+ decimal: +685,230
543
+ octal: 02472256
544
+ hexadecimal: 0x0A,74,AE
545
+ sexagesimal: 190:20:30
546
+ EOY
547
+ end
548
+
549
+ def test_spec_type_float
550
+ assert_parse_only(
551
+ { 'canonical' => 1230.15, 'exponential' => 1230.15, 'fixed' => 1230.15,
552
+ 'negative infinity' => -1.0/0.0 }, <<EOY)
553
+ canonical: 1.23015e+3
554
+ exponential: 12.3015e+02
555
+ fixed: 1,230.15
556
+ negative infinity: -.inf
557
+ EOY
558
+ nan = Syck::load( <<EOY )
559
+ not a number: .NaN
560
+ EOY
561
+ assert( nan['not a number'].nan? )
562
+ end
563
+
564
+ def test_spec_type_misc
565
+ assert_parse_only(
566
+ { nil => nil, true => true, false => false, 'string' => '12345' }, <<EOY
567
+ null: ~
568
+ true: yes
569
+ false: no
570
+ string: '12345'
571
+ EOY
572
+ )
573
+ end
574
+
575
+ def test_spec_complex_invoice
576
+ # Complex invoice type
577
+ id001 = { 'given' => 'Chris', 'family' => 'Dumars', 'address' =>
578
+ { 'lines' => "458 Walkman Dr.\nSuite #292\n", 'city' => 'Royal Oak',
579
+ 'state' => 'MI', 'postal' => 48046 } }
580
+ assert_parse_only(
581
+ { 'invoice' => 34843, 'date' => Date.new( 2001, 1, 23 ),
582
+ 'bill-to' => id001, 'ship-to' => id001, 'product' =>
583
+ [ { 'sku' => 'BL394D', 'quantity' => 4,
584
+ 'description' => 'Basketball', 'price' => 450.00 },
585
+ { 'sku' => 'BL4438H', 'quantity' => 1,
586
+ 'description' => 'Super Hoop', 'price' => 2392.00 } ],
587
+ 'tax' => 251.42, 'total' => 4443.52,
588
+ 'comments' => "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.\n" }, <<EOY
589
+ invoice: 34843
590
+ date : 2001-01-23
591
+ bill-to: &id001
592
+ given : Chris
593
+ family : !str Dumars
594
+ address:
595
+ lines: |
596
+ 458 Walkman Dr.
597
+ Suite #292
598
+ city : Royal Oak
599
+ state : MI
600
+ postal : 48046
601
+ ship-to: *id001
602
+ product:
603
+ - !map
604
+ sku : BL394D
605
+ quantity : 4
606
+ description : Basketball
607
+ price : 450.00
608
+ - sku : BL4438H
609
+ quantity : 1
610
+ description : Super Hoop
611
+ price : 2392.00
612
+ tax : 251.42
613
+ total: 4443.52
614
+ comments: >
615
+ Late afternoon is best.
616
+ Backup contact is Nancy
617
+ Billsmer @ 338-4338.
618
+ EOY
619
+ )
620
+ end
621
+
622
+ def test_spec_log_file
623
+ doc_ct = 0
624
+ Syck::load_documents( <<EOY
625
+ ---
626
+ Time: 2001-11-23 15:01:42 -05:00
627
+ User: ed
628
+ Warning: >
629
+ This is an error message
630
+ for the log file
631
+ ---
632
+ Time: 2001-11-23 15:02:31 -05:00
633
+ User: ed
634
+ Warning: >
635
+ A slightly different error
636
+ message.
637
+ ---
638
+ Date: 2001-11-23 15:03:17 -05:00
639
+ User: ed
640
+ Fatal: >
641
+ Unknown variable "bar"
642
+ Stack:
643
+ - file: TopClass.py
644
+ line: 23
645
+ code: |
646
+ x = MoreObject("345\\n")
647
+ - file: MoreClass.py
648
+ line: 58
649
+ code: |-
650
+ foo = bar
651
+ EOY
652
+ ) { |doc|
653
+ case doc_ct
654
+ when 0
655
+ assert_equal( doc, { 'Time' => mktime( 2001, 11, 23, 15, 01, 42, 00, "-05:00" ),
656
+ 'User' => 'ed', 'Warning' => "This is an error message for the log file\n" } )
657
+ when 1
658
+ assert_equal( doc, { 'Time' => mktime( 2001, 11, 23, 15, 02, 31, 00, "-05:00" ),
659
+ 'User' => 'ed', 'Warning' => "A slightly different error message.\n" } )
660
+ when 2
661
+ assert_equal( doc, { 'Date' => mktime( 2001, 11, 23, 15, 03, 17, 00, "-05:00" ),
662
+ 'User' => 'ed', 'Fatal' => "Unknown variable \"bar\"\n",
663
+ 'Stack' => [
664
+ { 'file' => 'TopClass.py', 'line' => 23, 'code' => "x = MoreObject(\"345\\n\")\n" },
665
+ { 'file' => 'MoreClass.py', 'line' => 58, 'code' => "foo = bar" } ] } )
666
+ end
667
+ doc_ct += 1
668
+ }
669
+ assert_equal( doc_ct, 3 )
670
+ end
671
+
672
+ def test_spec_root_fold
673
+ y = Syck::load( <<EOY
674
+ --- >
675
+ This YAML stream contains a single text value.
676
+ The next stream is a log file - a sequence of
677
+ log entries. Adding an entry to the log is a
678
+ simple matter of appending it at the end.
679
+ EOY
680
+ )
681
+ assert_equal( y, "This YAML stream contains a single text value. The next stream is a log file - a sequence of log entries. Adding an entry to the log is a simple matter of appending it at the end.\n" )
682
+ end
683
+
684
+ def test_spec_root_mapping
685
+ y = Syck::load( <<EOY
686
+ # This stream is an example of a top-level mapping.
687
+ invoice : 34843
688
+ date : 2001-01-23
689
+ total : 4443.52
690
+ EOY
691
+ )
692
+ assert_equal( y, { 'invoice' => 34843, 'date' => Date.new( 2001, 1, 23 ), 'total' => 4443.52 } )
693
+ end
694
+
695
+ def test_spec_oneline_docs
696
+ doc_ct = 0
697
+ Syck::load_documents( <<EOY
698
+ # The following is a sequence of three documents.
699
+ # The first contains an empty mapping, the second
700
+ # an empty sequence, and the last an empty string.
701
+ --- {}
702
+ --- [ ]
703
+ --- ''
704
+ EOY
705
+ ) { |doc|
706
+ case doc_ct
707
+ when 0
708
+ assert_equal( doc, {} )
709
+ when 1
710
+ assert_equal( doc, [] )
711
+ when 2
712
+ assert_equal( doc, '' )
713
+ end
714
+ doc_ct += 1
715
+ }
716
+ assert_equal( doc_ct, 3 )
717
+ end
718
+
719
+ def test_spec_domain_prefix
720
+ customer_proc = proc { |type, val|
721
+ if Hash === val
722
+ scheme, domain, type = type.split( ':', 3 )
723
+ val['type'] = "domain #{type}"
724
+ val
725
+ else
726
+ raise ArgumentError, "Not a Hash in domain.tld,2002/invoice: " + val.inspect
727
+ end
728
+ }
729
+ Syck.add_domain_type( "domain.tld,2002", 'invoice', &customer_proc )
730
+ Syck.add_domain_type( "domain.tld,2002", 'customer', &customer_proc )
731
+ assert_parse_only( { "invoice"=> { "customers"=> [ { "given"=>"Chris", "type"=>"domain customer", "family"=>"Dumars" } ], "type"=>"domain invoice" } }, <<EOY
732
+ # 'http://domain.tld,2002/invoice' is some type family.
733
+ invoice: !domain.tld,2002/^invoice
734
+ # 'seq' is shorthand for 'http://yaml.org/seq'.
735
+ # This does not effect '^customer' below
736
+ # because it is does not specify a prefix.
737
+ customers: !seq
738
+ # '^customer' is shorthand for the full
739
+ # notation 'http://domain.tld,2002/customer'.
740
+ - !^customer
741
+ given : Chris
742
+ family : Dumars
743
+ EOY
744
+ )
745
+ end
746
+
747
+ def test_spec_throwaway
748
+ assert_parse_only(
749
+ {"this"=>"contains three lines of text.\nThe third one starts with a\n# character. This isn't a comment.\n"}, <<EOY
750
+ ### These are four throwaway comment ###
751
+
752
+ ### lines (the second line is empty). ###
753
+ this: | # Comments may trail lines.
754
+ contains three lines of text.
755
+ The third one starts with a
756
+ # character. This isn't a comment.
757
+
758
+ # These are three throwaway comment
759
+ # lines (the first line is empty).
760
+ EOY
761
+ )
762
+ end
763
+
764
+ def test_spec_force_implicit
765
+ # Force implicit
766
+ assert_parse_only(
767
+ { 'integer' => 12, 'also int' => 12, 'string' => '12' }, <<EOY
768
+ integer: 12
769
+ also int: ! "12"
770
+ string: !str 12
771
+ EOY
772
+ )
773
+ end
774
+
775
+ def test_spec_private_types
776
+ doc_ct = 0
777
+ Syck::parse_documents( <<EOY
778
+ # Private types are per-document.
779
+ ---
780
+ pool: !!ball
781
+ number: 8
782
+ color: black
783
+ ---
784
+ bearing: !!ball
785
+ material: steel
786
+ EOY
787
+ ) { |doc|
788
+ case doc_ct
789
+ when 0
790
+ assert_equal( doc['pool'].type_id, 'x-private:ball' )
791
+ assert_equal( doc['pool'].transform.value, { 'number' => 8, 'color' => 'black' } )
792
+ when 1
793
+ assert_equal( doc['bearing'].type_id, 'x-private:ball' )
794
+ assert_equal( doc['bearing'].transform.value, { 'material' => 'steel' } )
795
+ end
796
+ doc_ct += 1
797
+ }
798
+ assert_equal( doc_ct, 2 )
799
+ end
800
+
801
+ def test_spec_url_escaping
802
+ Syck.add_domain_type( "domain.tld,2002", "type0" ) { |type, val|
803
+ "ONE: #{val}"
804
+ }
805
+ Syck.add_domain_type( "domain.tld,2002", "type%30" ) { |type, val|
806
+ "TWO: #{val}"
807
+ }
808
+ assert_parse_only(
809
+ { 'same' => [ 'ONE: value', 'ONE: value' ], 'different' => [ 'TWO: value' ] }, <<EOY
810
+ same:
811
+ - !domain.tld,2002/type\\x30 value
812
+ - !domain.tld,2002/type0 value
813
+ different: # As far as the YAML parser is concerned
814
+ - !domain.tld,2002/type%30 value
815
+ EOY
816
+ )
817
+ end
818
+
819
+ def test_spec_override_anchor
820
+ # Override anchor
821
+ a001 = "The alias node below is a repeated use of this value.\n"
822
+ assert_parse_only(
823
+ { 'anchor' => 'This scalar has an anchor.', 'override' => a001, 'alias' => a001 }, <<EOY
824
+ anchor : &A001 This scalar has an anchor.
825
+ override : &A001 >
826
+ The alias node below is a
827
+ repeated use of this value.
828
+ alias : *A001
829
+ EOY
830
+ )
831
+ end
832
+
833
+ def test_spec_explicit_families
834
+ Syck.add_domain_type( "somewhere.com,2002", 'type' ) { |type, val|
835
+ "SOMEWHERE: #{val}"
836
+ }
837
+ assert_parse_only(
838
+ { 'not-date' => '2002-04-28', 'picture' => "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236i^\020' \202\n\001\000;", 'hmm' => "SOMEWHERE: family above is short for\nhttp://somewhere.com/type\n" }, <<EOY
839
+ not-date: !str 2002-04-28
840
+ picture: !binary |
841
+ R0lGODlhDAAMAIQAAP//9/X
842
+ 17unp5WZmZgAAAOfn515eXv
843
+ Pz7Y6OjuDg4J+fn5OTk6enp
844
+ 56enmleECcgggoBADs=
845
+
846
+ hmm: !somewhere.com,2002/type |
847
+ family above is short for
848
+ http://somewhere.com/type
849
+ EOY
850
+ )
851
+ end
852
+
853
+ def test_spec_application_family
854
+ # Testing the clarkevans.com graphs
855
+ Syck.add_domain_type( "clarkevans.com,2002", 'graph/shape' ) { |type, val|
856
+ if Array === val
857
+ val << "Shape Container"
858
+ val
859
+ else
860
+ raise ArgumentError, "Invalid graph of type #{val.class}: " + val.inspect
861
+ end
862
+ }
863
+ one_shape_proc = Proc.new { |type, val|
864
+ if Hash === val
865
+ type = type.split( /:/ )
866
+ val['TYPE'] = "Shape: #{type[2]}"
867
+ val
868
+ else
869
+ raise ArgumentError, "Invalid graph of type #{val.class}: " + val.inspect
870
+ end
871
+ }
872
+ Syck.add_domain_type( "clarkevans.com,2002", 'graph/circle', &one_shape_proc )
873
+ Syck.add_domain_type( "clarkevans.com,2002", 'graph/line', &one_shape_proc )
874
+ Syck.add_domain_type( "clarkevans.com,2002", 'graph/text', &one_shape_proc )
875
+ assert_parse_only(
876
+ [[{"radius"=>7, "center"=>{"x"=>73, "y"=>129}, "TYPE"=>"Shape: graph/circle"}, {"finish"=>{"x"=>89, "y"=>102}, "TYPE"=>"Shape: graph/line", "start"=>{"x"=>73, "y"=>129}}, {"TYPE"=>"Shape: graph/text", "value"=>"Pretty vector drawing.", "start"=>{"x"=>73, "y"=>129}, "color"=>16772795}, "Shape Container"]], <<EOY
877
+ - !clarkevans.com,2002/graph/^shape
878
+ - !^circle
879
+ center: &ORIGIN {x: 73, y: 129}
880
+ radius: 7
881
+ - !^line # !clarkevans.com,2002/graph/line
882
+ start: *ORIGIN
883
+ finish: { x: 89, y: 102 }
884
+ - !^text
885
+ start: *ORIGIN
886
+ color: 0xFFEEBB
887
+ value: Pretty vector drawing.
888
+ EOY
889
+ )
890
+ end
891
+
892
+ def test_spec_float_explicit
893
+ assert_parse_only(
894
+ [ 10.0, 10.0, 10.0, 10.0 ], <<EOY
895
+ # All entries in the sequence
896
+ # have the same type and value.
897
+ - 10.0
898
+ - !float 10
899
+ - !yaml.org,2002/^float '10'
900
+ - !yaml.org,2002/float "\\
901
+ 1\\
902
+ 0"
903
+ EOY
904
+ )
905
+ end
906
+
907
+ def test_spec_builtin_seq
908
+ # Assortment of sequences
909
+ assert_parse_only(
910
+ { 'empty' => [], 'in-line' => [ 'one', 'two', 'three', 'four', 'five' ],
911
+ 'nested' => [ 'First item in top sequence', [ 'Subordinate sequence entry' ],
912
+ "A multi-line sequence entry\n", 'Sixth item in top sequence' ] }, <<EOY
913
+ empty: []
914
+ in-line: [ one, two, three # May span lines,
915
+ , four, # indentation is
916
+ five ] # mostly ignored.
917
+ nested:
918
+ - First item in top sequence
919
+ -
920
+ - Subordinate sequence entry
921
+ - >
922
+ A multi-line
923
+ sequence entry
924
+ - Sixth item in top sequence
925
+ EOY
926
+ )
927
+ end
928
+
929
+ def test_spec_builtin_map
930
+ # Assortment of mappings
931
+ assert_parse_only(
932
+ { 'empty' => {}, 'in-line' => { 'one' => 1, 'two' => 2 },
933
+ 'spanning' => { 'one' => 1, 'two' => 2 },
934
+ 'nested' => { 'first' => 'First entry', 'second' =>
935
+ { 'key' => 'Subordinate mapping' }, 'third' =>
936
+ [ 'Subordinate sequence', {}, 'Previous mapping is empty.',
937
+ { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' },
938
+ 'The previous entry is equal to the following one.',
939
+ { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' } ],
940
+ 12.0 => 'This key is a float.', "?\n" => 'This key had to be protected.',
941
+ "\a" => 'This key had to be escaped.',
942
+ "This is a multi-line folded key\n" => "Whose value is also multi-line.\n",
943
+ [ 'This key', 'is a sequence' ] => [ 'With a sequence value.' ] } }, <<EOY
944
+
945
+ empty: {}
946
+ in-line: { one: 1, two: 2 }
947
+ spanning: { one: 1,
948
+ two: 2 }
949
+ nested:
950
+ first : First entry
951
+ second:
952
+ key: Subordinate mapping
953
+ third:
954
+ - Subordinate sequence
955
+ - { }
956
+ - Previous mapping is empty.
957
+ - A key: value pair in a sequence.
958
+ A second: key:value pair.
959
+ - The previous entry is equal to the following one.
960
+ -
961
+ A key: value pair in a sequence.
962
+ A second: key:value pair.
963
+ !float 12 : This key is a float.
964
+ ? >
965
+ ?
966
+ : This key had to be protected.
967
+ "\\a" : This key had to be escaped.
968
+ ? >
969
+ This is a
970
+ multi-line
971
+ folded key
972
+ : >
973
+ Whose value is
974
+ also multi-line.
975
+ ?
976
+ - This key
977
+ - is a sequence
978
+ :
979
+ - With a sequence value.
980
+ # The following parses correctly,
981
+ # but Ruby 1.6.* fails the comparison!
982
+ # ?
983
+ # This: key
984
+ # is a: mapping
985
+ # :
986
+ # with a: mapping value.
987
+ EOY
988
+ )
989
+ end
990
+
991
+ def test_spec_builtin_literal_blocks
992
+ # Assortment of literal scalar blocks
993
+ assert_parse_only(
994
+ {"both are equal to"=>" This has no newline.", "is equal to"=>"The \\ ' \" characters may be\nfreely used. Leading white\n space is significant.\n\nLine breaks are significant.\nThus this value contains one\nempty line and ends with a\nsingle line break, but does\nnot start with one.\n", "also written as"=>" This has no newline.", "indented and chomped"=>" This has no newline.", "empty"=>"", "literal"=>"The \\ ' \" characters may be\nfreely used. Leading white\n space is significant.\n\nLine breaks are significant.\nThus this value contains one\nempty line and ends with a\nsingle line break, but does\nnot start with one.\n"}, <<EOY
995
+ empty: |
996
+
997
+ literal: |
998
+ The \\ ' " characters may be
999
+ freely used. Leading white
1000
+ space is significant.
1001
+
1002
+ Line breaks are significant.
1003
+ Thus this value contains one
1004
+ empty line and ends with a
1005
+ single line break, but does
1006
+ not start with one.
1007
+
1008
+ is equal to: "The \\ ' \\" characters may \\
1009
+ be\\nfreely used. Leading white\\n space \\
1010
+ is significant.\\n\\nLine breaks are \\
1011
+ significant.\\nThus this value contains \\
1012
+ one\\nempty line and ends with a\\nsingle \\
1013
+ line break, but does\\nnot start with one.\\n"
1014
+
1015
+ # Comments may follow a nested
1016
+ # scalar value. They must be
1017
+ # less indented.
1018
+
1019
+ # Modifiers may be combined in any order.
1020
+ indented and chomped: |2-
1021
+ This has no newline.
1022
+
1023
+ also written as: |-2
1024
+ This has no newline.
1025
+
1026
+ both are equal to: " This has no newline."
1027
+ EOY
1028
+ )
1029
+
1030
+ str1 = "This has one newline.\n"
1031
+ str2 = "This has no newline."
1032
+ str3 = "This has two newlines.\n\n"
1033
+ assert_parse_only(
1034
+ { 'clipped' => str1, 'same as "clipped" above' => str1,
1035
+ 'stripped' => str2, 'same as "stripped" above' => str2,
1036
+ 'kept' => str3, 'same as "kept" above' => str3 }, <<EOY
1037
+ clipped: |
1038
+ This has one newline.
1039
+
1040
+ same as "clipped" above: "This has one newline.\\n"
1041
+
1042
+ stripped: |-
1043
+ This has no newline.
1044
+
1045
+ same as "stripped" above: "This has no newline."
1046
+
1047
+ kept: |+
1048
+ This has two newlines.
1049
+
1050
+ same as "kept" above: "This has two newlines.\\n\\n"
1051
+
1052
+ EOY
1053
+ )
1054
+ end
1055
+
1056
+ def test_spec_span_single_quote
1057
+ assert_parse_only( {"third"=>"a single quote ' must be escaped.", "second"=>"! : \\ etc. can be used freely.", "is same as"=>"this contains six spaces\nand one line break", "empty"=>"", "span"=>"this contains six spaces\nand one line break"}, <<EOY
1058
+ empty: ''
1059
+ second: '! : \\ etc. can be used freely.'
1060
+ third: 'a single quote '' must be escaped.'
1061
+ span: 'this contains
1062
+ six spaces
1063
+
1064
+ and one
1065
+ line break'
1066
+ is same as: "this contains six spaces\\nand one line break"
1067
+ EOY
1068
+ )
1069
+ end
1070
+
1071
+ def test_spec_span_double_quote
1072
+ assert_parse_only( {"is equal to"=>"this contains four spaces", "third"=>"a \" or a \\ must be escaped.", "second"=>"! : etc. can be used freely.", "empty"=>"", "fourth"=>"this value ends with an LF.\n", "span"=>"this contains four spaces"}, <<EOY
1073
+ empty: ""
1074
+ second: "! : etc. can be used freely."
1075
+ third: "a \\\" or a \\\\ must be escaped."
1076
+ fourth: "this value ends with an LF.\\n"
1077
+ span: "this contains
1078
+ four \\
1079
+ spaces"
1080
+ is equal to: "this contains four spaces"
1081
+ EOY
1082
+ )
1083
+ end
1084
+
1085
+ def test_spec_builtin_time
1086
+ # Time
1087
+ assert_parse_only(
1088
+ { "space separated" => mktime( 2001, 12, 14, 21, 59, 43, ".10", "-05:00" ),
1089
+ "canonical" => mktime( 2001, 12, 15, 2, 59, 43, ".10" ),
1090
+ "date (noon UTC)" => Date.new( 2002, 12, 14),
1091
+ "valid iso8601" => mktime( 2001, 12, 14, 21, 59, 43, ".10", "-05:00" ) }, <<EOY
1092
+ canonical: 2001-12-15T02:59:43.1Z
1093
+ valid iso8601: 2001-12-14t21:59:43.10-05:00
1094
+ space separated: 2001-12-14 21:59:43.10 -05:00
1095
+ date (noon UTC): 2002-12-14
1096
+ EOY
1097
+ )
1098
+ end
1099
+
1100
+ def test_spec_builtin_binary
1101
+ arrow_gif = "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236iiiccc\243\243\243\204\204\204\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371!\376\016Made with GIMP\000,\000\000\000\000\f\000\f\000\000\005, \216\2010\236\343@\024\350i\020\304\321\212\010\034\317\200M$z\357\3770\205p\270\2601f\r\e\316\001\303\001\036\020' \202\n\001\000;"
1102
+ assert_parse_only(
1103
+ { 'canonical' => arrow_gif, 'base64' => arrow_gif,
1104
+ 'description' => "The binary value above is a tiny arrow encoded as a gif image.\n" }, <<EOY
1105
+ canonical: !binary "\\
1106
+ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOf\\
1107
+ n515eXvPz7Y6OjuDg4J+fn5OTk6enp56enmlpaW\\
1108
+ NjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++\\
1109
+ f/++f/++f/++f/++f/++f/++f/++SH+Dk1hZGUg\\
1110
+ d2l0aCBHSU1QACwAAAAADAAMAAAFLCAgjoEwnuN\\
1111
+ AFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84Bww\\
1112
+ EeECcgggoBADs="
1113
+ base64: !binary |
1114
+ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOf
1115
+ n515eXvPz7Y6OjuDg4J+fn5OTk6enp56enmlpaW
1116
+ NjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++
1117
+ f/++f/++f/++f/++f/++f/++f/++SH+Dk1hZGUg
1118
+ d2l0aCBHSU1QACwAAAAADAAMAAAFLCAgjoEwnuN
1119
+ AFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84Bww
1120
+ EeECcgggoBADs=
1121
+ description: >
1122
+ The binary value above is a tiny arrow
1123
+ encoded as a gif image.
1124
+ EOY
1125
+ )
1126
+ end
1127
+ def test_ruby_regexp
1128
+ # Test Ruby regular expressions
1129
+ assert_to_yaml(
1130
+ { 'simple' => /a.b/, 'complex' => %r'\A"((?:[^"]|\")+)"',
1131
+ 'case-insensitive' => /George McFly/i }, <<EOY
1132
+ case-insensitive: !ruby/regexp "/George McFly/i"
1133
+ complex: !ruby/regexp "/\\\\A\\"((?:[^\\"]|\\\\\\")+)\\"/"
1134
+ simple: !ruby/regexp "/a.b/"
1135
+ EOY
1136
+ )
1137
+ end
1138
+
1139
+ #
1140
+ # Test of Ranges
1141
+ #
1142
+ def test_ranges
1143
+
1144
+ # Simple numeric
1145
+ assert_to_yaml( 1..3, <<EOY )
1146
+ --- !ruby/range 1..3
1147
+ EOY
1148
+
1149
+ # Simple alphabetic
1150
+ assert_to_yaml( 'a'..'z', <<EOY )
1151
+ --- !ruby/range a..z
1152
+ EOY
1153
+
1154
+ # Float
1155
+ assert_to_yaml( 10.5...30.3, <<EOY )
1156
+ --- !ruby/range 10.5...30.3
1157
+ EOY
1158
+
1159
+ end
1160
+
1161
+ def test_ruby_struct
1162
+ # Ruby structures
1163
+ book_struct = Struct::new( "BookStruct", :author, :title, :year, :isbn )
1164
+ assert_to_yaml(
1165
+ [ book_struct.new( "Yukihiro Matsumoto", "Ruby in a Nutshell", 2002, "0-596-00214-9" ),
1166
+ book_struct.new( [ 'Dave Thomas', 'Andy Hunt' ], "The Pickaxe", 2002,
1167
+ book_struct.new( "This should be the ISBN", "but I have another struct here", 2002, "None" )
1168
+ ) ], <<EOY
1169
+ - !ruby/struct:BookStruct
1170
+ author: Yukihiro Matsumoto
1171
+ title: Ruby in a Nutshell
1172
+ year: 2002
1173
+ isbn: 0-596-00214-9
1174
+ - !ruby/struct:BookStruct
1175
+ author:
1176
+ - Dave Thomas
1177
+ - Andy Hunt
1178
+ title: The Pickaxe
1179
+ year: 2002
1180
+ isbn: !ruby/struct:BookStruct
1181
+ author: This should be the ISBN
1182
+ title: but I have another struct here
1183
+ year: 2002
1184
+ isbn: None
1185
+ EOY
1186
+ )
1187
+
1188
+ assert_to_yaml( YAML_Tests::StructTest.new( 123 ), <<EOY )
1189
+ --- !ruby/struct:YAML_Tests::StructTest
1190
+ c: 123
1191
+ EOY
1192
+
1193
+ end
1194
+
1195
+ def test_ruby_rational
1196
+ assert_to_yaml( Rational(1, 2), <<EOY )
1197
+ --- !ruby/object:Rational
1198
+ numerator: 1
1199
+ denominator: 2
1200
+ EOY
1201
+
1202
+ # Read YAML dumped by the ruby 1.8.3.
1203
+ assert_to_yaml( Rational(1, 2), "!ruby/object:Rational 1/2\n" )
1204
+ assert_raise( ArgumentError ) { Syck.load("!ruby/object:Rational INVALID/RATIONAL\n") }
1205
+ end
1206
+
1207
+ def test_ruby_complex
1208
+ assert_to_yaml( Complex(3, 4), <<EOY )
1209
+ --- !ruby/object:Complex
1210
+ image: 4
1211
+ real: 3
1212
+ EOY
1213
+
1214
+ # Read YAML dumped by the ruby 1.8.3.
1215
+ assert_to_yaml( Complex(3, 4), "!ruby/object:Complex 3+4i\n" )
1216
+ assert_raise( ArgumentError ) { Syck.load("!ruby/object:Complex INVALID+COMPLEXi\n") }
1217
+ end
1218
+
1219
+ def test_emitting_indicators
1220
+ assert_to_yaml( "Hi, from Object 1. You passed: please, pretty please", <<EOY
1221
+ --- "Hi, from Object 1. You passed: please, pretty please"
1222
+ EOY
1223
+ )
1224
+ end
1225
+
1226
+ #
1227
+ # Test the YAML::Stream class -- INACTIVE at the moment
1228
+ #
1229
+ def test_document
1230
+ y = Syck::Stream.new( :Indent => 2, :UseVersion => 0 )
1231
+ y.add(
1232
+ { 'hi' => 'hello', 'map' =>
1233
+ { 'good' => 'two' },
1234
+ 'time' => Time.now,
1235
+ 'try' => /^po(.*)$/,
1236
+ 'bye' => 'goodbye'
1237
+ }
1238
+ )
1239
+ y.add( { 'po' => 'nil', 'oper' => 90 } )
1240
+ y.add( { 'hi' => 'wow!', 'bye' => 'wow!' } )
1241
+ y.add( { [ 'Red Socks', 'Boston' ] => [ 'One', 'Two', 'Three' ] } )
1242
+ y.add( [ true, false, false ] )
1243
+ end
1244
+
1245
+ #
1246
+ # Test YPath choices parsing
1247
+ #
1248
+ def test_ypath_parsing
1249
+ assert_path_segments( "/*/((one|three)/name|place)|//place",
1250
+ [ ["*", "one", "name"],
1251
+ ["*", "three", "name"],
1252
+ ["*", "place"],
1253
+ ["/", "place"] ]
1254
+ )
1255
+ end
1256
+
1257
+ #
1258
+ # Tests from Tanaka Akira on [ruby-core]
1259
+ #
1260
+ def test_akira
1261
+
1262
+ # Commas in plain scalars [ruby-core:1066]
1263
+ assert_to_yaml(
1264
+ {"A"=>"A,","B"=>"B"}, <<EOY
1265
+ A: "A,"
1266
+ B: B
1267
+ EOY
1268
+ )
1269
+
1270
+ # Double-quoted keys [ruby-core:1069]
1271
+ assert_to_yaml(
1272
+ {"1"=>2, "2"=>3}, <<EOY
1273
+ '1': 2
1274
+ "2": 3
1275
+ EOY
1276
+ )
1277
+
1278
+ # Anchored mapping [ruby-core:1071]
1279
+ assert_to_yaml(
1280
+ [{"a"=>"b"}] * 2, <<EOY
1281
+ - &id001
1282
+ a: b
1283
+ - *id001
1284
+ EOY
1285
+ )
1286
+
1287
+ # Stress test [ruby-core:1071]
1288
+ # a = []; 1000.times { a << {"a"=>"b", "c"=>"d"} }
1289
+ # YAML::load( a.to_yaml )
1290
+
1291
+ end
1292
+
1293
+ #
1294
+ # Test Time.now cycle
1295
+ #
1296
+ def test_time_now_cycle
1297
+ #
1298
+ # From Minero Aoki [ruby-core:2305]
1299
+ #
1300
+ require 'yaml'
1301
+ t = Time.now
1302
+ t = Time.at(t.tv_sec, t.tv_usec)
1303
+ 5.times do
1304
+ assert_cycle(t)
1305
+ end
1306
+ end
1307
+
1308
+ #
1309
+ # Test Range cycle
1310
+ #
1311
+ def test_range_cycle
1312
+ #
1313
+ # From Minero Aoki [ruby-core:02306]
1314
+ #
1315
+ assert_cycle("a".."z")
1316
+
1317
+ #
1318
+ # From Nobu Nakada [ruby-core:02311]
1319
+ #
1320
+ assert_cycle(0..1)
1321
+ assert_cycle(1.0e20 .. 2.0e20)
1322
+ assert_cycle("0".."1")
1323
+ assert_cycle(".."..."...")
1324
+ assert_cycle(".rb"..".pl")
1325
+ assert_cycle(".rb"...".pl")
1326
+ assert_cycle('"'...".")
1327
+ assert_cycle("'"...".")
1328
+ end
1329
+
1330
+ #
1331
+ # Circular references
1332
+ #
1333
+ def test_circular_references
1334
+ a = []; a[0] = a; a[1] = a
1335
+ inspect_str = "[[...], [...]]"
1336
+ assert_equal( inspect_str, Syck::load( a.to_yaml ).inspect )
1337
+ end
1338
+
1339
+ #
1340
+ # Test Symbol cycle
1341
+ #
1342
+ def test_symbol_cycle
1343
+ #
1344
+ # From Aaron Schrab [ruby-Bugs:2535]
1345
+ #
1346
+ assert_cycle(:"^foo")
1347
+ end
1348
+
1349
+ #
1350
+ # Test Numeric cycle
1351
+ #
1352
+ class NumericTest < Numeric
1353
+ def initialize(value)
1354
+ @value = value
1355
+ end
1356
+ def ==(other)
1357
+ @value == other.instance_eval{ @value }
1358
+ end
1359
+ end
1360
+ def test_numeric_cycle
1361
+ assert_cycle(1) # Fixnum
1362
+ assert_cycle(111111111111111111111111111111111) # Bignum
1363
+ assert_cycle(NumericTest.new(3)) # Subclass of Numeric
1364
+ end
1365
+
1366
+ #
1367
+ # Test empty map/seq in map cycle
1368
+ #
1369
+ def test_empty_map_key
1370
+ #
1371
+ # empty seq as key
1372
+ #
1373
+ o = Syck.load({[]=>""}.to_yaml)
1374
+ assert_equal(Hash, o.class)
1375
+ assert_equal([[]], o.keys)
1376
+
1377
+ #
1378
+ # empty map as key
1379
+ #
1380
+ o = Syck.load({{}=>""}.to_yaml)
1381
+ assert_equal(Hash, o.class)
1382
+ assert_equal([{}], o.keys)
1383
+ end
1384
+
1385
+ #
1386
+ # contributed by riley lynch [ruby-Bugs-8548]
1387
+ #
1388
+ def test_object_id_collision
1389
+ omap = Syck::Omap.new
1390
+ 1000.times { |i| omap["key_#{i}"] = { "value" => i } }
1391
+ raise "id collision in ordered map" if omap.to_yaml =~ /id\d+/
1392
+ end
1393
+
1394
+ def test_date_out_of_range
1395
+ assert_nothing_raised{Syck::load('1900-01-01T00:00:00+00:00')}
1396
+ end
1397
+
1398
+ def test_normal_exit
1399
+ Syck.load("2000-01-01 00:00:00.#{"0"*1000} +00:00\n")
1400
+ # '[ruby-core:13735]'
1401
+ end
1402
+ end
1403
+ end