psych 1.3.4 → 2.0.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 (88) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +138 -0
  3. data/Manifest.txt +27 -8
  4. data/README.rdoc +23 -2
  5. data/Rakefile +1 -1
  6. data/ext/psych/depend +3 -0
  7. data/ext/psych/extconf.rb +27 -11
  8. data/ext/psych/psych.h +4 -4
  9. data/ext/psych/{emitter.c → psych_emitter.c} +0 -0
  10. data/ext/psych/{emitter.h → psych_emitter.h} +0 -0
  11. data/ext/psych/{parser.c → psych_parser.c} +1 -1
  12. data/ext/psych/{parser.h → psych_parser.h} +0 -0
  13. data/ext/psych/{to_ruby.c → psych_to_ruby.c} +3 -1
  14. data/ext/psych/{to_ruby.h → psych_to_ruby.h} +0 -0
  15. data/ext/psych/{yaml_tree.c → psych_yaml_tree.c} +0 -0
  16. data/ext/psych/{yaml_tree.h → psych_yaml_tree.h} +0 -0
  17. data/ext/psych/yaml/LICENSE +19 -0
  18. data/ext/psych/yaml/api.c +1392 -0
  19. data/ext/psych/yaml/config.h +11 -0
  20. data/ext/psych/yaml/dumper.c +394 -0
  21. data/ext/psych/yaml/emitter.c +2329 -0
  22. data/ext/psych/yaml/loader.c +432 -0
  23. data/ext/psych/yaml/parser.c +1374 -0
  24. data/ext/psych/yaml/reader.c +465 -0
  25. data/ext/psych/yaml/scanner.c +3570 -0
  26. data/ext/psych/yaml/writer.c +141 -0
  27. data/ext/psych/yaml/yaml.h +1971 -0
  28. data/ext/psych/yaml/yaml_private.h +643 -0
  29. data/lib/psych.rb +217 -51
  30. data/lib/psych/class_loader.rb +101 -0
  31. data/lib/psych/core_ext.rb +1 -8
  32. data/lib/psych/deprecated.rb +3 -1
  33. data/lib/psych/exception.rb +13 -0
  34. data/lib/psych/handler.rb +13 -0
  35. data/lib/psych/handlers/recorder.rb +39 -0
  36. data/lib/psych/json/stream.rb +1 -0
  37. data/lib/psych/nodes/node.rb +3 -1
  38. data/lib/psych/scalar_scanner.rb +46 -25
  39. data/lib/psych/stream.rb +1 -0
  40. data/lib/psych/streaming.rb +10 -5
  41. data/lib/psych/syntax_error.rb +3 -1
  42. data/lib/psych/visitors/json_tree.rb +5 -2
  43. data/lib/psych/visitors/to_ruby.rb +123 -75
  44. data/lib/psych/visitors/yaml_tree.rb +59 -17
  45. data/lib/psych/y.rb +9 -0
  46. data/test/psych/handlers/test_recorder.rb +25 -0
  47. data/test/psych/helper.rb +30 -1
  48. data/test/psych/test_alias_and_anchor.rb +1 -1
  49. data/test/psych/test_array.rb +1 -1
  50. data/test/psych/test_boolean.rb +1 -1
  51. data/test/psych/test_class.rb +1 -1
  52. data/test/psych/test_coder.rb +3 -3
  53. data/test/psych/test_date_time.rb +1 -1
  54. data/test/psych/test_deprecated.rb +6 -2
  55. data/test/psych/test_document.rb +1 -1
  56. data/test/psych/test_emitter.rb +1 -1
  57. data/test/psych/test_encoding.rb +51 -65
  58. data/test/psych/test_engine_manager.rb +1 -11
  59. data/test/psych/test_exception.rb +40 -19
  60. data/test/psych/test_hash.rb +1 -1
  61. data/test/psych/test_json_tree.rb +1 -1
  62. data/test/psych/test_merge_keys.rb +52 -1
  63. data/test/psych/test_nil.rb +1 -1
  64. data/test/psych/test_null.rb +1 -1
  65. data/test/psych/test_numeric.rb +21 -1
  66. data/test/psych/test_object.rb +1 -1
  67. data/test/psych/test_object_references.rb +3 -3
  68. data/test/psych/test_omap.rb +1 -1
  69. data/test/psych/test_parser.rb +1 -1
  70. data/test/psych/test_psych.rb +15 -15
  71. data/test/psych/test_safe_load.rb +97 -0
  72. data/test/psych/test_scalar.rb +1 -1
  73. data/test/psych/test_scalar_scanner.rb +17 -2
  74. data/test/psych/test_serialize_subclasses.rb +1 -1
  75. data/test/psych/test_set.rb +1 -1
  76. data/test/psych/test_stream.rb +1 -1
  77. data/test/psych/test_string.rb +51 -3
  78. data/test/psych/test_struct.rb +1 -1
  79. data/test/psych/test_symbol.rb +1 -1
  80. data/test/psych/test_tainted.rb +8 -8
  81. data/test/psych/test_to_yaml_properties.rb +1 -1
  82. data/test/psych/test_tree_builder.rb +1 -1
  83. data/test/psych/test_yaml.rb +22 -2
  84. data/test/psych/test_yamldbm.rb +1 -1
  85. data/test/psych/test_yamlstore.rb +1 -1
  86. data/test/psych/visitors/test_to_ruby.rb +5 -4
  87. data/test/psych/visitors/test_yaml_tree.rb +19 -1
  88. metadata +45 -34
