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
data/lib/psych/set.rb ADDED
@@ -0,0 +1,4 @@
1
+ module Psych
2
+ class Set < ::Hash
3
+ end
4
+ end
@@ -0,0 +1,36 @@
1
+ module Psych
2
+ ###
3
+ # Psych::Stream is a streaming YAML emitter. It will not buffer your YAML,
4
+ # but send it straight to an IO.
5
+ #
6
+ # Here is an example use:
7
+ #
8
+ # stream = Psych::Stream.new($stdout)
9
+ # stream.start
10
+ # stream.push({:foo => 'bar'})
11
+ # stream.finish
12
+ #
13
+ # YAML will be immediately emitted to $stdout with no buffering.
14
+ #
15
+ # Psych::Stream#start will take a block and ensure that Psych::Stream#finish
16
+ # is called, so you can do this form:
17
+ #
18
+ # stream = Psych::Stream.new($stdout)
19
+ # stream.start do |em|
20
+ # em.push(:foo => 'bar')
21
+ # end
22
+ #
23
+ class Stream < Psych::Visitors::YAMLTree
24
+ class Emitter < Psych::Emitter # :nodoc:
25
+ def end_document implicit_end = !streaming?
26
+ super
27
+ end
28
+
29
+ def streaming?
30
+ true
31
+ end
32
+ end
33
+
34
+ include Psych::Streaming
35
+ end
36
+ end
@@ -0,0 +1,22 @@
1
+ module Psych
2
+ module Streaming
3
+ ###
4
+ # Create a new streaming emitter. Emitter will print to +io+. See
5
+ # Psych::Stream for an example.
6
+ def initialize io
7
+ super({}, self.class.const_get(:Emitter).new(io))
8
+ end
9
+
10
+ ###
11
+ # Start streaming using +encoding+
12
+ def start encoding = Nodes::Stream::UTF8
13
+ super.tap { yield self if block_given? }
14
+ ensure
15
+ finish if block_given?
16
+ end
17
+
18
+ private
19
+ def register target, obj
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,94 @@
1
+ require 'psych/handler'
2
+
3
+ module Psych
4
+ ###
5
+ # This class works in conjunction with Psych::Parser to build an in-memory
6
+ # parse tree that represents a YAML document.
7
+ #
8
+ # == Example
9
+ #
10
+ # parser = Psych::Parser.new Psych::TreeBuilder.new
11
+ # parser.parse('--- foo')
12
+ # tree = parser.handler.root
13
+ #
14
+ # See Psych::Handler for documentation on the event methods used in this
15
+ # class.
16
+ class TreeBuilder < Psych::Handler
17
+ # Returns the root node for the built tree
18
+ attr_reader :root
19
+
20
+ # Create a new TreeBuilder instance
21
+ def initialize
22
+ @stack = []
23
+ @last = nil
24
+ @root = nil
25
+ end
26
+
27
+ %w{
28
+ Sequence
29
+ Mapping
30
+ }.each do |node|
31
+ class_eval %{
32
+ def start_#{node.downcase}(anchor, tag, implicit, style)
33
+ n = Nodes::#{node}.new(anchor, tag, implicit, style)
34
+ @last.children << n
35
+ push n
36
+ end
37
+
38
+ def end_#{node.downcase}
39
+ pop
40
+ end
41
+ }
42
+ end
43
+
44
+ ###
45
+ # Handles start_document events with +version+, +tag_directives+,
46
+ # and +implicit+ styling.
47
+ #
48
+ # See Psych::Handler#start_document
49
+ def start_document version, tag_directives, implicit
50
+ n = Nodes::Document.new version, tag_directives, implicit
51
+ @last.children << n
52
+ push n
53
+ end
54
+
55
+ ###
56
+ # Handles end_document events with +version+, +tag_directives+,
57
+ # and +implicit+ styling.
58
+ #
59
+ # See Psych::Handler#start_document
60
+ def end_document implicit_end = !streaming?
61
+ @last.implicit_end = implicit_end
62
+ pop
63
+ end
64
+
65
+ def start_stream encoding
66
+ @root = Nodes::Stream.new(encoding)
67
+ push @root
68
+ end
69
+
70
+ def end_stream
71
+ pop
72
+ end
73
+
74
+ def scalar value, anchor, tag, plain, quoted, style
75
+ @last.children << Nodes::Scalar.new(value,anchor,tag,plain,quoted,style)
76
+ end
77
+
78
+ def alias anchor
79
+ @last.children << Nodes::Alias.new(anchor)
80
+ end
81
+
82
+ private
83
+ def push value
84
+ @stack.push value
85
+ @last = value
86
+ end
87
+
88
+ def pop
89
+ x = @stack.pop
90
+ @last = @stack.last
91
+ x
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,6 @@
1
+ require 'psych/visitors/visitor'
2
+ require 'psych/visitors/to_ruby'
3
+ require 'psych/visitors/emitter'
4
+ require 'psych/visitors/yaml_tree'
5
+ require 'psych/visitors/json_tree'
6
+ require 'psych/visitors/depth_first'
@@ -0,0 +1,26 @@
1
+ module Psych
2
+ module Visitors
3
+ class DepthFirst < Psych::Visitors::Visitor
4
+ def initialize block
5
+ @block = block
6
+ end
7
+
8
+ private
9
+
10
+ def nary o
11
+ o.children.each { |x| visit x }
12
+ @block.call o
13
+ end
14
+ alias :visit_Psych_Nodes_Stream :nary
15
+ alias :visit_Psych_Nodes_Document :nary
16
+ alias :visit_Psych_Nodes_Sequence :nary
17
+ alias :visit_Psych_Nodes_Mapping :nary
18
+
19
+ def terminal o
20
+ @block.call o
21
+ end
22
+ alias :visit_Psych_Nodes_Scalar :terminal
23
+ alias :visit_Psych_Nodes_Alias :terminal
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,44 @@
1
+ module Psych
2
+ module Visitors
3
+ class Emitter < Psych::Visitors::Visitor
4
+ def initialize io, options = {}
5
+ @handler = Psych::Emitter.new io
6
+ @handler.indentation = options[:indentation] if options[:indentation]
7
+ @handler.canonical = options[:canonical] if options[:canonical]
8
+ @handler.line_width = options[:line_width] if options[:line_width]
9
+ end
10
+
11
+ def visit_Psych_Nodes_Stream o
12
+ @handler.start_stream o.encoding
13
+ o.children.each { |c| accept c }
14
+ @handler.end_stream
15
+ end
16
+
17
+ def visit_Psych_Nodes_Document o
18
+ @handler.start_document o.version, o.tag_directives, o.implicit
19
+ o.children.each { |c| accept c }
20
+ @handler.end_document o.implicit_end
21
+ end
22
+
23
+ def visit_Psych_Nodes_Scalar o
24
+ @handler.scalar o.value, o.anchor, o.tag, o.plain, o.quoted, o.style
25
+ end
26
+
27
+ def visit_Psych_Nodes_Sequence o
28
+ @handler.start_sequence o.anchor, o.tag, o.implicit, o.style
29
+ o.children.each { |c| accept c }
30
+ @handler.end_sequence
31
+ end
32
+
33
+ def visit_Psych_Nodes_Mapping o
34
+ @handler.start_mapping o.anchor, o.tag, o.implicit, o.style
35
+ o.children.each { |c| accept c }
36
+ @handler.end_mapping
37
+ end
38
+
39
+ def visit_Psych_Nodes_Alias o
40
+ @handler.alias o.anchor
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,21 @@
1
+ require 'psych/json/ruby_events'
2
+
3
+ module Psych
4
+ module Visitors
5
+ class JSONTree < YAMLTree
6
+ include Psych::JSON::RubyEvents
7
+
8
+ def initialize options = {}, emitter = Psych::JSON::TreeBuilder.new
9
+ super
10
+ end
11
+
12
+ def accept target
13
+ if target.respond_to?(:encode_with)
14
+ dump_coder target
15
+ else
16
+ send(@dispatch_cache[target.class], target)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,267 @@
1
+ require 'psych/scalar_scanner'
2
+
3
+ module Psych
4
+ module Visitors
5
+ ###
6
+ # This class walks a YAML AST, converting each node to ruby
7
+ class ToRuby < Psych::Visitors::Visitor
8
+ def initialize
9
+ super
10
+ @st = {}
11
+ @ss = ScalarScanner.new
12
+ @domain_types = Psych.domain_types
13
+ end
14
+
15
+ def accept target
16
+ result = super
17
+ return result if @domain_types.empty? || !target.tag
18
+
19
+ key = target.tag.sub(/^[!\/]*/, '').sub(/(,\d+)\//, '\1:')
20
+ key = "tag:#{key}" unless key =~ /^(tag:|x-private)/
21
+
22
+ if @domain_types.key? key
23
+ value, block = @domain_types[key]
24
+ return block.call value, result
25
+ end
26
+
27
+ result
28
+ end
29
+
30
+ def visit_Psych_Nodes_Scalar o
31
+ @st[o.anchor] = o.value if o.anchor
32
+
33
+ if klass = Psych.load_tags[o.tag]
34
+ instance = klass.allocate
35
+
36
+ if instance.respond_to?(:init_with)
37
+ coder = Psych::Coder.new(o.tag)
38
+ coder.scalar = o.value
39
+ instance.init_with coder
40
+ end
41
+
42
+ return instance
43
+ end
44
+
45
+ return o.value if o.quoted
46
+ return @ss.tokenize(o.value) unless o.tag
47
+
48
+ case o.tag
49
+ when '!binary', 'tag:yaml.org,2002:binary'
50
+ o.value.unpack('m').first
51
+ when '!str', 'tag:yaml.org,2002:str'
52
+ o.value
53
+ when "!ruby/object:DateTime"
54
+ require 'date'
55
+ @ss.parse_time(o.value).to_datetime
56
+ when "!ruby/object:Complex"
57
+ Complex(o.value)
58
+ when "!ruby/object:Rational"
59
+ Rational(o.value)
60
+ when "tag:yaml.org,2002:float", "!float"
61
+ Float(@ss.tokenize(o.value))
62
+ when "!ruby/regexp"
63
+ o.value =~ /^\/(.*)\/([mixn]*)$/
64
+ source = $1
65
+ options = 0
66
+ lang = nil
67
+ ($2 || '').split('').each do |option|
68
+ case option
69
+ when 'x' then options |= Regexp::EXTENDED
70
+ when 'i' then options |= Regexp::IGNORECASE
71
+ when 'm' then options |= Regexp::MULTILINE
72
+ when 'n' then options |= Regexp::NOENCODING
73
+ else lang = option
74
+ end
75
+ end
76
+ Regexp.new(*[source, options, lang].compact)
77
+ when "!ruby/range"
78
+ args = o.value.split(/([.]{2,3})/, 2).map { |s|
79
+ accept Nodes::Scalar.new(s)
80
+ }
81
+ args.push(args.delete_at(1) == '...')
82
+ Range.new(*args)
83
+ when /^!ruby\/sym(bol)?:?(.*)?$/
84
+ o.value.to_sym
85
+ else
86
+ @ss.tokenize o.value
87
+ end
88
+ end
89
+
90
+ def visit_Psych_Nodes_Sequence o
91
+ if klass = Psych.load_tags[o.tag]
92
+ instance = klass.allocate
93
+
94
+ if instance.respond_to?(:init_with)
95
+ coder = Psych::Coder.new(o.tag)
96
+ coder.seq = o.children.map { |c| accept c }
97
+ instance.init_with coder
98
+ end
99
+
100
+ return instance
101
+ end
102
+
103
+ case o.tag
104
+ when '!omap', 'tag:yaml.org,2002:omap'
105
+ map = Psych::Omap.new
106
+ @st[o.anchor] = map if o.anchor
107
+ o.children.each { |a|
108
+ map[accept(a.children.first)] = accept a.children.last
109
+ }
110
+ map
111
+ else
112
+ list = []
113
+ @st[o.anchor] = list if o.anchor
114
+ o.children.each { |c| list.push accept c }
115
+ list
116
+ end
117
+ end
118
+
119
+ def visit_Psych_Nodes_Mapping o
120
+ return revive(Psych.load_tags[o.tag], o) if Psych.load_tags[o.tag]
121
+
122
+ case o.tag
123
+ when '!str', 'tag:yaml.org,2002:str'
124
+ members = Hash[*o.children.map { |c| accept c }]
125
+ string = members.delete 'str'
126
+ init_with(string, members.map { |k,v| [k.to_s.sub(/^@/, ''),v] }, o)
127
+ when /^!ruby\/struct:?(.*)?$/
128
+ klass = resolve_class($1)
129
+
130
+ if klass
131
+ s = klass.allocate
132
+ @st[o.anchor] = s if o.anchor
133
+
134
+ members = {}
135
+ struct_members = s.members.map { |x| x.to_sym }
136
+ o.children.each_slice(2) do |k,v|
137
+ member = accept(k)
138
+ value = accept(v)
139
+ if struct_members.include?(member.to_sym)
140
+ s.send("#{member}=", value)
141
+ else
142
+ members[member.to_s.sub(/^@/, '')] = value
143
+ end
144
+ end
145
+ init_with(s, members, o)
146
+ else
147
+ members = o.children.map { |c| accept c }
148
+ h = Hash[*members]
149
+ Struct.new(*h.map { |k,v| k.to_sym }).new(*h.map { |k,v| v })
150
+ end
151
+
152
+ when '!ruby/range'
153
+ h = Hash[*o.children.map { |c| accept c }]
154
+ Range.new(h['begin'], h['end'], h['excl'])
155
+
156
+ when /^!ruby\/exception:?(.*)?$/
157
+ h = Hash[*o.children.map { |c| accept c }]
158
+
159
+ e = build_exception((resolve_class($1) || Exception),
160
+ h.delete('message'))
161
+ init_with(e, h, o)
162
+
163
+ when '!set', 'tag:yaml.org,2002:set'
164
+ set = Psych::Set.new
165
+ @st[o.anchor] = set if o.anchor
166
+ o.children.each_slice(2) do |k,v|
167
+ set[accept(k)] = accept(v)
168
+ end
169
+ set
170
+
171
+ when '!ruby/object:Complex'
172
+ h = Hash[*o.children.map { |c| accept c }]
173
+ Complex(h['real'], h['image'])
174
+
175
+ when '!ruby/object:Rational'
176
+ h = Hash[*o.children.map { |c| accept c }]
177
+ Rational(h['numerator'], h['denominator'])
178
+
179
+ when /^!ruby\/object:?(.*)?$/
180
+ name = $1 || 'Object'
181
+ obj = revive((resolve_class(name) || Object), o)
182
+ @st[o.anchor] = obj if o.anchor
183
+ obj
184
+ else
185
+ hash = {}
186
+ @st[o.anchor] = hash if o.anchor
187
+
188
+ o.children.each_slice(2) { |k,v|
189
+ key = accept(k)
190
+
191
+ if key == '<<'
192
+ case v
193
+ when Nodes::Alias
194
+ hash.merge! accept(v)
195
+ when Nodes::Sequence
196
+ accept(v).reverse_each do |value|
197
+ hash.merge! value
198
+ end
199
+ else
200
+ hash[key] = accept(v)
201
+ end
202
+ else
203
+ hash[key] = accept(v)
204
+ end
205
+
206
+ }
207
+ hash
208
+ end
209
+ end
210
+
211
+ def visit_Psych_Nodes_Document o
212
+ accept o.root
213
+ end
214
+
215
+ def visit_Psych_Nodes_Stream o
216
+ o.children.map { |c| accept c }
217
+ end
218
+
219
+ def visit_Psych_Nodes_Alias o
220
+ @st[o.anchor]
221
+ end
222
+
223
+ private
224
+ def revive klass, node
225
+ s = klass.allocate
226
+ h = Hash[*node.children.map { |c| accept c }]
227
+ init_with(s, h, node)
228
+ end
229
+
230
+ def init_with o, h, node
231
+ c = Psych::Coder.new(node.tag)
232
+ c.map = h
233
+
234
+ if o.respond_to?(:init_with)
235
+ o.init_with c
236
+ elsif o.respond_to?(:yaml_initialize)
237
+ if $VERBOSE
238
+ "Implementing #{o.class}#yaml_initialize is deprecated, please implement \"init_with(coder)\""
239
+ end
240
+ o.yaml_initialize c.tag, c.map
241
+ else
242
+ h.each { |k,v| o.instance_variable_set(:"@#{k}", v) }
243
+ end
244
+ o
245
+ end
246
+
247
+ # Convert +klassname+ to a Class
248
+ def resolve_class klassname
249
+ return nil unless klassname and not klassname.empty?
250
+
251
+ name = klassname
252
+ retried = false
253
+
254
+ begin
255
+ path2class(name)
256
+ rescue ArgumentError, NameError => ex
257
+ unless retried
258
+ name = "Struct::#{name}"
259
+ retried = ex
260
+ retry
261
+ end
262
+ raise retried
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end