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
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