@@ -18,10 +18,12 @@ require 'psych/handlers/document_stream'
18
18
  ###
19
19
  # = Overview
20
20
  #
21
- # Psych is a YAML parser and emitter. Psych leverages
22
- # libyaml[http://libyaml.org] for it's YAML parsing and emitting capabilities.
23
- # In addition to wrapping libyaml, Psych also knows how to serialize and
24
- # de-serialize most Ruby objects to and from the YAML format.
21
+ # Psych is a YAML parser and emitter.
22
+ # Psych leverages libyaml [Home page: http://pyyaml.org/wiki/LibYAML]
23
+ # or [Git repo: https://github.com/zerotao/libyaml] for its YAML parsing
24
+ # and emitting capabilities. In addition to wrapping libyaml, Psych also
25
+ # knows how to serialize and de-serialize most Ruby objects to and from
26
+ # the YAML format.
25
27
  #
26
28
  # = I NEED TO PARSE OR EMIT YAML RIGHT NOW!
27
29
  #
@@ -41,14 +43,72 @@ require 'psych/handlers/document_stream'
41
43
  # level, is an event based parser. Mid level is access to the raw YAML AST,
42
44
  # and at the highest level is the ability to unmarshal YAML to ruby objects.
43
45
  #
44
- # === Low level parsing
46
+ # == YAML Emitting
45
47
  #
46
- # The lowest level parser should be used when the YAML input is already known,
47
- # and the developer does not want to pay the price of building an AST or
48
- # automatic detection and conversion to ruby objects. See Psych::Parser for
49
- # more information on using the event based parser.
48
+ # Psych provides a range of interfaces ranging from low to high level for
49
+ # producing YAML documents. Very similar to the YAML parsing interfaces, Psych
50
+ # provides at the lowest level, an event based system, mid-level is building
51
+ # a YAML AST, and the highest level is converting a Ruby object straight to
52
+ # a YAML document.
53
+ #
54
+ # == High-level API
55
+ #
56
+ # === Parsing
57
+ #
58
+ # The high level YAML parser provided by Psych simply takes YAML as input and
59
+ # returns a Ruby data structure. For information on using the high level parser
60
+ # see Psych.load
61
+ #
62
+ # ==== Reading from a string
63
+ #
64
+ # Psych.load("--- a") # => 'a'
65
+ # Psych.load("---\n - a\n - b") # => ['a', 'b']
66
+ #
67
+ # ==== Reading from a file
68
+ #
69
+ # Psych.load_file("database.yml")
50
70
  #
