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,53 @@
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
+ ###
35
+ # Create a new streaming emitter. Emitter will print to +io+. See
36
+ # Psych::Stream for an example.
37
+ def initialize io
38
+ super({}, self.class.const_get(:Emitter).new(io))
39
+ end
40
+
41
+ ###
42
+ # Start streaming using +encoding+
43
+ def start encoding = Nodes::Stream::UTF8
44
+ super.tap { yield self if block_given? }
45
+ ensure
46
+ finish if block_given?
47
+ end
48
+
49
+ private
50
+ def register target, obj
51
+ end
52
+ end
53
+ 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,5 @@
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'
@@ -0,0 +1,41 @@
1
+ module Psych
2
+ module Visitors
3
+ class Emitter < Psych::Visitors::Visitor
4
+ def initialize io
5
+ @handler = Psych::Emitter.new io
6
+ end
7
+
8
+ def visit_Psych_Nodes_Stream o
9
+ @handler.start_stream o.encoding
10
+ o.children.each { |c| accept c }
11
+ @handler.end_stream
12
+ end
13
+
14
+ def visit_Psych_Nodes_Document o
15
+ @handler.start_document o.version, o.tag_directives, o.implicit
16
+ o.children.each { |c| accept c }
17
+ @handler.end_document o.implicit_end
18
+ end
19
+
20
+ def visit_Psych_Nodes_Scalar o
21
+ @handler.scalar o.value, o.anchor, o.tag, o.plain, o.quoted, o.style
22
+ end
23
+
24
+ def visit_Psych_Nodes_Sequence o
25
+ @handler.start_sequence o.anchor, o.tag, o.implicit, o.style
26
+ o.children.each { |c| accept c }
27
+ @handler.end_sequence
28
+ end
29
+
30
+ def visit_Psych_Nodes_Mapping o
31
+ @handler.start_mapping o.anchor, o.tag, o.implicit, o.style
32
+ o.children.each { |c| accept c }
33
+ @handler.end_mapping
34
+ end
35
+
36
+ def visit_Psych_Nodes_Alias o
37
+ @handler.alias o.anchor
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,14 @@
1
+ module Psych
2
+ module Visitors
3
+ class JSONTree < YAMLTree
4
+ def initialize options = {}, emitter = Psych::JSON::TreeBuilder.new
5
+ super
6
+ end
7
+
8
+ def visit_String o
9
+ @emitter.scalar o.to_s, nil, nil, false, true, Nodes::Scalar::ANY
10
+ end
11
+ alias :visit_Symbol :visit_String
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,263 @@
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 =~ /^\/(.*)\/([mix]*)$/
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
+ else lang = option
73
+ end
74
+ end
75
+ Regexp.new(*[source, options, lang].compact)
76
+ when "!ruby/range"
77
+ args = o.value.split(/([.]{2,3})/, 2).map { |s|
78
+ accept Nodes::Scalar.new(s)
79
+ }
80
+ args.push(args.delete_at(1) == '...')
81
+ Range.new(*args)
82
+ when /^!ruby\/sym(bol)?:?(.*)?$/
83
+ o.value.to_sym
84
+ else
85
+ @ss.tokenize o.value
86
+ end
87
+ end
88
+
89
+ def visit_Psych_Nodes_Sequence o
90
+ if klass = Psych.load_tags[o.tag]
91
+ instance = klass.allocate
92
+
93
+ if instance.respond_to?(:init_with)
94
+ coder = Psych::Coder.new(o.tag)
95
+ coder.seq = o.children.map { |c| accept c }
96
+ instance.init_with coder
97
+ end
98
+
99
+ return instance
100
+ end
101
+
102
+ case o.tag
103
+ when '!omap', 'tag:yaml.org,2002:omap'
104
+ map = Psych::Omap.new
105
+ @st[o.anchor] = map if o.anchor
106
+ o.children.each { |a|
107
+ map[accept(a.children.first)] = accept a.children.last
108
+ }
109
+ map
110
+ else
111
+ list = []
112
+ @st[o.anchor] = list if o.anchor
113
+ o.children.each { |c| list.push accept c }
114
+ list
115
+ end
116
+ end
117
+
118
+ def visit_Psych_Nodes_Mapping o
119
+ return revive(Psych.load_tags[o.tag], o) if Psych.load_tags[o.tag]
120
+
121
+ case o.tag
122
+ when '!str', 'tag:yaml.org,2002:str'
123
+ members = Hash[*o.children.map { |c| accept c }]
124
+ string = members.delete 'str'
125
+ init_with(string, members.map { |k,v| [k.to_s.sub(/^@/, ''),v] }, o)
126
+ when /^!ruby\/struct:?(.*)?$/
127
+ klass = resolve_class($1)
128
+
129
+ if klass
130
+ s = klass.allocate
131
+ @st[o.anchor] = s if o.anchor
132
+
133
+ members = {}
134
+ struct_members = s.members.map { |x| x.to_sym }
135
+ o.children.each_slice(2) do |k,v|
136
+ member = accept(k)
137
+ value = accept(v)
138
+ if struct_members.include?(member.to_sym)
139
+ s.send("#{member}=", value)
140
+ else
141
+ members[member.to_s.sub(/^@/, '')] = value
142
+ end
143
+ end
144
+ init_with(s, members, o)
145
+ else
146
+ members = o.children.map { |c| accept c }
147
+ h = Hash[*members]
148
+ Struct.new(*h.map { |k,v| k.to_sym }).new(*h.map { |k,v| v })
149
+ end
150
+
151
+ when '!ruby/range'
152
+ h = Hash[*o.children.map { |c| accept c }]
153
+ Range.new(h['begin'], h['end'], h['excl'])
154
+
155
+ when /^!ruby\/exception:?(.*)?$/
156
+ h = Hash[*o.children.map { |c| accept c }]
157
+
158
+ e = build_exception((resolve_class($1) || Exception),
159
+ h.delete('message'))
160
+ init_with(e, h, o)
161
+
162
+ when '!set', 'tag:yaml.org,2002:set'
163
+ set = Psych::Set.new
164
+ @st[o.anchor] = set if o.anchor
165
+ o.children.each_slice(2) do |k,v|
166
+ set[accept(k)] = accept(v)
167
+ end
168
+ set
169
+
170
+ when '!ruby/object:Complex'
171
+ h = Hash[*o.children.map { |c| accept c }]
172
+ Complex(h['real'], h['image'])
173
+
174
+ when '!ruby/object:Rational'
175
+ h = Hash[*o.children.map { |c| accept c }]
176
+ Rational(h['numerator'], h['denominator'])
177
+
178
+ when /^!ruby\/object:?(.*)?$/
179
+ name = $1 || 'Object'
180
+ obj = revive((resolve_class(name) || Object), o)
181
+ @st[o.anchor] = obj if o.anchor
182
+ obj
183
+ else
184
+ hash = {}
185
+ @st[o.anchor] = hash if o.anchor
186
+
187
+ o.children.each_slice(2) { |k,v|
188
+ key = accept(k)
189
+
190
+ if key == '<<' && Nodes::Alias === v
191
+ # FIXME: remove this when "<<" syntax is deprecated
192
+ if $VERBOSE
193
+ where = caller.find { |x| x !~ /psych/ }
194
+ warn where
195
+ warn "\"<<: *#{v.anchor}\" is no longer supported, please switch to \"*#{v.anchor}\""
196
+ end
197
+ return accept(v)
198
+ else
199
+ hash[key] = accept(v)
200
+ end
201
+
202
+ }
203
+ hash
204
+ end
205
+ end
206
+
207
+ def visit_Psych_Nodes_Document o
208
+ accept o.root
209
+ end
210
+
211
+ def visit_Psych_Nodes_Stream o
212
+ o.children.map { |c| accept c }
213
+ end
214
+
215
+ def visit_Psych_Nodes_Alias o
216
+ @st[o.anchor]
217
+ end
218
+
219
+ private
220
+ def revive klass, node
221
+ s = klass.allocate
222
+ h = Hash[*node.children.map { |c| accept c }]
223
+ init_with(s, h, node)
224
+ end
225
+
226
+ def init_with o, h, node
227
+ c = Psych::Coder.new(node.tag)
228
+ c.map = h
229
+
230
+ if o.respond_to?(:init_with)
231
+ o.init_with c
232
+ elsif o.respond_to?(:yaml_initialize)
233
+ if $VERBOSE
234
+ "Implementing #{o.class}#yaml_initialize is deprecated, please implement \"init_with(coder)\""
235
+ end
236
+ o.yaml_initialize c.tag, c.map
237
+ else
238
+ h.each { |k,v| o.instance_variable_set(:"@#{k}", v) }
239
+ end
240
+ o
241
+ end
242
+
243
+ # Convert +klassname+ to a Class
244
+ def resolve_class klassname
245
+ return nil unless klassname and not klassname.empty?
246
+
247
+ name = klassname
248
+ retried = false
249
+
250
+ begin
251
+ path2class(name)
252
+ rescue ArgumentError, NameError => ex
253
+ unless retried
254
+ name = "Struct::#{name}"
255
+ retried = ex
256
+ retry
257
+ end
258
+ raise retried
259
+ end
260
+ end
261
+ end
262
+ end
263
+ end