km-psych 0.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 (76) hide show
  1. data/README.rdoc +129 -0
  2. data/ext/psych/emitter.c +488 -0
  3. data/ext/psych/emitter.h +8 -0
  4. data/ext/psych/extconf.rb +22 -0
  5. data/ext/psych/parser.c +349 -0
  6. data/ext/psych/parser.h +6 -0
  7. data/ext/psych/psych.c +34 -0
  8. data/ext/psych/psych.h +20 -0
  9. data/ext/psych/to_ruby.c +41 -0
  10. data/ext/psych/to_ruby.h +8 -0
  11. data/ext/psych/yaml_tree.c +24 -0
  12. data/ext/psych/yaml_tree.h +8 -0
  13. data/lib/km-psych.rb +244 -0
  14. data/lib/psych/coder.rb +86 -0
  15. data/lib/psych/core_ext.rb +38 -0
  16. data/lib/psych/deprecated.rb +82 -0
  17. data/lib/psych/handler.rb +221 -0
  18. data/lib/psych/json.rb +6 -0
  19. data/lib/psych/json/stream.rb +32 -0
  20. data/lib/psych/json/tree_builder.rb +32 -0
  21. data/lib/psych/nodes.rb +77 -0
  22. data/lib/psych/nodes/alias.rb +18 -0
  23. data/lib/psych/nodes/document.rb +60 -0
  24. data/lib/psych/nodes/mapping.rb +56 -0
  25. data/lib/psych/nodes/node.rb +42 -0
  26. data/lib/psych/nodes/scalar.rb +67 -0
  27. data/lib/psych/nodes/sequence.rb +81 -0
  28. data/lib/psych/nodes/stream.rb +37 -0
  29. data/lib/psych/omap.rb +4 -0
  30. data/lib/psych/parser.rb +44 -0
  31. data/lib/psych/scalar_scanner.rb +105 -0
  32. data/lib/psych/set.rb +4 -0
  33. data/lib/psych/stream.rb +53 -0
  34. data/lib/psych/tree_builder.rb +94 -0
  35. data/lib/psych/visitors.rb +5 -0
  36. data/lib/psych/visitors/emitter.rb +41 -0
  37. data/lib/psych/visitors/json_tree.rb +14 -0
  38. data/lib/psych/visitors/to_ruby.rb +263 -0
  39. data/lib/psych/visitors/visitor.rb +27 -0
  40. data/lib/psych/visitors/yaml_tree.rb +342 -0
  41. data/test/psych/helper.rb +63 -0
  42. data/test/psych/json/test_stream.rb +75 -0
  43. data/test/psych/test_alias_and_anchor.rb +26 -0
  44. data/test/psych/test_array.rb +19 -0
  45. data/test/psych/test_boolean.rb +36 -0
  46. data/test/psych/test_class.rb +17 -0
  47. data/test/psych/test_coder.rb +169 -0
  48. data/test/psych/test_date_time.rb +17 -0
  49. data/test/psych/test_deprecated.rb +210 -0
  50. data/test/psych/test_document.rb +46 -0
  51. data/test/psych/test_emitter.rb +88 -0
  52. data/test/psych/test_encoding.rb +179 -0
  53. data/test/psych/test_engine_manager.rb +57 -0
  54. data/test/psych/test_exception.rb +39 -0
  55. data/test/psych/test_hash.rb +30 -0
  56. data/test/psych/test_json_tree.rb +43 -0
  57. data/test/psych/test_null.rb +19 -0
  58. data/test/psych/test_object.rb +27 -0
  59. data/test/psych/test_omap.rb +68 -0
  60. data/test/psych/test_parser.rb +216 -0
  61. data/test/psych/test_psych.rb +133 -0
  62. data/test/psych/test_scalar.rb +11 -0
  63. data/test/psych/test_scalar_scanner.rb +70 -0
  64. data/test/psych/test_serialize_subclasses.rb +38 -0
  65. data/test/psych/test_set.rb +49 -0
  66. data/test/psych/test_stream.rb +49 -0
  67. data/test/psych/test_string.rb +49 -0
  68. data/test/psych/test_struct.rb +51 -0
  69. data/test/psych/test_symbol.rb +17 -0
  70. data/test/psych/test_to_yaml_properties.rb +63 -0
  71. data/test/psych/test_tree_builder.rb +79 -0
  72. data/test/psych/test_yaml.rb +1251 -0
  73. data/test/psych/visitors/test_emitter.rb +124 -0
  74. data/test/psych/visitors/test_to_ruby.rb +325 -0
  75. data/test/psych/visitors/test_yaml_tree.rb +149 -0
  76. metadata +187 -0
