psych 4.0.0-java → 4.0.1-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5149acda4b0d41732db12bc694d4e72b0f5a511b628389ec0add96cef04d3b23
4
- data.tar.gz: 4706cd6d83b23b106bdf5b5ef5953cdb416992bfa5ae774bb5f57e2f5394c1c8
3
+ metadata.gz: 23dccf627759f7113d2058885273d84aa92edc9a162a7a620510d27ba4c80fff
4
+ data.tar.gz: 50bd2d6eb02c3ea703bbb1064493c62ba7f579fdefa8b98cdb7a417438c5fd09
5
5
  SHA512:
6
- metadata.gz: 109458fa5a2da076004e322aa34f8b777be41d9dd3f51e27e4f6282e14896d84b8c9964f5a748803ba4873fbcf3857e72aa9993952997ea962c9104f9a4af969
7
- data.tar.gz: cf2cb03494fade26c250914388b11ade2d529e81ddfa4bee119c9a5a149681239c6d9c81261f5ec247540a40a137a14e96fcaac4190b9db94ca0a92167410fe8
6
+ metadata.gz: a4bf4b8036cc63a7dffa4a0e0858a3eb327f1812d422c60c22852458f4a163dad53688c8bbef8300059c25ba61be455a470d5ca5918308648c450bde29231a5a
7
+ data.tar.gz: eba85f35d6f01299dd169c9c5e97f63ab795f041fa8e333a1dc26bfea125dbe14cb094b68535c687663210fc4dca88516babcd89f9235af68b493c42408f8a7d
@@ -29,13 +29,16 @@ package org.jruby.ext.psych;
29
29
 
30
30
  import java.io.IOException;
31
31
  import java.io.OutputStreamWriter;
32
+ import java.io.Writer;
32
33
  import java.nio.charset.Charset;
33
34
  import java.util.HashMap;
34
35
  import java.util.Map;
35
36
 
36
37
  import org.jcodings.Encoding;
38
+ import org.jcodings.specific.UTF8Encoding;
37
39
  import org.jruby.Ruby;
38
40
  import org.jruby.RubyArray;
41
+ import org.jruby.RubyBoolean;
39
42
  import org.jruby.RubyClass;
40
43
  import org.jruby.RubyFixnum;
41
44
  import org.jruby.RubyModule;
@@ -46,6 +49,8 @@ import org.jruby.runtime.ObjectAllocator;
46
49
  import org.jruby.runtime.ThreadContext;
47
50
  import org.jruby.runtime.builtin.IRubyObject;
48
51
  import org.jruby.util.IOOutputStream;
52
+ import org.jruby.util.TypeConverter;
53
+ import org.jruby.util.io.EncodingUtils;
49
54
  import org.yaml.snakeyaml.DumperOptions;
50
55
  import org.yaml.snakeyaml.emitter.Emitter;
51
56
  import org.yaml.snakeyaml.emitter.EmitterException;