51
- # === Mid level parsing
71
+ # ==== Exception handling
72
+ #
73
+ # begin
74
+ # # The second argument chnages only the exception contents
75
+ # Psych.parse("--- `", "file.txt")
76
+ # rescue Psych::SyntaxError => ex
77
+ # ex.file # => 'file.txt'
78
+ # ex.message # => "(file.txt): found character that cannot start any token"
79
+ # end
80
+ #
81
+ # === Emitting
82
+ #
83
+ # The high level emitter has the easiest interface. Psych simply takes a Ruby
84
+ # data structure and converts it to a YAML document. See Psych.dump for more
85
+ # information on dumping a Ruby data structure.
86
+ #
87
+ # ==== Writing to a string
88
+ #
89
+ # # Dump an array, get back a YAML string
90
+ # Psych.dump(['a', 'b']) # => "---\n- a\n- b\n"
91
+ #
92
+ # # Dump an array to an IO object
93
+ # Psych.dump(['a', 'b'], StringIO.new) # => #<StringIO:0x000001009d0890>
94
+ #
95
+ # # Dump an array with indentation set
96
+ # Psych.dump(['a', ['b']], :indentation => 3) # => "---\n- a\n- - b\n"
97
+ #
98
+ # # Dump an array to an IO with indentation set
99
+ # Psych.dump(['a', ['b']], StringIO.new, :indentation => 3)
100
+ #
101
+ # ==== Writing to a file
102
+ #
103
+ # Currently there is no direct API for dumping Ruby structure to file:
104
+ #
105
+ # File.open('database.yml', 'w') do |file|
106
+ # file.write(Psych.dump(['a', 'b']))
107
+ # end
108
+ #
109
+ # == Mid-level API
110
+ #
111
+ # === Parsing
52
112
  #
53
113
  # Psych provides access to an AST produced from parsing a YAML document. This
54
114
  # tree is built using the Psych::Parser and Psych::TreeBuilder. The AST can
@@ -56,28 +116,33 @@ require 'psych/handlers/document_stream'
56
116
  # Psych::Nodes, and Psych::Nodes::Node for more information on dealing with
57
117
  # YAML syntax trees.
58
118
  #
59
- # === High level parsing
119
+ # ==== Reading from a string
60
120
  #
61
- # The high level YAML parser provided by Psych simply takes YAML as input and
62
- # returns a Ruby data structure. For information on using the high level parser
63
- # see Psych.load
121
+ # # Returns Psych::Nodes::Stream
122
+ # Psych.parse_stream("---\n - a\n - b")
64
123
  #
65
- # == YAML Emitting
124
+ # # Returns Psych::Nodes::Document
125
+ # Psych.parse("---\n - a\n - b")
66
126
  #
67
- # Psych provides a range of interfaces ranging from low to high level for
68
- # producing YAML documents. Very similar to the YAML parsing interfaces, Psych
69
- # provides at the lowest level, an event based system, mid-level is building
70
- # a YAML AST, and the highest level is converting a Ruby object straight to
71
- # a YAML document.
127
+ # ==== Reading from a file
72
128
  #
73
- # === Low level emitting
129
+ # # Returns Psych::Nodes::Stream
130
+ # Psych.parse_stream(File.read('database.yml'))
74
131
  #
75
- # The lowest level emitter is an event based system. Events are sent to a
76
- # Psych::Emitter object. That object knows how to convert the events to a YAML
77
- # document. This interface should be used when document format is known in
78
- # advance or speed is a concern. See Psych::Emitter for more information.
132
+ # # Returns Psych::Nodes::Document
133
+ # Psych.parse_file('database.yml')
134
+ #
135
+ # ==== Exception handling
136
+ #
137
+ # begin
138
+ # # The second argument chnages only the exception contents
139
+ # Psych.parse("--- `", "file.txt")
140
+ # rescue Psych::SyntaxError => ex
141
+ # ex.file # => 'file.txt'
142
+ # ex.message # => "(file.txt): found character that cannot start any token"
143
+ # end
79
144
  #
80
- # === Mid level emitting
145
+ # === Emitting
81
146
  #
82
147
  # At the mid level is building an AST. This AST is exactly the same as the AST
83
148
  # used when parsing a YAML document. Users can build an AST by hand and the
@@ -85,25 +150,77 @@ require 'psych/handlers/document_stream'
85
150
  # Psych::Nodes::Node, and Psych::TreeBuilder for more information on building
86
151
  # a YAML AST.
87
152
  #
88
- # === High level emitting
153
+ # ==== Writing to a string
89
154
  #
