psych 4.0.0-java → 4.0.1-java

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