@@ -110,9 +115,7 @@ public class PsychEmitter extends RubyObject {
110
115
 
111
116
  @JRubyMethod
112
117
  public IRubyObject start_stream(ThreadContext context, IRubyObject encoding) {
113
- if (!(encoding instanceof RubyFixnum)) {
114
- throw context.runtime.newTypeError(encoding, context.runtime.getFixnum());
115
- }
118
+ TypeConverter.checkType(context, encoding, context.runtime.getFixnum());
116
119
 
117
120
  initEmitter(context, encoding);
118
121
 
@@ -136,6 +139,8 @@ public class PsychEmitter extends RubyObject {
136
139
  boolean implicitBool = implicit.isTrue();
137
140
  Map<String, String> tagsMap = null;
138
141
 
142
+ TypeConverter.checkType(context, _version, context.runtime.getArray());
143
+
139
144
  RubyArray versionAry = _version.convertToArray();
140
145
  if (versionAry.size() == 2) {
141
146
  int versionInt0 = (int)versionAry.eltInternal(0).convertToInteger().getLongValue();
@@ -153,19 +158,23 @@ public class PsychEmitter extends RubyObject {
153
158
  }
154
159
  }
155
160
 
156
- RubyArray tagsAry = tags.convertToArray();
157
- if (tagsAry.size() > 0) {
158
- tagsMap = new HashMap<String, String>(tagsAry.size());
159
- for (int i = 0; i < tagsAry.size(); i++) {
160
- RubyArray tagsTuple = tagsAry.eltInternal(i).convertToArray();
161
- if (tagsTuple.size() != 2) {
162
- throw context.runtime.newRuntimeError("tags tuple must be of length 2");
161
+ if (!tags.isNil()) {
162
+ TypeConverter.checkType(context, tags, context.runtime.getArray());
163
+
164
+ RubyArray tagsAry = tags.convertToArray();
165
+ if (tagsAry.size() > 0) {
166
+ tagsMap = new HashMap<>(tagsAry.size());
167
+ for (int i = 0; i < tagsAry.size(); i++) {
168
+ RubyArray tagsTuple = tagsAry.eltInternal(i).convertToArray();
169
+ if (tagsTuple.size() != 2) {
170
+ throw context.runtime.newRuntimeError("tags tuple must be of length 2");
171
+ }
172
+ IRubyObject key = tagsTuple.eltInternal(0);
173
+ IRubyObject value = tagsTuple.eltInternal(1);
174
+ tagsMap.put(
175
+ key.asJavaString(),
176
+ value.asJavaString());
163
177
  }
164
- IRubyObject key = tagsTuple.eltInternal(0);
165
- IRubyObject value = tagsTuple.eltInternal(1);
166
- tagsMap.put(
167
- key.asJavaString(),
168
- value.asJavaString());
169
178
  }
170
179
  }
171
180
 
@@ -189,21 +198,29 @@ public class PsychEmitter extends RubyObject {
189
198
  IRubyObject plain = args[3];
190
199
  IRubyObject quoted = args[4];
191
200
  IRubyObject style = args[5];
192
-
193
- if (!(value instanceof RubyString)) {
194
- throw context.runtime.newTypeError(value, context.runtime.getString());
195
- }
201
+
202
+ RubyClass stringClass = context.runtime.getString();
203
+
204
+ TypeConverter.checkType(context, value, stringClass);
205
+
206
+ RubyString valueStr = (RubyString) value;
207
+
208
+ valueStr = EncodingUtils.strConvEnc(context, valueStr, valueStr.getEncoding(), UTF8Encoding.INSTANCE);
209
+
210
+ RubyString anchorStr = exportToUTF8(context, anchor, stringClass);
211
+ RubyString tagStr = exportToUTF8(context, tag, stringClass);
196
212
 
197
213
  ScalarEvent event = new ScalarEvent(
198
- anchor.isNil() ? null : anchor.asJavaString(),
199
- tag.isNil() ? null : tag.asJavaString(),
200
- new ImplicitTuple(plain.isTrue(),
201
- quoted.isTrue()),
202
- value.asJavaString(),
214
+ anchorStr == null ? null : anchorStr.asJavaString(),
215
+ tagStr == null ? null : tagStr.asJavaString(),
216
+ new ImplicitTuple(plain.isTrue(), quoted.isTrue()),
217
+ valueStr.asJavaString(),
203
218
  NULL_MARK,
204
219
  NULL_MARK,
205
220
  SCALAR_STYLES[style.convertToInteger().getIntValue()]);
221
+
206
222
  emit(context, event);
223
+
207
224
  return this;
208
225
  }
209
226
 
@@ -214,11 +231,14 @@ public class PsychEmitter extends RubyObject {
214
231
  IRubyObject implicit = args[2];
215
232
  IRubyObject style = args[3];
216
233
 
217
- final int SEQUENCE_BLOCK = 1; // see psych/nodes/sequence.rb
234
+ RubyClass stringClass = context.runtime.getString();
235
+
236
+ RubyString anchorStr = exportToUTF8(context, anchor, stringClass);
237
+ RubyString tagStr = exportToUTF8(context, tag, stringClass);
218
238
 
219
239
  SequenceStartEvent event = new SequenceStartEvent(
220
- anchor.isNil() ? null : anchor.asJavaString(),
221
- tag.isNil() ? null : tag.asJavaString(),
240
+ anchorStr == null ? null : anchorStr.asJavaString(),
241
+ tagStr == null ? null : tagStr.asJavaString(),
222
242
  implicit.isTrue(),
223
243
  NULL_MARK,
224
244
  NULL_MARK,
@@ -241,16 +261,21 @@ public class PsychEmitter extends RubyObject {
241
261
  IRubyObject implicit = args[2];
242
262
  IRubyObject style = args[3];
243
263
 
244
- final int MAPPING_BLOCK = 1; // see psych/nodes/mapping.rb
264
+ RubyClass stringClass = context.runtime.getString();
265
+
266
+ RubyString anchorStr = exportToUTF8(context, anchor, stringClass);
267
+ RubyString tagStr = exportToUTF8(context, tag, stringClass);
245
268
 
246
269
  MappingStartEvent event = new MappingStartEvent(
247
- anchor.isNil() ? null : anchor.asJavaString(),
248
- tag.isNil() ? null : tag.asJavaString(),
270
+ anchorStr == null ? null : anchorStr.asJavaString(),
271
+ tagStr == null ? null : tagStr.asJavaString(),
249
272
  implicit.isTrue(),
250
273
  NULL_MARK,
251
274
  NULL_MARK,
252
275
  FLOW_STYLES[style.convertToInteger().getIntValue()]);
276
+
253
277
  emit(context, event);
278
+
254
279
  return this;
255
280
  }
256
281
 
@@ -263,7 +288,11 @@ public class PsychEmitter extends RubyObject {
263
288
 
264
289
  @JRubyMethod
265
290
  public IRubyObject alias(ThreadContext context, IRubyObject anchor) {
266
- AliasEvent event = new AliasEvent(anchor.asJavaString(), NULL_MARK, NULL_MARK);
291
+ RubyClass stringClass = context.runtime.getString();
292
+
293
+ RubyString anchorStr = exportToUTF8(context, anchor, stringClass);
294
+
295
+ AliasEvent event = new AliasEvent(anchorStr.asJavaString(), NULL_MARK, NULL_MARK);
267
296
  emit(context, event);
268
297
  return this;
269
298
  }
@@ -278,7 +307,7 @@ public class PsychEmitter extends RubyObject {
278
307
  @JRubyMethod
279
308
  public IRubyObject canonical(ThreadContext context) {
280
309
  // TODO: unclear if this affects a running emitter
281
- return context.runtime.newBoolean(options.isCanonical());
310
+ return RubyBoolean.newBoolean(context, options.isCanonical());
282
311
  }
283
312
 
284
313
  @JRubyMethod(name = "indentation=")
@@ -312,6 +341,9 @@ public class PsychEmitter extends RubyObject {
312
341
  if (emitter == null) throw context.runtime.newRuntimeError("uninitialized emitter");
313
342
 
314
343
  emitter.emit(event);
344
+
345
+ // flush writer after each emit
346
+ writer.flush();
315
347
  } catch (IOException ioe) {
316
348
  throw context.runtime.newIOErrorFromException(ioe);
317
349
  } catch (EmitterException ee) {
@@ -325,10 +357,22 @@ public class PsychEmitter extends RubyObject {
325
357
  Encoding encoding = PsychLibrary.YAMLEncoding.values()[(int)_encoding.convertToInteger().getLongValue()].encoding;
326
358
  Charset charset = context.runtime.getEncodingService().charsetForEncoding(encoding);
327
359
 
328
- emitter = new Emitter(new OutputStreamWriter(new IOOutputStream(io, encoding), charset), options);
360
+ writer = new OutputStreamWriter(new IOOutputStream(io, encoding), charset);
361
+ emitter = new Emitter(writer, options);
362
+ }
363
+
364
+ private RubyString exportToUTF8(ThreadContext context, IRubyObject tag, RubyClass stringClass) {
365
+ RubyString tagStr = null;
366
+ if (!tag.isNil()) {
367
+ TypeConverter.checkType(context, tag, stringClass);
368
+ tagStr = (RubyString) tag;
369
+ tagStr = EncodingUtils.strConvEnc(context, tagStr, tagStr.getEncoding(), UTF8Encoding.INSTANCE);
370
+ }
371
+ return tagStr;
329
372
  }
330
373
 
331
374
  Emitter emitter;
375
+ Writer writer;
332
376
  DumperOptions options = new DumperOptions();
333
377
  IRubyObject io;
334
378
 
@@ -30,6 +30,9 @@ package org.jruby.ext.psych;
30
30
  import java.io.ByteArrayInputStream;
31
31
  import java.io.InputStreamReader;
32
32
  import java.nio.charset.Charset;
33
+ import java.nio.charset.CharsetDecoder;
34
+ import java.nio.charset.CodingErrorAction;
35
+ import java.nio.charset.MalformedInputException;
33
36
  import java.util.Map;
34
37
 
35
38
  import org.jcodings.Encoding;
@@ -61,6 +64,7 @@ import org.jruby.util.log.LoggerFactory;
61
64
  import org.yaml.snakeyaml.DumperOptions;
62
65
  import org.yaml.snakeyaml.error.Mark;
63
66
  import org.yaml.snakeyaml.error.MarkedYAMLException;
67
+ import org.yaml.snakeyaml.error.YAMLException;
64
68
  import org.yaml.snakeyaml.events.AliasEvent;
65
69
  import org.yaml.snakeyaml.events.DocumentEndEvent;
66
70
  import org.yaml.snakeyaml.events.DocumentStartEvent;
@@ -75,6 +79,8 @@ import org.yaml.snakeyaml.parser.ParserImpl;
75
79
  import org.yaml.snakeyaml.reader.ReaderException;
76
80
  import org.yaml.snakeyaml.reader.StreamReader;
77
81
  import org.yaml.snakeyaml.scanner.ScannerException;
82
+
83
+ import static org.jruby.runtime.Helpers.arrayOf;
78
84
  import static org.jruby.runtime.Helpers.invoke;
79
85
  import org.jruby.util.ByteList;
80
86
 
@@ -89,8 +95,7 @@ public class PsychParser extends RubyObject {
89
95
  }
90
96
  }, psych);
91
97
 
92
- RubyKernel.require(runtime.getNil(),
93
- runtime.newString("psych/syntax_error"), Block.NULL_BLOCK);
98
+ runtime.getLoadService().require("psych/syntax_error");
94
99
  psychParser.defineConstant("ANY", runtime.newFixnum(YAML_ANY_ENCODING.ordinal()));
95
100
  psychParser.defineConstant("UTF8", runtime.newFixnum(YAML_UTF8_ENCODING.ordinal()));
96
101
  psychParser.defineConstant("UTF16LE", runtime.newFixnum(YAML_UTF16LE_ENCODING.ordinal()));
@@ -110,13 +115,15 @@ public class PsychParser extends RubyObject {
110
115
  return parse(context, yaml, runtime.getNil());
111
116
  }
112
117
 
113
- private IRubyObject stringOrNilFor(Ruby runtime, String value, boolean tainted) {
114
- if (value == null) return runtime.getNil(); // No need to taint nil
118
+ private IRubyObject stringOrNilFor(ThreadContext context, String value, boolean tainted) {
119
+ if (value == null) return context.nil;
115
120
 
116
- return stringFor(runtime, value, tainted);
121
+ return stringFor(context, value, tainted);
117
122
  }
118
123
 
119
- private RubyString stringFor(Ruby runtime, String value, boolean tainted) {
124
+ private RubyString stringFor(ThreadContext context, String value, boolean tainted) {
125
+ Ruby runtime = context.runtime;
126
+
120
127
  Encoding encoding = runtime.getDefaultInternalEncoding();
121
128
  if (encoding == null) {
122
129
  encoding = UTF8Encoding.INSTANCE;
@@ -136,8 +143,6 @@ public class PsychParser extends RubyObject {
136
143
  }
137
144
 
138
145
  private StreamReader readerFor(ThreadContext context, IRubyObject yaml) {
139
- Ruby runtime = context.runtime;
140
-
141
146
  if (yaml instanceof RubyString) {
142
147
  ByteList byteList = ((RubyString)yaml).getByteList();
143
148
  Encoding enc = byteList.getEncoding();
@@ -175,8 +180,14 @@ public class PsychParser extends RubyObject {
175
180
  // If we can't get it from the IO or it doesn't have a charset, fall back on UTF-8
176
181
  charset = UTF8Encoding.INSTANCE.getCharset();
177
182
  }
178
- return new StreamReader(new InputStreamReader(new IOInputStream(yaml), charset));
183
+ CharsetDecoder decoder = charset.newDecoder();
184
+ decoder.onMalformedInput(CodingErrorAction.REPORT);
185
+ decoder.onMalformedInput(CodingErrorAction.REPORT);
186
+
187
+ return new StreamReader(new InputStreamReader(new IOInputStream(yaml), decoder));
179
188
  } else {
189
+ Ruby runtime = context.runtime;
190
+
180
191
  throw runtime.newTypeError(yaml, runtime.getIO());
181
192
  }
182
193
  }
@@ -214,7 +225,7 @@ public class PsychParser extends RubyObject {
214
225
 
215
226
  invoke(context, handler, "end_document", notExplicit);
216
227
  } else if (event.is(ID.Alias)) {
217
- IRubyObject alias = stringOrNilFor(runtime, ((AliasEvent)event).getAnchor(), tainted);
228
+ IRubyObject alias = stringOrNilFor(context, ((AliasEvent)event).getAnchor(), tainted);
218
229
 
219
230
  invoke(context, handler, "alias", alias);
220
231
  } else if (event.is(ID.Scalar)) {
@@ -249,6 +260,16 @@ public class PsychParser extends RubyObject {
249
260
  parser = null;
250
261
  raiseParserException(context, yaml, re, path);
251
262
 
263
+ } catch (YAMLException ye) {
264
+ Throwable cause = ye.getCause();
265
+
266
+ if (cause instanceof MalformedInputException) {
267
+ // failure due to improperly encoded input
268
+ raiseParserException(context, yaml, (MalformedInputException) cause, path);
269
+ }
270
+
271
+ throw ye;
272
+
252
273
  } catch (Throwable t) {
253
274
  Helpers.throwException(t);
254
275
  return this;
@@ -268,8 +289,8 @@ public class PsychParser extends RubyObject {
268
289
  RubyArray tags = RubyArray.newArray(runtime);
269
290
  if (tagsMap != null && tagsMap.size() > 0) {
270
291
  for (Map.Entry<String, String> tag : tagsMap.entrySet()) {
271
- IRubyObject key = stringFor(runtime, tag.getKey(), tainted);
272
- IRubyObject value = stringFor(runtime, tag.getValue(), tainted);
292
+ IRubyObject key = stringFor(context, tag.getKey(), tainted);
293
+ IRubyObject value = stringFor(context, tag.getValue(), tainted);
273
294
 
274
295
  tags.append(RubyArray.newArray(runtime, key, value));
275
296
  }
@@ -281,8 +302,8 @@ public class PsychParser extends RubyObject {
281
302
 
282
303
  private void handleMappingStart(ThreadContext context, MappingStartEvent mse, boolean tainted, IRubyObject handler) {
283
304
  Ruby runtime = context.runtime;
284
- IRubyObject anchor = stringOrNilFor(runtime, mse.getAnchor(), tainted);
285
- IRubyObject tag = stringOrNilFor(runtime, mse.getTag(), tainted);
305
+ IRubyObject anchor = stringOrNilFor(context, mse.getAnchor(), tainted);
306
+ IRubyObject tag = stringOrNilFor(context, mse.getTag(), tainted);
286
307
  IRubyObject implicit = runtime.newBoolean(mse.getImplicit());
287
308
  IRubyObject style = runtime.newFixnum(translateFlowStyle(mse.getFlowStyle()));
288
309
 
@@ -292,12 +313,12 @@ public class PsychParser extends RubyObject {
292
313
  private void handleScalar(ThreadContext context, ScalarEvent se, boolean tainted, IRubyObject handler) {
293
314
  Ruby runtime = context.runtime;
294
315
 
295
- IRubyObject anchor = stringOrNilFor(runtime, se.getAnchor(), tainted);
296
- IRubyObject tag = stringOrNilFor(runtime, se.getTag(), tainted);
316
+ IRubyObject anchor = stringOrNilFor(context, se.getAnchor(), tainted);
317
+ IRubyObject tag = stringOrNilFor(context, se.getTag(), tainted);
297
318
  IRubyObject plain_implicit = runtime.newBoolean(se.getImplicit().canOmitTagInPlainScalar());
298
319
  IRubyObject quoted_implicit = runtime.newBoolean(se.getImplicit().canOmitTagInNonPlainScalar());
299
320
  IRubyObject style = runtime.newFixnum(translateStyle(se.getScalarStyle()));
300
- IRubyObject val = stringFor(runtime, se.getValue(), tainted);
321
+ IRubyObject val = stringFor(context, se.getValue(), tainted);
301
322
 
302
323
  invoke(context, handler, "scalar", val, anchor, tag, plain_implicit,
303
324
  quoted_implicit, style);
@@ -305,8 +326,8 @@ public class PsychParser extends RubyObject {
305
326
 
306
327
  private void handleSequenceStart(ThreadContext context, SequenceStartEvent sse, boolean tainted, IRubyObject handler) {
307
328
  Ruby runtime = context.runtime;
308
- IRubyObject anchor = stringOrNilFor(runtime, sse.getAnchor(), tainted);
309
- IRubyObject tag = stringOrNilFor(runtime, sse.getTag(), tainted);
329
+ IRubyObject anchor = stringOrNilFor(context, sse.getAnchor(), tainted);
330
+ IRubyObject tag = stringOrNilFor(context, sse.getTag(), tainted);
310
331
  IRubyObject implicit = runtime.newBoolean(sse.getImplicit());
311
332
  IRubyObject style = runtime.newFixnum(translateFlowStyle(sse.getFlowStyle()));
312
333
 
@@ -360,6 +381,31 @@ public class PsychParser extends RubyObject {
360
381
  RubyKernel.raise(context, runtime.getKernel(), new IRubyObject[] { exception }, Block.NULL_BLOCK);
361
382
  }
362
383
 
384
+ private static void raiseParserException(ThreadContext context, IRubyObject yaml, MalformedInputException mie, IRubyObject rbPath) {
385
+ Ruby runtime;
386
+ Mark mark;
387
+ RubyClass se;
388
+ IRubyObject exception;
389
+
390
+ runtime = context.runtime;
391
+ se = (RubyClass)runtime.getModule("Psych").getConstant("SyntaxError");
392
+
393
+ mie.getInputLength();
394
+
395
+ exception = se.newInstance(context,
396
+ arrayOf(
397
+ rbPath,
398
+ runtime.newFixnum(-1),
399
+ runtime.newFixnum(-1),
400
+ runtime.newFixnum(mie.getInputLength()),
401
+ runtime.getNil(),
402
+ runtime.getNil()
403
+ ),
404
+ Block.NULL_BLOCK);
405
+
406
+ RubyKernel.raise(context, runtime.getKernel(), new IRubyObject[] { exception }, Block.NULL_BLOCK);
407
+ }
408
+
363
409
  private static int translateStyle(DumperOptions.ScalarStyle style) {
364
410
  if (style == null) return 0; // any
365
411
 
data/lib/psych.rb CHANGED
@@ -282,7 +282,8 @@ module Psych
282
282
  # * TrueClass
283
283
  # * FalseClass
284
284
  # * NilClass
285
- # * Numeric
285
+ # * Integer
286
+ # * Float
286
287
  # * String
287
288
  # * Array
288
289
  # * Hash
@@ -512,6 +513,79 @@ module Psych
512
513
  visitor.tree.yaml io, options
513
514
  end
514
515
 
516
+ ###
517
+ # call-seq:
518
+ # Psych.safe_dump(o) -> string of yaml
519
+ # Psych.safe_dump(o, options) -> string of yaml
520
+ # Psych.safe_dump(o, io) -> io object passed in
521
+ # Psych.safe_dump(o, io, options) -> io object passed in
522
+ #
523
+ # Safely dump Ruby object +o+ to a YAML string. Optional +options+ may be passed in
524
+ # to control the output format. If an IO object is passed in, the YAML will
525
+ # be dumped to that IO object. By default, only the following
526
+ # classes are allowed to be serialized:
527
+ #
528
+ # * TrueClass
529
+ # * FalseClass
530
+ # * NilClass
531
+ # * Integer
532
+ # * Float
533
+ # * String
534
+ # * Array
535
+ # * Hash
536
+ #
537
+ # Arbitrary classes can be allowed by adding those classes to the +permitted_classes+
538
+ # keyword argument. They are additive. For example, to allow Date serialization:
539
+ #
540
+ # Psych.safe_dump(yaml, permitted_classes: [Date])
541
+ #
542
+ # Now the Date class can be dumped in addition to the classes listed above.
543
+ #
544
+ # A Psych::DisallowedClass exception will be raised if the object contains a
545
+ # class that isn't in the +permitted_classes+ list.
546
+ #
547
+ # Currently supported options are:
548
+ #
549
+ # [<tt>:indentation</tt>] Number of space characters used to indent.
550
+ # Acceptable value should be in <tt>0..9</tt> range,
551
+ # otherwise option is ignored.
552
+ #
553
+ # Default: <tt>2</tt>.
554
+ # [<tt>:line_width</tt>] Max character to wrap line at.
555
+ #
556
+ # Default: <tt>0</tt> (meaning "wrap at 81").
557
+ # [<tt>:canonical</tt>] Write "canonical" YAML form (very verbose, yet
558
+ # strictly formal).
559
+ #
560
+ # Default: <tt>false</tt>.
561
+ # [<tt>:header</tt>] Write <tt>%YAML [version]</tt> at the beginning of document.
562
+ #
563
+ # Default: <tt>false</tt>.
564
+ #
565
+ # Example:
566
+ #
567
+ # # Dump an array, get back a YAML string
568
+ # Psych.safe_dump(['a', 'b']) # => "---\n- a\n- b\n"
569
+ #
570
+ # # Dump an array to an IO object
571
+ # Psych.safe_dump(['a', 'b'], StringIO.new) # => #<StringIO:0x000001009d0890>
572
+ #
573
+ # # Dump an array with indentation set
574
+ # Psych.safe_dump(['a', ['b']], indentation: 3) # => "---\n- a\n- - b\n"
575
+ #
576
+ # # Dump an array to an IO with indentation set
577
+ # Psych.safe_dump(['a', ['b']], StringIO.new, indentation: 3)
578
+ def self.safe_dump o, io = nil, options = {}
579
+ if Hash === io
580
+ options = io
581
+ io = nil
582
+ end
583
+
584
+ visitor = Psych::Visitors::RestrictedYAMLTree.create options
585
+ visitor << o
586
+ visitor.tree.yaml io, options
587
+ end
588
+
515
589
  ###
516
590
  # Dump a list of objects as separate documents to a document stream.
517
591
  #
@@ -575,7 +649,6 @@ module Psych
575
649
  self.unsafe_load f, filename: filename, **kwargs
576
650
  }
577
651
  end
578
- class << self; alias :load_file :unsafe_load_file; end
579
652
 
580
653
  ###
581
654
  # Safely loads the document contained in +filename+. Returns the yaml contained in
@@ -587,7 +660,17 @@ module Psych
587
660
  self.safe_load f, filename: filename, **kwargs
588
661
  }
589
662
  end
590
- class << self; alias load_file safe_load_file end
663
+
664
+ ###
665
+ # Loads the document contained in +filename+. Returns the yaml contained in
666
+ # +filename+ as a Ruby object, or if the file is empty, it returns
667
+ # the specified +fallback+ return value, which defaults to +false+.
668
+ # See load for options.
669
+ def self.load_file filename, **kwargs
670
+ File.open(filename, 'r:bom|utf-8') { |f|
671
+ self.load f, filename: filename, **kwargs
672
+ }
673
+ end
591
674
 
592
675
  # :stopdoc:
593
676
  def self.add_domain_type domain, type_tag, &block
@@ -86,7 +86,7 @@ module Psych
86
86
  if @symbols.include? sym
87
87
  super
88
88
  else
89
- raise DisallowedClass, 'Symbol'
89
+ raise DisallowedClass.new('load', 'Symbol')
90
90
  end
91
91
  end
92
92
 
@@ -96,7 +96,7 @@ module Psych
96
96
  if @classes.include? klassname
97
97
  super
98
98
  else
99
- raise DisallowedClass, klassname
99
+ raise DisallowedClass.new('load', klassname)
100
100
  end
101
101
  end
102
102
  end
@@ -7,8 +7,8 @@ module Psych
7
7
  end
8
8
 
9
9
  class DisallowedClass < Exception
10
- def initialize klass_name
11
- super "Tried to load unspecified class: #{klass_name}"
10
+ def initialize action, klass_name
11
+ super "Tried to #{action} unspecified class: #{klass_name}"
12
12
  end
13
13
  end
14
14
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Psych
4
4
  # The version of Psych you are using
5
- VERSION = '4.0.0'
5
+ VERSION = '4.0.1'
6
6
 
7
7
  if RUBY_ENGINE == 'jruby'
8
8
  DEFAULT_SNAKEYAML_VERSION = '1.28'.freeze
@@ -535,5 +535,51 @@ module Psych
535
535
  end
536
536
  end
537
537
  end
538
+
539
+ class RestrictedYAMLTree < YAMLTree
540
+ DEFAULT_PERMITTED_CLASSES = {
541
+ TrueClass => true,
542
+ FalseClass => true,
543
+ NilClass => true,
544
+ Integer => true,
545
+ Float => true,
546
+ String => true,
547
+ Array => true,
548
+ Hash => true,
549
+ }.compare_by_identity.freeze
550
+
551
+ def initialize emitter, ss, options
552
+ super
553
+ @permitted_classes = DEFAULT_PERMITTED_CLASSES.dup
554
+ Array(options[:permitted_classes]).each do |klass|
555
+ @permitted_classes[klass] = true
556
+ end
557
+ @permitted_symbols = {}.compare_by_identity
558
+ Array(options[:permitted_symbols]).each do |symbol|
559
+ @permitted_symbols[symbol] = true
560
+ end
561
+ @aliases = options.fetch(:aliases, false)
562
+ end
563
+
564
+ def accept target
565
+ if !@aliases && @st.key?(target)
566
+ raise BadAlias, "Tried to dump an aliased object"
567
+ end
568
+
569
+ unless @permitted_classes[target.class]
570
+ raise DisallowedClass.new('dump', target.class.name || target.class.inspect)
571
+ end
572
+
573
+ super
574
+ end
575
+
576
+ def visit_Symbol sym
577
+ unless @permitted_symbols[sym]
578
+ raise DisallowedClass.new('dump', "Symbol(#{sym.inspect})")
579
+ end
580
+
581
+ super
582
+ end
583
+ end
538
584
  end
539
585
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: psych
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.0.1
5
5
  platform: java
6
6
  authors:
7
7
  - Aaron Patterson
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-05-13 00:00:00.000000000 Z
13
+ date: 2021-06-07 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  requirement: !ruby/object:Gem::Requirement
@@ -19,8 +19,8 @@ dependencies:
19
19
  - !ruby/object:Gem::Version
20
20
  version: 0.1.7
21
21
  name: jar-dependencies
22
- type: :runtime
23
22
  prerelease: false
23
+ type: :runtime
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - ">="
@@ -138,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
138
  version: '0'
139
139
  requirements:
140
140
  - jar org.yaml:snakeyaml, 1.28
141
- rubygems_version: 3.0.6
141
+ rubygems_version: 3.1.6
142
142
  signing_key:
143
143
  specification_version: 4
144
144
  summary: Psych is a YAML parser and emitter