psych 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. data/.autotest +18 -0
  2. data/.gemtest +0 -0
  3. data/CHANGELOG.rdoc +3 -0
  4. data/Manifest.txt +87 -0
  5. data/README.rdoc +50 -0
  6. data/Rakefile +66 -0
  7. data/ext/psych/emitter.c +517 -0
  8. data/ext/psych/emitter.h +8 -0
  9. data/ext/psych/extconf.rb +22 -0
  10. data/ext/psych/parser.c +384 -0
  11. data/ext/psych/parser.h +6 -0
  12. data/ext/psych/psych.c +34 -0
  13. data/ext/psych/psych.h +20 -0
  14. data/ext/psych/to_ruby.c +41 -0
  15. data/ext/psych/to_ruby.h +8 -0
  16. data/ext/psych/yaml_tree.c +24 -0
  17. data/ext/psych/yaml_tree.h +8 -0
  18. data/lib/psych.rb +263 -0
  19. data/lib/psych/coder.rb +94 -0
  20. data/lib/psych/core_ext.rb +39 -0
  21. data/lib/psych/deprecated.rb +82 -0
  22. data/lib/psych/handler.rb +221 -0
  23. data/lib/psych/json.rb +6 -0
  24. data/lib/psych/json/ruby_events.rb +19 -0
  25. data/lib/psych/json/stream.rb +15 -0
  26. data/lib/psych/json/tree_builder.rb +12 -0
  27. data/lib/psych/json/yaml_events.rb +29 -0
  28. data/lib/psych/nodes.rb +77 -0
  29. data/lib/psych/nodes/alias.rb +18 -0
  30. data/lib/psych/nodes/document.rb +60 -0
  31. data/lib/psych/nodes/mapping.rb +56 -0
  32. data/lib/psych/nodes/node.rb +52 -0
  33. data/lib/psych/nodes/scalar.rb +67 -0
  34. data/lib/psych/nodes/sequence.rb +81 -0
  35. data/lib/psych/nodes/stream.rb +37 -0
  36. data/lib/psych/omap.rb +4 -0
  37. data/lib/psych/parser.rb +47 -0
  38. data/lib/psych/scalar_scanner.rb +105 -0
  39. data/lib/psych/set.rb +4 -0
  40. data/lib/psych/stream.rb +36 -0
  41. data/lib/psych/streaming.rb +22 -0
  42. data/lib/psych/tree_builder.rb +94 -0
  43. data/lib/psych/visitors.rb +6 -0
  44. data/lib/psych/visitors/depth_first.rb +26 -0
  45. data/lib/psych/visitors/emitter.rb +44 -0
  46. data/lib/psych/visitors/json_tree.rb +21 -0
  47. data/lib/psych/visitors/to_ruby.rb +267 -0
  48. data/lib/psych/visitors/visitor.rb +19 -0
  49. data/lib/psych/visitors/yaml_tree.rb +373 -0
  50. data/test/psych/helper.rb +63 -0
  51. data/test/psych/json/test_stream.rb +109 -0
  52. data/test/psych/nodes/test_enumerable.rb +43 -0
  53. data/test/psych/test_alias_and_anchor.rb +26 -0
  54. data/test/psych/test_array.rb +19 -0
  55. data/test/psych/test_boolean.rb +36 -0
  56. data/test/psych/test_class.rb +17 -0
  57. data/test/psych/test_coder.rb +184 -0
  58. data/test/psych/test_date_time.rb +17 -0
  59. data/test/psych/test_deprecated.rb +210 -0
  60. data/test/psych/test_document.rb +46 -0
  61. data/test/psych/test_emitter.rb +94 -0
  62. data/test/psych/test_encoding.rb +179 -0
  63. data/test/psych/test_engine_manager.rb +57 -0
  64. data/test/psych/test_exception.rb +39 -0
  65. data/test/psych/test_hash.rb +30 -0
  66. data/test/psych/test_json_tree.rb +65 -0
  67. data/test/psych/test_merge_keys.rb +72 -0
  68. data/test/psych/test_nil.rb +18 -0
  69. data/test/psych/test_null.rb +19 -0
  70. data/test/psych/test_object.rb +27 -0
  71. data/test/psych/test_omap.rb +68 -0
  72. data/test/psych/test_parser.rb +297 -0
  73. data/test/psych/test_psych.rb +168 -0
  74. data/test/psych/test_scalar.rb +11 -0
  75. data/test/psych/test_scalar_scanner.rb +69 -0
  76. data/test/psych/test_serialize_subclasses.rb +38 -0
  77. data/test/psych/test_set.rb +49 -0
  78. data/test/psych/test_stream.rb +49 -0
  79. data/test/psych/test_string.rb +49 -0
  80. data/test/psych/test_struct.rb +51 -0
  81. data/test/psych/test_symbol.rb +17 -0
  82. data/test/psych/test_to_yaml_properties.rb +63 -0
  83. data/test/psych/test_tree_builder.rb +79 -0
  84. data/test/psych/test_yaml.rb +1256 -0
  85. data/test/psych/visitors/test_depth_first.rb +49 -0
  86. data/test/psych/visitors/test_emitter.rb +144 -0
  87. data/test/psych/visitors/test_to_ruby.rb +325 -0
  88. data/test/psych/visitors/test_yaml_tree.rb +155 -0
  89. metadata +232 -0