90
- # The high level emitter has the easiest interface. Psych simply takes a Ruby
91
- # data structure and converts it to a YAML document. See Psych.dump for more
92
- # information on dumping a Ruby data structure.
155
+ # # We need Psych::Nodes::Stream (not Psych::Nodes::Document)
156
+ # stream = Psych.parse_stream("---\n - a\n - b")
157
+ #
158
+ # stream.to_yaml # => "---\n- a\n- b\n"
159
+ #
160
+ # ==== Writing to a file
161
+ #
162
+ # # We need Psych::Nodes::Stream (not Psych::Nodes::Document)
163
+ # stream = Psych.parse_stream(File.read('database.yml'))
164
+ #
165
+ # File.open('database.yml', 'w') do |file|
166
+ # file.write(stream.to_yaml)
167
+ # end
168
+ #
169
+ # == Low-level API
170
+ #
171
+ # === Parsing
172
+ #
173
+ # The lowest level parser should be used when the YAML input is already known,
174
+ # and the developer does not want to pay the price of building an AST or
175
+ # automatic detection and conversion to ruby objects. See Psych::Parser for
176
+ # more information on using the event based parser.
177
+ #
178
+ # ==== Reading to Psych::Nodes::Stream structure
179
+ #
180
+ # parser = Psych::Parser.new(TreeBuilder.new) # => #<Psych::Parser>
181
+ # parser = Psych.parser # it's an alias for the above
182
+ #
183
+ # parser.parse("---\n - a\n - b") # => #<Psych::Parser>
184
+ # parser.handler # => #<Psych::TreeBuilder>
185
+ # parser.handler.root # => #<Psych::Nodes::Stream>
186
+ #
187
+ # ==== Receiving an events stream
188
+ #
189
+ # parser = Psych::Parser.new(Psych::Handlers::Recorder.new)
190
+ #
191
+ # parser.parse("---\n - a\n - b")
192
+ # parser.events # => [list of [event, args] lists]
193
+ # # event is one of: Psych::Handler::EVENTS
194
+ # # args are the arguments passed to the event
195
+ #
196
+ # === Emitting
197
+ #
198
+ # The lowest level emitter is an event based system. Events are sent to a
199
+ # Psych::Emitter object. That object knows how to convert the events to a YAML
200
+ # document. This interface should be used when document format is known in
201
+ # advance or speed is a concern. See Psych::Emitter for more information.
202
+ #
203
+ # ==== Writing to a ruby structure
204
+ #
205
+ # Psych.parser.parse("--- a") # => #<Psych::Parser>
206
+ #
207
+ # parser.handler.first # => #<Psych::Nodes::Stream>
208
+ # parser.handler.first.to_ruby # => ["a"]
209
+ #
210
+ # parser.handler.root.first # => #<Psych::Nodes::Document>
211
+ # parser.handler.root.first.to_ruby # => "a"
212
+ #
213
+ # # You can instantiate an Emitter manually
214
+ # Psych::Visitors::ToRuby.new.accept(parser.handler.root.first)
215
+ # # => "a"
93
216
 
94
217
  module Psych
95
218
  # The version is Psych you're using
96
- VERSION = '1.3.4'
219
+ VERSION = '2.0.0'
97
220
 
98
221
  # The version of libyaml Psych is using
99
222
  LIBYAML_VERSION = Psych.libyaml_version.join '.'
100
223
 
101
- class Exception < RuntimeError
102
- end
103
-
104
- class BadAlias < Exception
105
- end
106
-
107
224
  ###
108
225
  # Load +yaml+ in to a Ruby data structure. If multiple documents are
109
226
  # provided, the object contained in the first document will be returned.
@@ -121,7 +238,7 @@ module Psych
121
238
  # Psych.load("--- `", "file.txt")
122
239
  # rescue Psych::SyntaxError => ex
123
240
  # ex.file # => 'file.txt'
124
- # ex.message # => "(foo.txt): found character that cannot start any token"
241
+ # ex.message # => "(file.txt): found character that cannot start any token"
125
242
  # end
126
243
  def self.load yaml, filename = nil
127
244
  result = parse(yaml, filename)
@@ -129,7 +246,56 @@ module Psych
129
246
  end
130
247
 
131
248
  ###
