psych 1.1.0

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