@@ -0,0 +1,27 @@
1
+ module Psych
2
+ module Visitors
3
+ class Visitor
4
+ attr_reader :started, :finished
5
+ alias :finished? :finished
6
+ alias :started? :started
7
+
8
+ def initialize
9
+ @started = false
10
+ @finished = false
11
+ end
12
+
13
+ def accept target
14
+ case target
15
+ when Psych::Nodes::Scalar then visit_Psych_Nodes_Scalar target
16
+ when Psych::Nodes::Mapping then visit_Psych_Nodes_Mapping target
17
+ when Psych::Nodes::Sequence then visit_Psych_Nodes_Sequence target
18
+ when Psych::Nodes::Alias then visit_Psych_Nodes_Alias target
19
+ when Psych::Nodes::Document then visit_Psych_Nodes_Document target
20
+ when Psych::Nodes::Stream then visit_Psych_Nodes_Stream target
21
+ else
22
+ raise "Can't handle #{target}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,342 @@
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
+ def initialize options = {}, emitter = Psych::TreeBuilder.new
12
+ super()
13
+ @emitter = emitter
14
+ @st = {}
15
+ @ss = ScalarScanner.new
16
+
17
+ @dispatch_cache = Hash.new do |h,klass|
18
+ method = "visit_#{(klass.name || '').split('::').join('_')}"
19
+
20
+ method = respond_to?(method) ? method : h[klass.superclass]
21
+
22
+ raise(TypeError, "Can't dump #{target.class}") unless method
23
+
24
+ h[klass] = method
25
+ end
26
+ end
27
+
28
+ def start encoding = Nodes::Stream::UTF8
29
+ @emitter.start_stream(encoding).tap do
30
+ @started = true
31
+ end
32
+ end
33
+
34
+ def finish
35
+ @emitter.end_stream.tap do
36
+ @finished = true
37
+ end
38
+ end
39
+
40
+ def tree
41
+ finish unless finished?
42
+ end
43
+
44
+ def push object
45
+ start unless started?
46
+ @emitter.start_document [], [], false
47
+ accept object
48
+ @emitter.end_document
49
+ end
50
+ alias :<< :push
51
+
52
+ def accept target
53
+ # return any aliases we find
54
+ if node = @st[target.object_id]
55
+ node.anchor = target.object_id.to_s
56
+ return @emitter.alias target.object_id.to_s
57
+ end
58
+
59
+ if target.respond_to?(:to_yaml)
60
+ loc = target.method(:to_yaml).source_location.first
61
+ if loc !~ /(syck\/rubytypes.rb|psych\/core_ext.rb)/
62
+ unless target.respond_to?(:encode_with)
63
+ if $VERBOSE
64
+ warn "implementing to_yaml is deprecated, please implement \"encode_with\""
65
+ end
66
+
67
+ target.to_yaml(:nodump => true)
68
+ end
69
+ end
70
+ end
71
+
72
+ if target.respond_to?(:encode_with)
73
+ dump_coder target
74
+ else
75
+ send(@dispatch_cache[target.class], target)
76
+ end
77
+ end
78
+
79
+ def visit_Psych_Omap o
80
+ seq = @emitter.start_sequence(nil, '!omap', false, Nodes::Sequence::BLOCK)
81
+ register(o, seq)
82
+
83
+ o.each { |k,v| visit_Hash k => v }
84
+ @emitter.end_sequence
85
+ end
86
+
87
+ def visit_Object o
88
+ tag = Psych.dump_tags[o.class]
89
+ unless tag
90
+ klass = o.class == Object ? nil : o.class.name
91
+ tag = ['!ruby/object', klass].compact.join(':')
92
+ end
93
+
94
+ map = @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK)
95
+ register(o, map)
96
+
97
+ dump_ivars o
98
+ @emitter.end_mapping
99
+ end
100
+
101
+ def visit_Struct o
102
+ tag = ['!ruby/struct', o.class.name].compact.join(':')
103
+
104
+ register o, @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK)
105
+ o.members.each do |member|
106
+ @emitter.scalar member.to_s, nil, nil, true, false, Nodes::Scalar::ANY
107
+ accept o[member]
108
+ end
109
+
110
+ dump_ivars o
111
+
112
+ @emitter.end_mapping
113
+ end
114
+
115
+ def visit_Exception o
116
+ tag = ['!ruby/exception', o.class.name].join ':'
117
+
118
+ @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK
119
+
120
+ {
121
+ 'message' => private_iv_get(o, 'mesg'),
122
+ 'backtrace' => private_iv_get(o, 'backtrace'),
123
+ }.each do |k,v|
124
+ next unless v
125
+ @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
126
+ accept v
127
+ end
128
+
129
+ dump_ivars o
130
+
131
+ @emitter.end_mapping
132
+ end
133
+
134
+ def visit_Regexp o
135
+ @emitter.scalar o.inspect, nil, '!ruby/regexp', false, false, Nodes::Scalar::ANY
136
+ end
137
+
138
+ def visit_DateTime o
139
+ formatted = format_time o.to_time
140
+ tag = '!ruby/object:DateTime'
141
+ @emitter.scalar formatted, nil, tag, false, false, Nodes::Scalar::ANY
142
+ end
143
+
144
+ def visit_Time o
145
+ formatted = format_time o
146
+ @emitter.scalar formatted, nil, nil, true, false, Nodes::Scalar::ANY
147
+ end
148
+
149
+ def visit_Rational o
150
+ @emitter.start_mapping(nil, '!ruby/object:Rational', false, Nodes::Mapping::BLOCK)
151
+
152
+ [
153
+ 'denominator', o.denominator.to_s,
154
+ 'numerator', o.numerator.to_s
155
+ ].each do |m|
156
+ @emitter.scalar m, nil, nil, true, false, Nodes::Scalar::ANY
157
+ end
158
+
159
+ @emitter.end_mapping
160
+ end
161
+
162
+ def visit_Complex o
163
+ @emitter.start_mapping(nil, '!ruby/object:Complex', false, Nodes::Mapping::BLOCK)
164
+
165
+ ['real', o.real.to_s, 'image', o.imag.to_s].each do |m|
166
+ @emitter.scalar m, nil, nil, true, false, Nodes::Scalar::ANY
167
+ end
168
+
169
+ @emitter.end_mapping
170
+ end
171
+
172
+ def visit_Integer o
173
+ @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY
174
+ end
175
+ alias :visit_TrueClass :visit_Integer
176
+ alias :visit_FalseClass :visit_Integer
177
+ alias :visit_Date :visit_Integer
178
+
179
+ def visit_Float o
180
+ if o.nan?
181
+ @emitter.scalar '.nan', nil, nil, true, false, Nodes::Scalar::ANY
182
+ elsif o.infinite?
183
+ @emitter.scalar((o.infinite? > 0 ? '.inf' : '-.inf'),
184
+ nil, nil, true, false, Nodes::Scalar::ANY)
185
+ else
186
+ @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY
187
+ end
188
+ end
189
+
190
+ def visit_String o
191
+ plain = false
192
+ quote = false
193
+ style = Nodes::Scalar::ANY
194
+
195
+ if o.index("\x00") || o.count("^ -~\t\r\n").fdiv(o.length) > 0.3
196
+ str = [o].pack('m').chomp
197
+ tag = '!binary' # FIXME: change to below when syck is removed
198
+ #tag = 'tag:yaml.org,2002:binary'
199
+ style = Nodes::Scalar::LITERAL
200
+ else
201
+ str = o
202
+ tag = nil
203
+ quote = !(String === @ss.tokenize(o))
204
+ plain = !quote
205
+ end
206
+
207
+ ivars = find_ivars o
208
+
209
+ if ivars.empty?
210
+ @emitter.scalar str, nil, tag, plain, quote, style
211
+ else
212
+ @emitter.start_mapping nil, '!str', false, Nodes::Mapping::BLOCK
213
+ @emitter.scalar 'str', nil, nil, true, false, Nodes::Scalar::ANY
214
+ @emitter.scalar str, nil, tag, plain, quote, style
215
+
216
+ dump_ivars o
217
+
218
+ @emitter.end_mapping
219
+ end
220
+ end
221
+
222
+ def visit_Class o
223
+ raise TypeError, "can't dump anonymous class #{o.class}"
224
+ end
225
+
226
+ def visit_Range o
227
+ @emitter.start_mapping nil, '!ruby/range', false, Nodes::Mapping::BLOCK
228
+ ['begin', o.begin, 'end', o.end, 'excl', o.exclude_end?].each do |m|
229
+ accept m
230
+ end
231
+ @emitter.end_mapping
232
+ end
233
+
234
+ def visit_Hash o
235
+ register(o, @emitter.start_mapping(nil, nil, true, Psych::Nodes::Mapping::BLOCK))
236
+
237
+ o.each do |k,v|
238
+ accept k
239
+ accept v
240
+ end
241
+
242
+ @emitter.end_mapping
243
+ end
244
+
245
+ def visit_Psych_Set o
246
+ register(o, @emitter.start_mapping(nil, '!set', false, Psych::Nodes::Mapping::BLOCK))
247
+
248
+ o.each do |k,v|
249
+ accept k
250
+ accept v
251
+ end
252
+
253
+ @emitter.end_mapping
254
+ end
255
+
256
+ def visit_Array o
257
+ register o, @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK)
258
+ o.each { |c| accept c }
259
+ @emitter.end_sequence
260
+ end
261
+
262
+ def visit_NilClass o
263
+ @emitter.scalar('', nil, 'tag:yaml.org,2002:null', false, false, Nodes::Scalar::ANY)
264
+ end
265
+
266
+ def visit_Symbol o
267
+ @emitter.scalar ":#{o}", nil, nil, true, false, Nodes::Scalar::ANY
268
+ end
269
+
270
+ private
271
+ def format_time time
272
+ formatted = time.strftime("%Y-%m-%d %H:%M:%S.%9N")
273
+ if time.utc?
274
+ formatted += "Z"
275
+ else
276
+ zone = time.strftime('%z')
277
+ formatted += " #{zone[0,3]}:#{zone[3,5]}"
278
+ end
279
+ formatted
280
+ end
281
+
282
+ # FIXME: remove this method once "to_yaml_properties" is removed
283
+ def find_ivars target
284
+ loc = target.method(:to_yaml_properties).source_location.first
285
+ unless loc.start_with?(Psych::DEPRECATED) || loc.end_with?('rubytypes.rb')
286
+ if $VERBOSE
287
+ warn "#{loc}: to_yaml_properties is deprecated, please implement \"encode_with(coder)\""
288
+ end
289
+ return target.to_yaml_properties
290
+ end
291
+
292
+ target.instance_variables
293
+ end
294
+
295
+ def register target, yaml_obj
296
+ @st[target.object_id] = yaml_obj
297
+ yaml_obj
298
+ end
299
+
300
+ def dump_coder o
301
+ tag = Psych.dump_tags[o.class]
302
+ unless tag
303
+ klass = o.class == Object ? nil : o.class.name
304
+ tag = ['!ruby/object', klass].compact.join(':')
305
+ end
306
+
307
+ c = Psych::Coder.new(tag)
308
+ o.encode_with(c)
309
+ emit_coder c
310
+ end
311
+
312
+ def emit_coder c
313
+ case c.type
314
+ when :scalar
315
+ @emitter.scalar c.scalar, nil, c.tag, c.tag.nil?, false, Nodes::Scalar::ANY
316
+ when :seq
317
+ @emitter.start_sequence nil, c.tag, c.tag.nil?, Nodes::Sequence::BLOCK
318
+ c.seq.each do |thing|
319
+ accept thing
320
+ end
321
+ @emitter.end_sequence
322
+ when :map
323
+ @emitter.start_mapping nil, c.tag, c.implicit, c.style
324
+ c.map.each do |k,v|
325
+ @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
326
+ accept v
327
+ end
328
+ @emitter.end_mapping
329
+ end
330
+ end
331
+
332
+ def dump_ivars target
333
+ ivars = find_ivars target
334
+
335
+ ivars.each do |iv|
336
+ @emitter.scalar("#{iv.to_s.sub(/^@/, '')}", nil, nil, true, false, Nodes::Scalar::ANY)
337
+ accept target.instance_variable_get(iv)
338
+ end
339
+ end
340
+ end
341
+ end
342
+ 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
@@ -0,0 +1,75 @@
1
+ require_relative '../helper'
2
+
3
+ module Psych
4
+ module JSON
5
+ class TestStream < TestCase
6
+ def setup
7
+ @io = StringIO.new
8
+ @stream = Psych::JSON::Stream.new(@io)
9
+ @stream.start
10
+ end
11
+
12
+ def test_explicit_documents
13
+ @io = StringIO.new
14
+ @stream = Psych::JSON::Stream.new(@io)
15
+ @stream.start
16
+
17
+ @stream.push({ 'foo' => 'bar' })
18
+
19
+ assert !@stream.finished?, 'stream not finished'
20
+ @stream.finish
21
+ assert @stream.finished?, 'stream finished'
22
+
23
+ assert_match(/^---/, @io.string)
24
+ assert_match(/\.\.\.$/, @io.string)
25
+ end
26
+
27
+ def test_null
28
+ @stream.push(nil)
29
+ assert_match(/^--- null/, @io.string)
30
+ end
31
+
32
+ def test_string
33
+ @stream.push "foo"
34
+ assert_match(/(['"])foo\1/, @io.string)
35
+ end
36
+
37
+ def test_symbol
38
+ @stream.push :foo
39
+ assert_match(/(['"])foo\1/, @io.string)
40
+ end
41
+
42
+ def test_int
43
+ @stream.push 10
44
+ assert_match(/^--- 10/, @io.string)
45
+ end
46
+
47
+ def test_float
48
+ @stream.push 1.2
49
+ assert_match(/^--- 1.2/, @io.string)
50
+ end
51
+
52
+ def test_hash
53
+ hash = { 'one' => 'two' }
54
+ @stream.push hash
55
+
56
+ json = @io.string
57
+ assert_match(/}$/, json)
58
+ assert_match(/^--- \{/, json)
59
+ assert_match(/['"]one['"]/, json)
60
+ assert_match(/['"]two['"]/, json)
61
+ end
62
+
63
+ def test_list_to_json
64
+ list = %w{ one two }
65
+ @stream.push list
66
+
67
+ json = @io.string
68
+ assert_match(/]$/, json)
69
+ assert_match(/^--- \[/, json)
70
+ assert_match(/['"]one['"]/, json)
71
+ assert_match(/['"]two['"]/, json)
72
+ end
73
+ end
74
+ end
75
+ end