132
- # Parse a YAML string in +yaml+. Returns the first object of a YAML AST.
249
+ # Safely load the yaml string in +yaml+. By default, only the following
250
+ # classes are allowed to be deserialized:
251
+ #
252
+ # * TrueClass
253
+ # * FalseClass
254
+ # * NilClass
255
+ # * Numeric
256
+ # * String
257
+ # * Array
258
+ # * Hash
259
+ #
260
+ # Recursive data structures are not allowed by default. Arbitrary classes
261
+ # can be allowed by adding those classes to the +whitelist+. They are
262
+ # additive. For example, to allow Date deserialization:
263
+ #
264
+ # Psych.safe_load(yaml, [Date])
265
+ #
266
+ # Now the Date class can be loaded in addition to the classes listed above.
267
+ #
268
+ # Aliases can be explicitly allowed by changing the +aliases+ parameter.
269
+ # For example:
270
+ #
271
+ # x = []
272
+ # x << x
273
+ # yaml = Psych.dump x
274
+ # Psych.safe_load yaml # => raises an exception
275
+ # Psych.safe_load yaml, [], [], true # => loads the aliases
276
+ #
277
+ # A Psych::DisallowedClass exception will be raised if the yaml contains a
278
+ # class that isn't in the whitelist.
279
+ #
280
+ # A Psych::BadAlias exception will be raised if the yaml contains aliases
281
+ # but the +aliases+ parameter is set to false.
282
+ def self.safe_load yaml, whitelist_classes = [], whitelist_symbols = [], aliases = false, filename = nil
283
+ result = parse(yaml, filename)
284
+ return unless result
285
+
286
+ class_loader = ClassLoader::Restricted.new(whitelist_classes.map(&:to_s),
287
+ whitelist_symbols.map(&:to_s))
288
+ scanner = ScalarScanner.new class_loader
289
+ if aliases
290
+ visitor = Visitors::ToRuby.new scanner, class_loader
291
+ else
292
+ visitor = Visitors::NoAliasRuby.new scanner, class_loader
293
+ end
294
+ visitor.accept result
295
+ end
296
+
297
+ ###
298
+ # Parse a YAML string in +yaml+. Returns the Psych::Nodes::Document.
133
299
  # +filename+ is used in the exception message if a Psych::SyntaxError is
134
300
  # raised.
135
301
  #
@@ -137,13 +303,13 @@ module Psych
137
303
  #
138
304
  # Example:
139
305
  #
140
- # Psych.parse("---\n - a\n - b") # => #<Psych::Nodes::Sequence:0x00>
306
+ # Psych.parse("---\n - a\n - b") # => #<Psych::Nodes::Document:0x00>
141
307
  #
142
308
  # begin
143
309
  # Psych.parse("--- `", "file.txt")
144
310
  # rescue Psych::SyntaxError => ex
145
311
  # ex.file # => 'file.txt'
146
- # ex.message # => "(foo.txt): found character that cannot start any token"
312
+ # ex.message # => "(file.txt): found character that cannot start any token"
147
313
  # end
148
314
  #
149
315
  # See Psych::Nodes for more information about YAML AST.
@@ -155,7 +321,7 @@ module Psych
155
321
  end
156
322
 
157
323
  ###
158
- # Parse a file at +filename+. Returns the YAML AST.
324
+ # Parse a file at +filename+. Returns the Psych::Nodes::Document.
159
325
  #
160
326
  # Raises a Psych::SyntaxError when a YAML syntax error is detected.
161
327
  def self.parse_file filename
@@ -171,7 +337,7 @@ module Psych
171
337
  end
172
338
 
173
339
  ###
174
- # Parse a YAML string in +yaml+. Returns the full AST for the YAML document.
340
+ # Parse a YAML string in +yaml+. Returns the Psych::Nodes::Stream.
175
341
  # This method can handle multiple YAML documents contained in +yaml+.
176
342
  # +filename+ is used in the exception message if a Psych::SyntaxError is
177
343
  # raised.
@@ -193,7 +359,7 @@ module Psych
193
359
  # Psych.parse_stream("--- `", "file.txt")
194
360
  # rescue Psych::SyntaxError => ex
195
361
  # ex.file # => 'file.txt'
196
- # ex.message # => "(foo.txt): found character that cannot start any token"
362
+ # ex.message # => "(file.txt): found character that cannot start any token"
197
363
  # end
198
364
  #
199
365
  # See Psych::Nodes for more information about YAML AST.