@@ -0,0 +1,19 @@
1
+ module Psych
2
+ module Visitors
3
+ class Visitor
4
+ def accept target
5
+ visit target
6
+ end
7
+
8
+ private
9
+
10
+ DISPATCH = Hash.new do |hash, klass|
11
+ hash[klass] = "visit_#{klass.name.gsub('::', '_')}"
12
+ end
13
+
14
+ def visit target
15
+ send DISPATCH[target.class], target
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,373 @@
1
+ module Psych
2
+ module Visitors
3
+ ###
4
+ # YAMLTree builds a YAML ast given a ruby object. For example:
5
+ #
6
+ # builder = Psych::Visitors::YAMLTree.new
7
+ # builder << { :foo => 'bar' }
8
+ # builder.tree # => #<Psych::Nodes::Stream .. }
9
+ #
10
+ class YAMLTree < Psych::Visitors::Visitor
11
+ attr_reader :started, :finished
12
+ alias :finished? :finished
13
+ alias :started? :started
14
+
15
+ def initialize options = {}, emitter = Psych::TreeBuilder.new
16
+ super()
17
+ @started = false
18
+ @finished = false
19
+ @emitter = emitter
20
+ @st = {}
21
+ @ss = ScalarScanner.new
22
+ @options = options
23
+
24
+ @dispatch_cache = Hash.new do |h,klass|
25
+ method = "visit_#{(klass.name || '').split('::').join('_')}"
26
+
27
+ method = respond_to?(method) ? method : h[klass.superclass]
28
+
29
+ raise(TypeError, "Can't dump #{target.class}") unless method
30
+
31
+ h[klass] = method
32
+ end
33
+ end
34
+
35
+ def start encoding = Nodes::Stream::UTF8
36
+ @emitter.start_stream(encoding).tap do
37
+ @started = true
38
+ end
39
+ end
40
+
41
+ def finish
42
+ @emitter.end_stream.tap do
43
+ @finished = true
44
+ end
45
+ end
46
+
47
+ def tree
48
+ finish unless finished?
49
+ end
50
+
51
+ def push object
52
+ start unless started?
53
+ version = []
54
+ version = [1,1] if @options[:header]
55
+
56
+ case @options[:version]
57
+ when Array
58
+ version = @options[:version]
59
+ when String
60
+ version = @options[:version].split('.').map { |x| x.to_i }
61
+ else
62
+ version = [1,1]
63
+ end if @options.key? :version
64
+
65
+ @emitter.start_document version, [], false
66
+ accept object
67
+ @emitter.end_document
68
+ end
69
+ alias :<< :push
70
+
71
+ def accept target
72
+ # return any aliases we find
73
+ if @st.key? target.object_id
74
+ oid = target.object_id
75
+ node = @st[oid]
76
+ anchor = oid.to_s
77
+ node.anchor = anchor
78
+ return @emitter.alias anchor
79
+ end
80
+
81
+ if target.respond_to?(:to_yaml)
82
+ begin
83
+ loc = target.method(:to_yaml).source_location.first
84
+ if loc !~ /(syck\/rubytypes.rb|psych\/core_ext.rb)/
85
+ unless target.respond_to?(:encode_with)
86
+ if $VERBOSE
87
+ warn "implementing to_yaml is deprecated, please implement \"encode_with\""
88
+ end
89
+
90
+ target.to_yaml(:nodump => true)
91
+ end
92
+ end
93
+ rescue
94
+ # public_method or source_location might be overridden,
95
+ # and it's OK to skip it since it's only to emit a warning
96
+ end
97
+ end
98
+
99
+ if target.respond_to?(:encode_with)
100
+ dump_coder target
101
+ else
102
+ send(@dispatch_cache[target.class], target)
103
+ end
104
+ end
105
+
106
+ def visit_Psych_Omap o
107
+ seq = @emitter.start_sequence(nil, '!omap', false, Nodes::Sequence::BLOCK)
108
+ register(o, seq)
109
+
110
+ o.each { |k,v| visit_Hash k => v }
111
+ @emitter.end_sequence
112
+ end
113
+
114
+ def visit_Object o
115
+ tag = Psych.dump_tags[o.class]
116
+ unless tag
117
+ klass = o.class == Object ? nil : o.class.name
118
+ tag = ['!ruby/object', klass].compact.join(':')
119
+ end
120
+
121
+ map = @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK)
122
+ register(o, map)
123
+
124
+ dump_ivars o
125
+ @emitter.end_mapping
126
+ end
127
+
128
+ def visit_Struct o
129
+ tag = ['!ruby/struct', o.class.name].compact.join(':')
130
+
131
+ register o, @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK)
132
+ o.members.each do |member|
133
+ @emitter.scalar member.to_s, nil, nil, true, false, Nodes::Scalar::ANY
134
+ accept o[member]
135
+ end
136
+
137
+ dump_ivars o
138
+
139
+ @emitter.end_mapping
140
+ end
141
+
142
+ def visit_Exception o
143
+ tag = ['!ruby/exception', o.class.name].join ':'
144
+
145
+ @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK
146
+
147
+ {
148
+ 'message' => private_iv_get(o, 'mesg'),
149
+ 'backtrace' => private_iv_get(o, 'backtrace'),
150
+ }.each do |k,v|
151
+ next unless v
152
+ @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
153
+ accept v
154
+ end
155
+
156
+ dump_ivars o
157
+
158
+ @emitter.end_mapping
159
+ end
160
+
161
+ def visit_Regexp o
162
+ @emitter.scalar o.inspect, nil, '!ruby/regexp', false, false, Nodes::Scalar::ANY
163
+ end
164
+
165
+ def visit_DateTime o
166
+ formatted = format_time o.to_time
167
+ tag = '!ruby/object:DateTime'
168
+ @emitter.scalar formatted, nil, tag, false, false, Nodes::Scalar::ANY
169
+ end
170
+
171
+ def visit_Time o
172
+ formatted = format_time o
173
+ @emitter.scalar formatted, nil, nil, true, false, Nodes::Scalar::ANY
174
+ end
175
+
176
+ def visit_Rational o
177
+ @emitter.start_mapping(nil, '!ruby/object:Rational', false, Nodes::Mapping::BLOCK)
178
+
179
+ [
180
+ 'denominator', o.denominator.to_s,
181
+ 'numerator', o.numerator.to_s
182
+ ].each do |m|
183
+ @emitter.scalar m, nil, nil, true, false, Nodes::Scalar::ANY
184
+ end
185
+
186
+ @emitter.end_mapping
187
+ end
188
+
189
+ def visit_Complex o
190
+ @emitter.start_mapping(nil, '!ruby/object:Complex', false, Nodes::Mapping::BLOCK)
191
+
192
+ ['real', o.real.to_s, 'image', o.imag.to_s].each do |m|
193
+ @emitter.scalar m, nil, nil, true, false, Nodes::Scalar::ANY
194
+ end
195
+
196
+ @emitter.end_mapping
197
+ end
198
+
199
+ def visit_Integer o
200
+ @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY
201
+ end
202
+ alias :visit_TrueClass :visit_Integer
203
+ alias :visit_FalseClass :visit_Integer
204
+ alias :visit_Date :visit_Integer
205
+
206
+ def visit_Float o
207
+ if o.nan?
208
+ @emitter.scalar '.nan', nil, nil, true, false, Nodes::Scalar::ANY
209
+ elsif o.infinite?
210
+ @emitter.scalar((o.infinite? > 0 ? '.inf' : '-.inf'),
211
+ nil, nil, true, false, Nodes::Scalar::ANY)
212
+ else
213
+ @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY
214
+ end
215
+ end
216
+
217
+ def visit_String o
218
+ plain = false
219
+ quote = false
220
+ style = Nodes::Scalar::ANY
221
+
222
+ if o.index("\x00") || o.count("\x00-\x7F", "^ -~\t\r\n").fdiv(o.length) > 0.3
223
+ str = [o].pack('m').chomp
224
+ tag = '!binary' # FIXME: change to below when syck is removed
225
+ #tag = 'tag:yaml.org,2002:binary'
226
+ style = Nodes::Scalar::LITERAL
227
+ else
228
+ str = o
229
+ tag = nil
230
+ quote = !(String === @ss.tokenize(o))
231
+ plain = !quote
232
+ end
233
+
234
+ ivars = find_ivars o
235
+
236
+ if ivars.empty?
237
+ @emitter.scalar str, nil, tag, plain, quote, style
238
+ else
239
+ @emitter.start_mapping nil, '!str', false, Nodes::Mapping::BLOCK
240
+ @emitter.scalar 'str', nil, nil, true, false, Nodes::Scalar::ANY
241
+ @emitter.scalar str, nil, tag, plain, quote, style
242
+
243
+ dump_ivars o
244
+
245
+ @emitter.end_mapping
246
+ end
247
+ end
248
+
249
+ def visit_Class o
250
+ raise TypeError, "can't dump anonymous class #{o.class}"
251
+ end
252
+
253
+ def visit_Range o
254
+ @emitter.start_mapping nil, '!ruby/range', false, Nodes::Mapping::BLOCK
255
+ ['begin', o.begin, 'end', o.end, 'excl', o.exclude_end?].each do |m|
256
+ accept m
257
+ end
258
+ @emitter.end_mapping
259
+ end
260
+
261
+ def visit_Hash o
262
+ register(o, @emitter.start_mapping(nil, nil, true, Psych::Nodes::Mapping::BLOCK))
263
+
264
+ o.each do |k,v|
265
+ accept k
266
+ accept v
267
+ end
268
+
269
+ @emitter.end_mapping
270
+ end
271
+
272
+ def visit_Psych_Set o
273
+ register(o, @emitter.start_mapping(nil, '!set', false, Psych::Nodes::Mapping::BLOCK))
274
+
275
+ o.each do |k,v|
276
+ accept k
277
+ accept v
278
+ end
279
+
280
+ @emitter.end_mapping
281
+ end
282
+
283
+ def visit_Array o
284
+ register o, @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK)
285
+ o.each { |c| accept c }
286
+ @emitter.end_sequence
287
+ end
288
+
289
+ def visit_NilClass o
290
+ @emitter.scalar('', nil, 'tag:yaml.org,2002:null', true, false, Nodes::Scalar::ANY)
291
+ end
292
+
293
+ def visit_Symbol o
294
+ @emitter.scalar ":#{o}", nil, nil, true, false, Nodes::Scalar::ANY
295
+ end
296
+
297
+ private
298
+ def format_time time
299
+ if time.utc?
300
+ time.strftime("%Y-%m-%d %H:%M:%S.%9NZ")
301
+ else
302
+ time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z")
303
+ end
304
+ end
305
+
306
+ # FIXME: remove this method once "to_yaml_properties" is removed
307
+ def find_ivars target
308
+ begin
309
+ loc = target.method(:to_yaml_properties).source_location.first
310
+ unless loc.start_with?(Psych::DEPRECATED) || loc.end_with?('rubytypes.rb')
311
+ if $VERBOSE
312
+ warn "#{loc}: to_yaml_properties is deprecated, please implement \"encode_with(coder)\""
313
+ end
314
+ return target.to_yaml_properties
315
+ end
316
+ rescue
317
+ # public_method or source_location might be overridden,
318
+ # and it's OK to skip it since it's only to emit a warning.
319
+ end
320
+
321
+ target.instance_variables
322
+ end
323
+
324
+ def register target, yaml_obj
325
+ @st[target.object_id] = yaml_obj
326
+ yaml_obj
327
+ end
328
+
329
+ def dump_coder o
330
+ tag = Psych.dump_tags[o.class]
331
+ unless tag
332
+ klass = o.class == Object ? nil : o.class.name
333
+ tag = ['!ruby/object', klass].compact.join(':')
334
+ end
335
+
336
+ c = Psych::Coder.new(tag)
337
+ o.encode_with(c)
338
+ emit_coder c
339
+ end
340
+
341
+ def emit_coder c
342
+ case c.type
343
+ when :scalar
344
+ @emitter.scalar c.scalar, nil, c.tag, c.tag.nil?, false, Nodes::Scalar::ANY
345
+ when :seq
346
+ @emitter.start_sequence nil, c.tag, c.tag.nil?, Nodes::Sequence::BLOCK
347
+ c.seq.each do |thing|
348
+ accept thing
349
+ end
350
+ @emitter.end_sequence
351
+ when :map
352
+ @emitter.start_mapping nil, c.tag, c.implicit, c.style
353
+ c.map.each do |k,v|
354
+ @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
355
+ accept v
356
+ end
357
+ @emitter.end_mapping
358
+ when :object
359
+ accept c.object
360
+ end
361
+ end
362
+
363
+ def dump_ivars target
364
+ ivars = find_ivars target
365
+
366
+ ivars.each do |iv|
367
+ @emitter.scalar("#{iv.to_s.sub(/^@/, '')}", nil, nil, true, false, Nodes::Scalar::ANY)
368
+ accept target.instance_variable_get(iv)
369
+ end
370
+ end
371
+ end
372
+ end
373
+ end
@@ -0,0 +1,63 @@
1
+ require 'minitest/autorun'
2
+ require 'stringio'
3
+ require 'tempfile'
4
+ require 'date'
5
+
6
+ module Psych
7
+ class TestCase < MiniTest::Unit::TestCase
8
+ #
9
+ # Convert between Psych and the object to verify correct parsing and
10
+ # emitting
11
+ #
12
+ def assert_to_yaml( obj, yaml )
13
+ assert_equal( obj, Psych::load( yaml ) )
14
+ assert_equal( obj, Psych::parse( yaml ).transform )
15
+ assert_equal( obj, Psych::load( obj.psych_to_yaml ) )
16
+ assert_equal( obj, Psych::parse( obj.psych_to_yaml ).transform )
17
+ assert_equal( obj, Psych::load(
18
+ obj.psych_to_yaml(
19
+ :UseVersion => true, :UseHeader => true, :SortKeys => true
20
+ )
21
+ ))
22
+ end
23
+
24
+ #
25
+ # Test parser only
26
+ #
27
+ def assert_parse_only( obj, yaml )
28
+ assert_equal( obj, Psych::load( yaml ) )
29
+ assert_equal( obj, Psych::parse( yaml ).transform )
30
+ end
31
+
32
+ def assert_cycle( obj )
33
+ v = Visitors::YAMLTree.new
34
+ v << obj
35
+ assert_equal(obj, Psych.load(v.tree.to_yaml))
36
+ assert_equal( obj, Psych::load(Psych.dump(obj)))
37
+ assert_equal( obj, Psych::load( obj.psych_to_yaml ) )
38
+ end
39
+
40
+ #
41
+ # Make a time with the time zone
42
+ #
43
+ def mktime( year, mon, day, hour, min, sec, usec, zone = "Z" )
44
+ usec = Rational(usec.to_s) * 1000000
45
+ val = Time::utc( year.to_i, mon.to_i, day.to_i, hour.to_i, min.to_i, sec.to_i, usec )
46
+ if zone != "Z"
47
+ hour = zone[0,3].to_i * 3600
48
+ min = zone[3,2].to_i * 60
49
+ ofs = (hour + min)
50
+ val = Time.at( val.tv_sec - ofs, val.tv_nsec / 1000.0 )
51
+ end
52
+ return val
53
+ end
54
+ end
55
+ end
56
+
57
+ require 'psych'
58
+
59
+ # FIXME: remove this when syck is removed
60
+ o = Object.new
61
+ a = o.method(:psych_to_yaml)
62
+ b = o.method(:to_yaml)
63
+ raise "psych should define to_yaml" unless a == b