@@ -238,7 +404,7 @@ module Psych
238
404
  io = nil
239
405
  end
240
406
 
241
- visitor = Psych::Visitors::YAMLTree.new options
407
+ visitor = Psych::Visitors::YAMLTree.create options
242
408
  visitor << o
243
409
  visitor.tree.yaml io, options
244
410
  end
@@ -250,7 +416,7 @@ module Psych
250
416
  #
251
417
  # Psych.dump_stream("foo\n ", {}) # => "--- ! \"foo\\n \"\n--- {}\n"
252
418
  def self.dump_stream *objects
253
- visitor = Psych::Visitors::YAMLTree.new {}
419
+ visitor = Psych::Visitors::YAMLTree.create({})
254
420
  objects.each do |o|
255
421
  visitor << o
256
422
  end
@@ -258,10 +424,10 @@ module Psych
258
424
  end
259
425
 
260
426
  ###
261
- # Dump Ruby object +o+ to a JSON string.
262
- def self.to_json o
263
- visitor = Psych::Visitors::JSONTree.new
264
- visitor << o
427
+ # Dump Ruby +object+ to a JSON string.
428
+ def self.to_json object
429
+ visitor = Psych::Visitors::JSONTree.create
430
+ visitor << object
265
431
  visitor.tree.yaml
266
432
  end
267
433
 
@@ -318,7 +484,7 @@ module Psych
318
484
  @load_tags = {}
319
485
  @dump_tags = {}
320
486
  def self.add_tag tag, klass
321
- @load_tags[tag] = klass
487
+ @load_tags[tag] = klass.name
322
488
  @dump_tags[klass] = tag
323
489
  end
324
490
 
@@ -0,0 +1,101 @@
1
+ require 'psych/omap'
2
+ require 'psych/set'
3
+
4
+ module Psych
5
+ class ClassLoader # :nodoc:
6
+ BIG_DECIMAL = 'BigDecimal'
7
+ COMPLEX = 'Complex'
8
+ DATE = 'Date'
9
+ DATE_TIME = 'DateTime'
10
+ EXCEPTION = 'Exception'
11
+ OBJECT = 'Object'
12
+ PSYCH_OMAP = 'Psych::Omap'
13
+ PSYCH_SET = 'Psych::Set'
14
+ RANGE = 'Range'
15
+ RATIONAL = 'Rational'
16
+ REGEXP = 'Regexp'
17
+ STRUCT = 'Struct'
18
+ SYMBOL = 'Symbol'
19
+
20
+ def initialize
21
+ @cache = CACHE.dup
22
+ end
23
+
24
+ def load klassname
25
+ return nil if !klassname || klassname.empty?
26
+
27
+ find klassname
28
+ end
29
+
30
+ def symbolize sym
31
+ symbol
32
+ sym.to_sym
33
+ end
34
+
35
+ constants.each do |const|
36
+ konst = const_get const
37
+ define_method(const.to_s.downcase) do
38
+ load konst
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def find klassname
45
+ @cache[klassname] ||= resolve(klassname)
46
+ end
47
+
48
+ def resolve klassname
49
+ name = klassname
50
+ retried = false
51
+
52
+ begin
53
+ path2class(name)
54
+ rescue ArgumentError, NameError => ex
55
+ unless retried
56
+ name = "Struct::#{name}"
57
+ retried = ex
58
+ retry
59
+ end
60
+ raise retried
61
+ end
62
+ end
63
+
64
+ CACHE = Hash[constants.map { |const|
65
+ val = const_get const
66
+ begin
67
+ [val, ::Object.const_get(val)]
68
+ rescue
69
+ nil
70
+ end
71
+ }.compact]
72
+
73
+ class Restricted < ClassLoader
74
+ def initialize classes, symbols
75
+ @classes = classes
76
+ @symbols = symbols
77
+ super()
78
+ end
79
+
80
+ def symbolize sym
81
+ return super if @symbols.empty?
82
+
83
+ if @symbols.include? sym
84
+ super
85
+ else
86
+ raise DisallowedClass, 'Symbol'
87
+ end
88
+ end
89
+
90
+ private
91
+
92
+ def find klassname
93
+ if @classes.include? klassname
94
+ super
95
+ else
96
+ raise DisallowedClass, klassname
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end