pax_global_header00006660000000000000000000000064143616746570014535gustar00rootroot0000000000000052 comment=a170b8eb466f25d16c4ceb1451a75de1d2c8c0cc psych-5.0.2/000077500000000000000000000000001436167465700126675ustar00rootroot00000000000000psych-5.0.2/.github/000077500000000000000000000000001436167465700142275ustar00rootroot00000000000000psych-5.0.2/.github/dependabot.yml000066400000000000000000000001661436167465700170620ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: 'github-actions' directory: '/' schedule: interval: 'weekly' psych-5.0.2/.github/workflows/000077500000000000000000000000001436167465700162645ustar00rootroot00000000000000psych-5.0.2/.github/workflows/libyaml.yml000066400000000000000000000031451436167465700204430ustar00rootroot00000000000000name: libyaml on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: ruby: [ head, 3.1, "3.0", 2.7, 2.6, 2.5, 2.4 ] libyaml: [0.1.7, 0.2.5] libyaml-prefix: [/tmp/local, ''] steps: - name: Install libraries run: sudo apt install haveged - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - name: Set up libyaml ${{ matrix.libyaml }} run: | wget https://pyyaml.org/download/libyaml/yaml-${{ matrix.libyaml }}.tar.gz tar xzf yaml-${{ matrix.libyaml }}.tar.gz - name: Compile libyaml ${{ matrix.libyaml }} run: | cd yaml-${{ matrix.libyaml }} ./configure --prefix=${{ matrix.libyaml-prefix }} make make install if: ${{ matrix.libyaml-prefix != '' }} - name: Install dependencies run: bundle install - name: Compile with libyaml run: rake compile -- --with-libyaml-dir=${{ matrix.libyaml-prefix }} if: ${{ matrix.libyaml-prefix != '' }} - name: Compile with libyaml source run: rake compile -- --with-libyaml-source-dir=$(pwd)/yaml-${{ matrix.libyaml }} if: ${{ matrix.libyaml-prefix == '' }} - name: Run test run: rake - name: Install gem run: | rake build gem install pkg/psych-*.gem -- --with-libyaml-dir=${{ matrix.libyaml-prefix }} if: ${{ matrix.ruby != 'head' && matrix.ruby != '3.1' && matrix.libyaml-prefix != '' }} psych-5.0.2/.github/workflows/macos.yml000066400000000000000000000011421436167465700201070ustar00rootroot00000000000000name: macos on: [push, pull_request] jobs: build: runs-on: macos-latest strategy: fail-fast: false matrix: ruby: [ head, 3.1, "3.0", 2.7, 2.6, 2.5, 2.4 ] steps: - uses: actions/checkout@v3 - name: Install libraries run: brew install libyaml - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - name: Install dependencies run: bundle install - name: Run test run: rake - name: Install gem run: rake install if: ${{ matrix.ruby != 'head' }} psych-5.0.2/.github/workflows/ubuntu-jruby.yml000066400000000000000000000010121436167465700214540ustar00rootroot00000000000000name: ubuntu-jruby on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: ruby: [ jruby, jruby-head ] steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - name: Install dependencies run: bundle install --jobs 1 - name: compile run: bundle exec rake compile - name: test run: bundle exec rake test psych-5.0.2/.github/workflows/ubuntu.yml000066400000000000000000000011641436167465700203330ustar00rootroot00000000000000name: ubuntu on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: ruby: [ head, 3.1, "3.0", 2.7, 2.6, 2.5, 2.4 ] steps: - name: Install libraries run: sudo apt install haveged libyaml-dev - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - name: Install dependencies run: bundle install - name: Run test run: rake - name: Install gem run: rake install if: ${{ matrix.ruby != 'head' }} psych-5.0.2/.github/workflows/windows.yml000066400000000000000000000012721436167465700205030ustar00rootroot00000000000000name: windows on: [push, pull_request] jobs: build: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: ruby: [ ucrt, mingw, mswin, 3.1, "3.0", 2.7, 2.6, 2.5, 2.4 ] os: [ windows-latest ] steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby-pkgs@v1 with: ruby-version: ${{ matrix.ruby }} vcpkg: libyaml - name: Install dependencies run: bundle install - name: Run test run: rake - name: Install gem run: rake install # use all possible head strings if: | !(contains('ucrt mingw mswin head', matrix.ruby)) psych-5.0.2/.gitignore000066400000000000000000000002061436167465700146550ustar00rootroot00000000000000*.swp *.bundle *.so *.jar *.class .mvn /.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ /vendor psych-5.0.2/CONTRIBUTING.md000066400000000000000000000025401436167465700151210ustar00rootroot00000000000000## How to contribute to Psych Full details [here](https://bugs.ruby-lang.org/projects/ruby/wiki/HowToContribute) #### **Did you find a bug?** * **Do not open an issue if the bug is a security vulnerability in Psych**, instead refer to our [security policy](https://www.ruby-lang.org/en/security/) and email [here](security@ruby-lang.org). * **Ensure the bug was not already reported** by searching on ruby-core, the ruby github repo and on Psych's github repo. More info [here](https://bugs.ruby-lang.org/projects/ruby/wiki/HowToReport) * If you're unable to find an open issue addressing the problem, [open a new one](https://bugs.ruby-lang.org/). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. #### **Did you write a patch that fixes a bug?** * Open a new GitHub pull request with the patch for small fixes. Anything larger look [here](https://bugs.ruby-lang.org/projects/ruby/wiki/HowToContribute); submit a Feature. * Ensure you clearly describe the problem and solution. Include the relevant ticket/issue number if applicable. Psych is a volunteer effort. We encourage you to pitch in and [join the team](https://github.com/ruby/psych/contributors)! Thanks! :heart: :heart: :heart: The Psych Team psych-5.0.2/Gemfile000066400000000000000000000002371436167465700141640ustar00rootroot00000000000000source 'https://rubygems.org' gemspec group :development do gem 'rake-compiler', ">= 0.4.1" gem 'test-unit' gem 'ruby-maven', :platforms => :jruby end psych-5.0.2/LICENSE000066400000000000000000000020701436167465700136730ustar00rootroot00000000000000MIT License Copyright (c) 2009 Aaron Patterson, et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. psych-5.0.2/Mavenfile000066400000000000000000000002221436167465700145140ustar00rootroot00000000000000#-*- mode: ruby -*- jar 'org.yaml:snakeyaml:${snakeyaml.version}' plugin :dependency, '2.8', :outputFile => 'pkg/classpath' # vim: syntax=Ruby psych-5.0.2/README.md000066400000000000000000000044311436167465700141500ustar00rootroot00000000000000# Psych * https://github.com/ruby/psych * https://docs.ruby-lang.org/en/master/Psych.html ## Description Psych is a YAML parser and emitter. Psych leverages [libyaml](https://pyyaml.org/wiki/LibYAML) for its YAML parsing and emitting capabilities. In addition to wrapping libyaml, Psych also knows how to serialize and de-serialize most Ruby objects to and from the YAML format. ## Examples ```ruby # Safely load YAML in to a Ruby object Psych.safe_load('--- foo') # => 'foo' # Emit YAML from a Ruby object Psych.dump("foo") # => "--- foo\n...\n" ``` ## Dependencies * libyaml ## Installation Psych has been included with MRI since 1.9.2, and is the default YAML parser in 1.9.3. If you want a newer gem release of Psych, you can use rubygems: gem install psych Psych supported the static build with specific version of libyaml sources. You can build psych with libyaml-0.2.5 like this. gem install psych -- --with-libyaml-source-dir=/path/to/libyaml-0.2.5 In order to use the gem release in your app, and not the stdlib version, you'll need the following: gem 'psych' require 'psych' Or if you use Bundler add this to your `Gemfile`: gem 'psych' JRuby ships with a pure Java implementation of Psych. ## License Copyright 2009 Aaron Patterson, et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. psych-5.0.2/Rakefile000066400000000000000000000026511436167465700143400ustar00rootroot00000000000000require "bundler" Bundler::GemHelper.install_tasks require "rake/testtask" Rake::TestTask.new(:test) do |t| t.libs << "test/lib" << "test" t.ruby_opts << "-rhelper" t.test_files = FileList['test/**/test_*.rb'] t.verbose = true t.warning = true end if RUBY_PLATFORM =~ /java/ require 'rake/javaextensiontask' Rake::JavaExtensionTask.new("psych") do |ext| require 'maven/ruby/maven' # force load of versions to overwrite constants with values from repo. load './lib/psych/versions.rb' # uses Mavenfile to write classpath into pkg/classpath # and tell maven via system properties the snakeyaml version # this is basically the same as running from the commandline: # rmvn dependency:build-classpath -Dsnakeyaml.version='use version from Psych::DEFAULT_SNAKEYAML_VERSION here' Maven::Ruby::Maven.new.exec('dependency:build-classpath', "-Dsnakeyaml.version=#{Psych::DEFAULT_SNAKEYAML_VERSION}", '-Dverbose=true') ext.source_version = '1.8' ext.target_version = '1.8' ext.classpath = File.read('pkg/classpath') ext.ext_dir = 'ext/java' end else require 'rake/extensiontask' Rake::ExtensionTask.new("psych") end task :sync_tool do require 'fileutils' FileUtils.cp "../ruby/tool/lib/core_assertions.rb", "./test/lib" FileUtils.cp "../ruby/tool/lib/envutil.rb", "./test/lib" FileUtils.cp "../ruby/tool/lib/find_executable.rb", "./test/lib" end task :default => [:compile, :test] psych-5.0.2/bin/000077500000000000000000000000001436167465700134375ustar00rootroot00000000000000psych-5.0.2/bin/console000077500000000000000000000001261436167465700150260ustar00rootroot00000000000000#!/usr/bin/env ruby require "bundler/setup" require "psych" require "irb" IRB.start psych-5.0.2/bin/setup000077500000000000000000000001121436167465700145170ustar00rootroot00000000000000#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install psych-5.0.2/ext/000077500000000000000000000000001436167465700134675ustar00rootroot00000000000000psych-5.0.2/ext/java/000077500000000000000000000000001436167465700144105ustar00rootroot00000000000000psych-5.0.2/ext/java/org/000077500000000000000000000000001436167465700151775ustar00rootroot00000000000000psych-5.0.2/ext/java/org/jruby/000077500000000000000000000000001436167465700163325ustar00rootroot00000000000000psych-5.0.2/ext/java/org/jruby/ext/000077500000000000000000000000001436167465700171325ustar00rootroot00000000000000psych-5.0.2/ext/java/org/jruby/ext/psych/000077500000000000000000000000001436167465700202605ustar00rootroot00000000000000psych-5.0.2/ext/java/org/jruby/ext/psych/PsychEmitter.java000066400000000000000000000352671436167465700235600ustar00rootroot00000000000000/***** BEGIN LICENSE BLOCK ***** * Version: EPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Eclipse Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.eclipse.org/legal/epl-v10.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Copyright (C) 2010 Charles O Nutter * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the EPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the EPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby.ext.psych; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; import org.jcodings.Encoding; import org.jcodings.specific.UTF8Encoding; import org.jruby.Ruby; import org.jruby.RubyArray; import org.jruby.RubyBoolean; import org.jruby.RubyClass; import org.jruby.RubyFixnum; import org.jruby.RubyModule; import org.jruby.RubyObject; import org.jruby.RubyString; import org.jruby.anno.JRubyMethod; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.IOOutputStream; import org.jruby.util.TypeConverter; import org.jruby.util.io.EncodingUtils; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.emitter.Emitter; import org.yaml.snakeyaml.emitter.EmitterException; import org.yaml.snakeyaml.error.Mark; import org.yaml.snakeyaml.events.AliasEvent; import org.yaml.snakeyaml.events.DocumentEndEvent; import org.yaml.snakeyaml.events.DocumentStartEvent; import org.yaml.snakeyaml.events.Event; import org.yaml.snakeyaml.events.ImplicitTuple; import org.yaml.snakeyaml.events.MappingEndEvent; import org.yaml.snakeyaml.events.MappingStartEvent; import org.yaml.snakeyaml.events.ScalarEvent; import org.yaml.snakeyaml.events.SequenceEndEvent; import org.yaml.snakeyaml.events.SequenceStartEvent; import org.yaml.snakeyaml.events.StreamEndEvent; import org.yaml.snakeyaml.events.StreamStartEvent; import static org.jruby.runtime.Visibility.*; public class PsychEmitter extends RubyObject { public static void initPsychEmitter(Ruby runtime, RubyModule psych) { RubyClass psychHandler = runtime.defineClassUnder("Handler", runtime.getObject(), runtime.getObject().getAllocator(), psych); RubyClass psychEmitter = runtime.defineClassUnder("Emitter", psychHandler, PsychEmitter::new, psych); psychEmitter.defineAnnotatedMethods(PsychEmitter.class); } public PsychEmitter(Ruby runtime, RubyClass klass) { super(runtime, klass); } @JRubyMethod(visibility = PRIVATE) public IRubyObject initialize(ThreadContext context, IRubyObject io) { options = new DumperOptions(); options.setIndent(2); this.io = io; return context.nil; } @JRubyMethod(visibility = PRIVATE) public IRubyObject initialize(ThreadContext context, IRubyObject io, IRubyObject rbOptions) { IRubyObject width = rbOptions.callMethod(context, "line_width"); IRubyObject canonical = rbOptions.callMethod(context, "canonical"); IRubyObject level = rbOptions.callMethod(context, "indentation"); options = new DumperOptions(); options.setCanonical(canonical.isTrue()); options.setIndent((int)level.convertToInteger().getLongValue()); line_width_set(context, width); this.io = io; return context.nil; } @JRubyMethod public IRubyObject start_stream(ThreadContext context, IRubyObject encoding) { TypeConverter.checkType(context, encoding, context.runtime.getFixnum()); initEmitter(context, encoding); StreamStartEvent event = new StreamStartEvent(NULL_MARK, NULL_MARK); emit(context, event); return this; } @JRubyMethod public IRubyObject end_stream(ThreadContext context) { StreamEndEvent event = new StreamEndEvent(NULL_MARK, NULL_MARK); emit(context, event); return this; } @JRubyMethod public IRubyObject start_document(ThreadContext context, IRubyObject _version, IRubyObject tags, IRubyObject implicit) { Ruby runtime = context.runtime; DumperOptions.Version version = null; boolean implicitBool = implicit.isTrue(); Map tagsMap = null; RubyClass arrayClass = runtime.getArray(); TypeConverter.checkType(context, _version, arrayClass); RubyArray versionAry = _version.convertToArray(); if (versionAry.size() == 2) { int versionInt0 = versionAry.eltInternal(0).convertToInteger().getIntValue(); int versionInt1 = versionAry.eltInternal(1).convertToInteger().getIntValue(); if (versionInt0 == 1) { if (versionInt1 == 0) { version = DumperOptions.Version.V1_0; } else if (versionInt1 == 1) { version = DumperOptions.Version.V1_1; } } if (version == null) { throw runtime.newArgumentError("invalid YAML version: " + versionAry); } } if (!tags.isNil()) { TypeConverter.checkType(context, tags, arrayClass); RubyArray tagsAry = tags.convertToArray(); if (tagsAry.size() > 0) { tagsMap = new HashMap<>(tagsAry.size()); for (int i = 0; i < tagsAry.size(); i++) { RubyArray tagsTuple = tagsAry.eltInternal(i).convertToArray(); if (tagsTuple.size() != 2) { throw runtime.newRuntimeError("tags tuple must be of length 2"); } IRubyObject key = tagsTuple.eltInternal(0); IRubyObject value = tagsTuple.eltInternal(1); tagsMap.put( key.asJavaString(), value.asJavaString()); } } } DocumentStartEvent event = new DocumentStartEvent(NULL_MARK, NULL_MARK, !implicitBool, version, tagsMap); emit(context, event); return this; } @JRubyMethod public IRubyObject end_document(ThreadContext context, IRubyObject implicit) { DocumentEndEvent event = new DocumentEndEvent(NULL_MARK, NULL_MARK, !implicit.isTrue()); emit(context, event); return this; } @JRubyMethod(required = 6) public IRubyObject scalar(ThreadContext context, IRubyObject[] args) { IRubyObject value = args[0]; IRubyObject anchor = args[1]; IRubyObject tag = args[2]; IRubyObject plain = args[3]; IRubyObject quoted = args[4]; IRubyObject style = args[5]; RubyClass stringClass = context.runtime.getString(); TypeConverter.checkType(context, value, stringClass); RubyString valueStr = (RubyString) value; valueStr = EncodingUtils.strConvEnc(context, valueStr, valueStr.getEncoding(), UTF8Encoding.INSTANCE); RubyString anchorStr = exportToUTF8(context, anchor, stringClass); RubyString tagStr = exportToUTF8(context, tag, stringClass); ScalarEvent event = new ScalarEvent( anchorStr == null ? null : anchorStr.asJavaString(), tagStr == null ? null : tagStr.asJavaString(), new ImplicitTuple(plain.isTrue(), quoted.isTrue()), valueStr.asJavaString(), NULL_MARK, NULL_MARK, SCALAR_STYLES[style.convertToInteger().getIntValue()]); emit(context, event); return this; } @JRubyMethod(required = 4) public IRubyObject start_sequence(ThreadContext context, IRubyObject[] args) { IRubyObject anchor = args[0]; IRubyObject tag = args[1]; IRubyObject implicit = args[2]; IRubyObject style = args[3]; RubyClass stringClass = context.runtime.getString(); RubyString anchorStr = exportToUTF8(context, anchor, stringClass); RubyString tagStr = exportToUTF8(context, tag, stringClass); SequenceStartEvent event = new SequenceStartEvent( anchorStr == null ? null : anchorStr.asJavaString(), tagStr == null ? null : tagStr.asJavaString(), implicit.isTrue(), NULL_MARK, NULL_MARK, FLOW_STYLES[style.convertToInteger().getIntValue()]); emit(context, event); return this; } @JRubyMethod public IRubyObject end_sequence(ThreadContext context) { SequenceEndEvent event = new SequenceEndEvent(NULL_MARK, NULL_MARK); emit(context, event); return this; } @JRubyMethod(required = 4) public IRubyObject start_mapping(ThreadContext context, IRubyObject[] args) { IRubyObject anchor = args[0]; IRubyObject tag = args[1]; IRubyObject implicit = args[2]; IRubyObject style = args[3]; RubyClass stringClass = context.runtime.getString(); RubyString anchorStr = exportToUTF8(context, anchor, stringClass); RubyString tagStr = exportToUTF8(context, tag, stringClass); MappingStartEvent event = new MappingStartEvent( anchorStr == null ? null : anchorStr.asJavaString(), tagStr == null ? null : tagStr.asJavaString(), implicit.isTrue(), NULL_MARK, NULL_MARK, FLOW_STYLES[style.convertToInteger().getIntValue()]); emit(context, event); return this; } @JRubyMethod public IRubyObject end_mapping(ThreadContext context) { MappingEndEvent event = new MappingEndEvent(NULL_MARK, NULL_MARK); emit(context, event); return this; } @JRubyMethod public IRubyObject alias(ThreadContext context, IRubyObject anchor) { RubyClass stringClass = context.runtime.getString(); RubyString anchorStr = exportToUTF8(context, anchor, stringClass); AliasEvent event = new AliasEvent(anchorStr.asJavaString(), NULL_MARK, NULL_MARK); emit(context, event); return this; } @JRubyMethod(name = "canonical=") public IRubyObject canonical_set(ThreadContext context, IRubyObject canonical) { // TODO: unclear if this affects a running emitter options.setCanonical(canonical.isTrue()); return canonical; } @JRubyMethod public IRubyObject canonical(ThreadContext context) { // TODO: unclear if this affects a running emitter return RubyBoolean.newBoolean(context, options.isCanonical()); } @JRubyMethod(name = "indentation=") public IRubyObject indentation_set(ThreadContext context, IRubyObject level) { // TODO: unclear if this affects a running emitter options.setIndent((int)level.convertToInteger().getLongValue()); return level; } @JRubyMethod public IRubyObject indentation(ThreadContext context) { // TODO: unclear if this affects a running emitter return context.runtime.newFixnum(options.getIndent()); } @JRubyMethod(name = "line_width=") public IRubyObject line_width_set(ThreadContext context, IRubyObject width) { int newWidth = (int)width.convertToInteger().getLongValue(); if (newWidth <= 0) newWidth = Integer.MAX_VALUE; options.setWidth(newWidth); return width; } @JRubyMethod public IRubyObject line_width(ThreadContext context) { return context.runtime.newFixnum(options.getWidth()); } private void emit(ThreadContext context, Event event) { try { if (emitter == null) throw context.runtime.newRuntimeError("uninitialized emitter"); emitter.emit(event); // flush writer after each emit writer.flush(); } catch (IOException ioe) { throw context.runtime.newIOErrorFromException(ioe); } catch (EmitterException ee) { throw context.runtime.newRuntimeError(ee.toString()); } } private void initEmitter(ThreadContext context, IRubyObject _encoding) { if (emitter != null) throw context.runtime.newRuntimeError("already initialized emitter"); Encoding encoding = PsychLibrary.YAMLEncoding.values()[(int)_encoding.convertToInteger().getLongValue()].encoding; Charset charset = context.runtime.getEncodingService().charsetForEncoding(encoding); writer = new OutputStreamWriter(new IOOutputStream(io, encoding), charset); emitter = new Emitter(writer, options); } private RubyString exportToUTF8(ThreadContext context, IRubyObject tag, RubyClass stringClass) { RubyString tagStr = null; if (!tag.isNil()) { TypeConverter.checkType(context, tag, stringClass); tagStr = (RubyString) tag; tagStr = EncodingUtils.strConvEnc(context, tagStr, tagStr.getEncoding(), UTF8Encoding.INSTANCE); } return tagStr; } Emitter emitter; Writer writer; DumperOptions options = new DumperOptions(); IRubyObject io; private static final Mark NULL_MARK = new Mark("", 0, 0, 0, new int[0], 0); // Map style constants from Psych values (ANY = 0 ... FOLDED = 5) // to SnakeYaml values; see psych/nodes/scalar.rb. private static final DumperOptions.ScalarStyle[] SCALAR_STYLES = { DumperOptions.ScalarStyle.PLAIN, // ANY DumperOptions.ScalarStyle.PLAIN, DumperOptions.ScalarStyle.SINGLE_QUOTED, DumperOptions.ScalarStyle.DOUBLE_QUOTED, DumperOptions.ScalarStyle.LITERAL, DumperOptions.ScalarStyle.FOLDED }; private static final DumperOptions.FlowStyle[] FLOW_STYLES = { DumperOptions.FlowStyle.AUTO, DumperOptions.FlowStyle.BLOCK, DumperOptions.FlowStyle.FLOW }; } psych-5.0.2/ext/java/org/jruby/ext/psych/PsychLibrary.java000066400000000000000000000126141436167465700235420ustar00rootroot00000000000000/***** BEGIN LICENSE BLOCK ***** * Version: EPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Eclipse Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.eclipse.org/legal/epl-v10.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Copyright (C) 2010 Charles O Nutter * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the EPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the EPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby.ext.psych; import java.io.InputStream; import java.io.IOException; import java.util.Properties; import org.jcodings.Encoding; import org.jcodings.specific.UTF16BEEncoding; import org.jcodings.specific.UTF16LEEncoding; import org.jcodings.specific.UTF8Encoding; import org.jruby.Ruby; import org.jruby.RubyArray; import org.jruby.RubyModule; import org.jruby.RubyString; import org.jruby.internal.runtime.methods.JavaMethod.JavaMethodZero; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.load.Library; import org.yaml.snakeyaml.error.Mark; public class PsychLibrary implements Library { private static final String DUMMY_VERSION = "0.0"; public void load(final Ruby runtime, boolean wrap) { RubyModule psych = runtime.defineModule("Psych"); // load version from properties packed with the jar Properties props = new Properties(); try( InputStream is = runtime.getJRubyClassLoader().getResourceAsStream("META-INF/maven/org.yaml/snakeyaml/pom.properties") ) { props.load(is); } catch( IOException e ) { // ignored } String snakeyamlVersion = props.getProperty("version", DUMMY_VERSION); if (snakeyamlVersion.endsWith("-SNAPSHOT")) { snakeyamlVersion = snakeyamlVersion.substring(0, snakeyamlVersion.length() - "-SNAPSHOT".length()); } // Try to determine if we have a new enough SnakeYAML. // Versions before 1.21 removed a Mark constructor that JRuby uses. // See https://github.com/bundler/bundler/issues/6878 if (snakeyamlVersion.equals(DUMMY_VERSION)) { try { // Use reflection to try to confirm we have a new enough version Mark.class.getConstructor(String.class, int.class, int.class, int.class, int[].class, int.class); } catch (NoSuchMethodException nsme) { throw runtime.newLoadError("bad SnakeYAML version, required 1.21 or higher; check your CLASSPATH for a conflicting jar"); } } else { // Parse version string to check for 1.21+ String[] majorMinor = snakeyamlVersion.split("\\."); if (majorMinor.length < 2 || Integer.parseInt(majorMinor[0]) < 1 || Integer.parseInt(majorMinor[1]) < 21) { throw runtime.newLoadError( "bad SnakeYAML version " + snakeyamlVersion + ", required 1.21 or higher; check your CLASSPATH for a conflicting jar"); } } RubyString version = runtime.newString(snakeyamlVersion + ".0"); version.setFrozen(true); psych.setConstant("SNAKEYAML_VERSION", version); String[] versionParts = version.toString().split("\\."); final RubyArray versionElements = runtime.newArray(runtime.newFixnum(Integer.parseInt(versionParts[0])), runtime.newFixnum(Integer.parseInt(versionParts[1])), runtime.newFixnum(Integer.parseInt(versionParts[2]))); versionElements.setFrozen(true); psych.getSingletonClass().addMethod("libyaml_version", new JavaMethodZero(psych, Visibility.PUBLIC) { @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) { return versionElements; } }); PsychParser.initPsychParser(runtime, psych); PsychEmitter.initPsychEmitter(runtime, psych); PsychToRuby.initPsychToRuby(runtime, psych); } public enum YAMLEncoding { YAML_ANY_ENCODING(UTF8Encoding.INSTANCE), YAML_UTF8_ENCODING(UTF8Encoding.INSTANCE), YAML_UTF16LE_ENCODING(UTF16LEEncoding.INSTANCE), YAML_UTF16BE_ENCODING(UTF16BEEncoding.INSTANCE); YAMLEncoding(Encoding encoding) { this.encoding = encoding; } public final Encoding encoding; } } psych-5.0.2/ext/java/org/jruby/ext/psych/PsychParser.java000066400000000000000000000460001436167465700233660ustar00rootroot00000000000000/***** BEGIN LICENSE BLOCK ***** * Version: EPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Eclipse Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.eclipse.org/legal/epl-v10.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Copyright (C) 2010 Charles O Nutter * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the EPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the EPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby.ext.psych; import java.io.ByteArrayInputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CodingErrorAction; import java.nio.charset.MalformedInputException; import java.util.Arrays; import java.util.Map; import org.jcodings.Encoding; import org.jcodings.specific.UTF16BEEncoding; import org.jcodings.specific.UTF16LEEncoding; import org.jcodings.specific.UTF8Encoding; import org.jcodings.unicode.UnicodeEncoding; import org.jruby.Ruby; import org.jruby.RubyArray; import org.jruby.RubyClass; import org.jruby.RubyEncoding; import org.jruby.RubyFixnum; import org.jruby.RubyIO; import org.jruby.RubyKernel; import org.jruby.RubyModule; import org.jruby.RubyObject; import org.jruby.RubyString; import org.jruby.anno.JRubyMethod; import static org.jruby.ext.psych.PsychLibrary.YAMLEncoding.*; import org.jruby.runtime.Block; import org.jruby.runtime.Helpers; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.callsite.CachingCallSite; import org.jruby.runtime.callsite.FunctionalCachingCallSite; import org.jruby.util.IOInputStream; import org.jruby.util.io.EncodingUtils; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.error.Mark; import org.yaml.snakeyaml.error.MarkedYAMLException; import org.yaml.snakeyaml.error.YAMLException; import org.yaml.snakeyaml.events.AliasEvent; import org.yaml.snakeyaml.events.DocumentEndEvent; import org.yaml.snakeyaml.events.DocumentStartEvent; import org.yaml.snakeyaml.events.Event; import org.yaml.snakeyaml.events.Event.ID; import org.yaml.snakeyaml.events.MappingStartEvent; import org.yaml.snakeyaml.events.ScalarEvent; import org.yaml.snakeyaml.events.SequenceStartEvent; import org.yaml.snakeyaml.parser.Parser; import org.yaml.snakeyaml.parser.ParserException; import org.yaml.snakeyaml.parser.ParserImpl; import org.yaml.snakeyaml.reader.ReaderException; import org.yaml.snakeyaml.reader.StreamReader; import org.yaml.snakeyaml.scanner.ScannerException; import static org.jruby.runtime.Helpers.arrayOf; import static org.jruby.runtime.Helpers.invoke; import org.jruby.util.ByteList; public class PsychParser extends RubyObject { public static final String JRUBY_CALL_SITES = "_jruby_call_sites"; private enum Call { path, event_location, start_stream, start_document, end_document, alias, scalar, start_sequence, end_sequence, start_mapping, end_mapping, end_stream } final CachingCallSite[] sites; public static void initPsychParser(Ruby runtime, RubyModule psych) { RubyClass psychParser = runtime.defineClassUnder("Parser", runtime.getObject(), PsychParser::new, psych); CachingCallSite[] sites = Arrays.stream(Call.values()) .map((call) -> new FunctionalCachingCallSite(call.name())) .toArray(CachingCallSite[]::new); psychParser.setInternalVariable(JRUBY_CALL_SITES, sites); runtime.getLoadService().require("psych/syntax_error"); psychParser.defineConstant("ANY", runtime.newFixnum(YAML_ANY_ENCODING.ordinal())); psychParser.defineConstant("UTF8", runtime.newFixnum(YAML_UTF8_ENCODING.ordinal())); psychParser.defineConstant("UTF16LE", runtime.newFixnum(YAML_UTF16LE_ENCODING.ordinal())); psychParser.defineConstant("UTF16BE", runtime.newFixnum(YAML_UTF16BE_ENCODING.ordinal())); psychParser.defineAnnotatedMethods(PsychParser.class); } public PsychParser(Ruby runtime, RubyClass klass) { super(runtime, klass); this.sites = (CachingCallSite[]) klass.getInternalVariable(JRUBY_CALL_SITES); } private IRubyObject stringOrNilFor(ThreadContext context, String value) { if (value == null) return context.nil; return stringFor(context, value); } private RubyString stringFor(ThreadContext context, String value) { Ruby runtime = context.runtime; Encoding encoding = runtime.getDefaultInternalEncoding(); if (encoding == null) { encoding = UTF8Encoding.INSTANCE; } Charset charset = RubyEncoding.UTF8; if (encoding.getCharset() != null) { charset = encoding.getCharset(); } ByteList bytes = new ByteList(value.getBytes(charset), encoding); RubyString string = RubyString.newString(runtime, bytes); return string; } private StreamReader readerFor(ThreadContext context, IRubyObject yaml) { if (yaml instanceof RubyString) { return readerForString(context, (RubyString) yaml); } // fall back on IOInputStream, using default charset return readerForIO(context, yaml); } private static StreamReader readerForIO(ThreadContext context, IRubyObject yaml) { boolean isIO = yaml instanceof RubyIO; if (isIO || yaml.respondsTo("read")) { // default to UTF8 unless RubyIO has UTF16 as encoding Charset charset = RubyEncoding.UTF8; if (isIO) { Encoding enc = ((RubyIO) yaml).getReadEncoding(); // libyaml treats non-utf encodings as utf-8 and hopes for the best. if (enc instanceof UTF16LEEncoding || enc instanceof UTF16BEEncoding) { charset = enc.getCharset(); } } CharsetDecoder decoder = charset.newDecoder(); decoder.onMalformedInput(CodingErrorAction.REPORT); return new StreamReader(new InputStreamReader(new IOInputStream(yaml), decoder)); } else { Ruby runtime = context.runtime; throw runtime.newTypeError(yaml, runtime.getIO()); } } private static StreamReader readerForString(ThreadContext context, RubyString string) { ByteList byteList = string.getByteList(); Encoding enc = byteList.getEncoding(); // if not unicode, transcode to UTF8 if (!(enc instanceof UnicodeEncoding)) { byteList = EncodingUtils.strConvEnc(context, byteList, enc, UTF8Encoding.INSTANCE); enc = UTF8Encoding.INSTANCE; } ByteArrayInputStream bais = new ByteArrayInputStream(byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize()); Charset charset = enc.getCharset(); assert charset != null : "charset for encoding " + enc + " should not be null"; InputStreamReader isr = new InputStreamReader(bais, charset); return new StreamReader(isr); } @JRubyMethod(name = "_native_parse") public IRubyObject parse(ThreadContext context, IRubyObject handler, IRubyObject yaml, IRubyObject path) { Ruby runtime = context.runtime; try { parser = new ParserImpl(readerFor(context, yaml)); if (path.isNil() && yaml.respondsTo("path")) { path = sites[Call.path.ordinal()].call(context, this, yaml); } while (true) { event = parser.getEvent(); Mark start = event.getStartMark(); IRubyObject start_line = runtime.newFixnum(start.getLine()); IRubyObject start_column = runtime.newFixnum(start.getColumn()); Mark end = event.getEndMark(); IRubyObject end_line = runtime.newFixnum(end.getLine()); IRubyObject end_column = runtime.newFixnum(end.getColumn()); sites[Call.event_location.ordinal()].call(context, this, handler, start_line, start_column, end_line, end_column); // FIXME: Event should expose a getID, so it can be switched if (event.is(ID.StreamStart)) { sites[Call.start_stream.ordinal()].call(context, this, handler, runtime.newFixnum(YAML_ANY_ENCODING.ordinal())); } else if (event.is(ID.DocumentStart)) { handleDocumentStart(context, (DocumentStartEvent) event, handler); } else if (event.is(ID.DocumentEnd)) { IRubyObject notExplicit = runtime.newBoolean(!((DocumentEndEvent) event).getExplicit()); sites[Call.end_document.ordinal()].call(context, this, handler, notExplicit); } else if (event.is(ID.Alias)) { IRubyObject alias = stringOrNilFor(context, ((AliasEvent)event).getAnchor()); sites[Call.alias.ordinal()].call(context, this, handler, alias); } else if (event.is(ID.Scalar)) { handleScalar(context, (ScalarEvent) event, handler); } else if (event.is(ID.SequenceStart)) { handleSequenceStart(context, (SequenceStartEvent) event, handler); } else if (event.is(ID.SequenceEnd)) { sites[Call.end_sequence.ordinal()].call(context, this, handler); } else if (event.is(ID.MappingStart)) { handleMappingStart(context, (MappingStartEvent) event, handler); } else if (event.is(ID.MappingEnd)) { sites[Call.end_mapping.ordinal()].call(context, this, handler); } else if (event.is(ID.StreamEnd)) { sites[Call.end_stream.ordinal()].call(context, this, handler); break; } } } catch (ParserException pe) { parser = null; raiseParserException(context, pe, path); } catch (ScannerException se) { parser = null; StringBuilder message = new StringBuilder("syntax error"); if (se.getProblemMark() != null) { message.append(se.getProblemMark().toString()); } raiseParserException(context, se, path); } catch (ReaderException re) { parser = null; raiseParserException(context, re, path); } catch (YAMLException ye) { Throwable cause = ye.getCause(); if (cause instanceof MalformedInputException) { // failure due to improperly encoded input raiseParserException(context, (MalformedInputException) cause, path); } throw ye; } catch (Throwable t) { Helpers.throwException(t); return this; } return this; } private void handleDocumentStart(ThreadContext context, DocumentStartEvent dse, IRubyObject handler) { Ruby runtime = context.runtime; DumperOptions.Version _version = dse.getVersion(); IRubyObject version = _version == null ? RubyArray.newArray(runtime) : RubyArray.newArray(runtime, runtime.newFixnum(_version.major()), runtime.newFixnum(_version.minor())); Map tagsMap = dse.getTags(); RubyArray tags = RubyArray.newArray(runtime); if (tagsMap != null && tagsMap.size() > 0) { for (Map.Entry tag : tagsMap.entrySet()) { IRubyObject key = stringFor(context, tag.getKey()); IRubyObject value = stringFor(context, tag.getValue()); tags.append(RubyArray.newArray(runtime, key, value)); } } IRubyObject notExplicit = runtime.newBoolean(!dse.getExplicit()); invoke(context, handler, "start_document", version, tags, notExplicit); } private void handleMappingStart(ThreadContext context, MappingStartEvent mse, IRubyObject handler) { Ruby runtime = context.runtime; IRubyObject anchor = stringOrNilFor(context, mse.getAnchor()); IRubyObject tag = stringOrNilFor(context, mse.getTag()); IRubyObject implicit = runtime.newBoolean(mse.getImplicit()); IRubyObject style = runtime.newFixnum(translateFlowStyle(mse.getFlowStyle())); sites[Call.start_mapping.ordinal()].call(context, this, handler, anchor, tag, implicit, style); } private void handleScalar(ThreadContext context, ScalarEvent se, IRubyObject handler) { Ruby runtime = context.runtime; IRubyObject anchor = stringOrNilFor(context, se.getAnchor()); IRubyObject tag = stringOrNilFor(context, se.getTag()); IRubyObject plain_implicit = runtime.newBoolean(se.getImplicit().canOmitTagInPlainScalar()); IRubyObject quoted_implicit = runtime.newBoolean(se.getImplicit().canOmitTagInNonPlainScalar()); IRubyObject style = runtime.newFixnum(translateStyle(se.getScalarStyle())); IRubyObject val = stringFor(context, se.getValue()); sites[Call.scalar.ordinal()].call(context, this, handler, val, anchor, tag, plain_implicit, quoted_implicit, style); } private void handleSequenceStart(ThreadContext context, SequenceStartEvent sse, IRubyObject handler) { Ruby runtime = context.runtime; IRubyObject anchor = stringOrNilFor(context, sse.getAnchor()); IRubyObject tag = stringOrNilFor(context, sse.getTag()); IRubyObject implicit = runtime.newBoolean(sse.getImplicit()); IRubyObject style = runtime.newFixnum(translateFlowStyle(sse.getFlowStyle())); sites[Call.start_sequence.ordinal()].call(context, this, handler, anchor, tag, implicit, style); } private static void raiseParserException(ThreadContext context, ReaderException re, IRubyObject rbPath) { Ruby runtime; RubyClass se; IRubyObject exception; runtime = context.runtime; se = (RubyClass)runtime.getModule("Psych").getConstant("SyntaxError"); exception = se.newInstance(context, new IRubyObject[] { rbPath, runtime.newFixnum(0), runtime.newFixnum(0), runtime.newFixnum(re.getPosition()), (null == re.getName() ? runtime.getNil() : runtime.newString(re.getName())), (null == re.toString() ? runtime.getNil() : runtime.newString(re.toString())) }, Block.NULL_BLOCK); RubyKernel.raise(context, runtime.getKernel(), new IRubyObject[] { exception }, Block.NULL_BLOCK); } private static void raiseParserException(ThreadContext context, MarkedYAMLException mye, IRubyObject rbPath) { Ruby runtime; Mark mark; RubyClass se; IRubyObject exception; runtime = context.runtime; se = (RubyClass)runtime.getModule("Psych").getConstant("SyntaxError"); mark = mye.getProblemMark(); exception = se.newInstance(context, new IRubyObject[] { rbPath, runtime.newFixnum(mark.getLine() + 1), runtime.newFixnum(mark.getColumn() + 1), runtime.newFixnum(mark.getIndex()), (null == mye.getProblem() ? runtime.getNil() : runtime.newString(mye.getProblem())), (null == mye.getContext() ? runtime.getNil() : runtime.newString(mye.getContext())) }, Block.NULL_BLOCK); RubyKernel.raise(context, runtime.getKernel(), new IRubyObject[] { exception }, Block.NULL_BLOCK); } private static void raiseParserException(ThreadContext context, MalformedInputException mie, IRubyObject rbPath) { Ruby runtime;; RubyClass se; IRubyObject exception; runtime = context.runtime; se = (RubyClass)runtime.getModule("Psych").getConstant("SyntaxError"); mie.getInputLength(); exception = se.newInstance(context, arrayOf( rbPath, runtime.newFixnum(-1), runtime.newFixnum(-1), runtime.newFixnum(mie.getInputLength()), runtime.getNil(), runtime.getNil() ), Block.NULL_BLOCK); RubyKernel.raise(context, runtime.getKernel(), new IRubyObject[] { exception }, Block.NULL_BLOCK); } private static int translateStyle(DumperOptions.ScalarStyle style) { if (style == null) return 0; // any switch (style) { case PLAIN: return 1; // plain case SINGLE_QUOTED: return 2; // single-quoted case DOUBLE_QUOTED: return 3; // double-quoted case LITERAL: return 4; // literal case FOLDED: return 5; // folded default: return 0; // any } } private static int translateFlowStyle(DumperOptions.FlowStyle flowStyle) { switch (flowStyle) { case AUTO: return 0; case BLOCK: return 1; case FLOW: default: return 2; } } @JRubyMethod public IRubyObject mark(ThreadContext context) { Ruby runtime = context.runtime; Event event = null; if (parser != null) { event = parser.peekEvent(); if (event == null) event = this.event; } if (event == null) { return ((RubyClass)context.runtime.getClassFromPath("Psych::Parser::Mark")).newInstance( context, RubyFixnum.zero(runtime), RubyFixnum.zero(runtime), RubyFixnum.zero(runtime), Block.NULL_BLOCK ); } Mark mark = event.getStartMark(); return ((RubyClass)context.runtime.getClassFromPath("Psych::Parser::Mark")).newInstance( context, RubyFixnum.zero(runtime), runtime.newFixnum(mark.getLine()), runtime.newFixnum(mark.getColumn()), Block.NULL_BLOCK ); } private Parser parser; private Event event; } psych-5.0.2/ext/java/org/jruby/ext/psych/PsychToRuby.java000066400000000000000000000072051436167465700233620ustar00rootroot00000000000000/***** BEGIN LICENSE BLOCK ***** * Version: EPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Eclipse Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.eclipse.org/legal/epl-v10.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Copyright (C) 2010 Charles O Nutter * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the EPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the EPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby.ext.psych; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyModule; import org.jruby.RubyObject; import org.jruby.RubyException; import org.jruby.anno.JRubyMethod; import org.jruby.exceptions.RaiseException; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import static org.jruby.runtime.Visibility.*; public class PsychToRuby { public static void initPsychToRuby(Ruby runtime, RubyModule psych) { RubyClass classLoader = runtime.defineClassUnder("ClassLoader", runtime.getObject(), RubyObject.OBJECT_ALLOCATOR, psych); RubyModule visitors = runtime.defineModuleUnder("Visitors", psych); RubyClass visitor = runtime.defineClassUnder("Visitor", runtime.getObject(), runtime.getObject().getAllocator(), visitors); RubyClass psychToRuby = runtime.defineClassUnder("ToRuby", visitor, RubyObject.OBJECT_ALLOCATOR, visitors); psychToRuby.defineAnnotatedMethods(ToRuby.class); classLoader.defineAnnotatedMethods(ClassLoader.class); } public static class ToRuby { @JRubyMethod(visibility = PRIVATE) public static IRubyObject build_exception(ThreadContext context, IRubyObject self, IRubyObject klass, IRubyObject message) { if (klass instanceof RubyClass) { IRubyObject exception = ((RubyClass)klass).allocate(); ((RubyException)exception).setMessage(message); return exception; } else { throw context.runtime.newTypeError(klass, context.runtime.getClassClass()); } } } public static class ClassLoader { @JRubyMethod(visibility = PRIVATE) public static IRubyObject path2class(ThreadContext context, IRubyObject self, IRubyObject path) { try { return context.runtime.getClassFromPath(path.asJavaString()); } catch (RaiseException re) { if (re.getException().getMetaClass() == context.runtime.getNameError()) { throw context.runtime.newArgumentError("undefined class/module " + path); } throw re; } } } } psych-5.0.2/ext/psych/000077500000000000000000000000001436167465700146155ustar00rootroot00000000000000psych-5.0.2/ext/psych/depend000066400000000000000000000005631436167465700160030ustar00rootroot00000000000000$(TARGET_SO): $(LIBYAML) libyaml $(LIBYAML): cd libyaml && $(MAKE) $(AR) $(ARFLAGS) $(LIBYAML) $(LIBYAML_OBJDIR)/*.$(OBJEXT) $(RANLIB) $(LIBYAML) clean-so:: -cd libyaml && $(MAKE) clean distclean-so:: -cd libyaml && $(MAKE) distclean -$(Q)$(RMDIRS) libyaml/* libyaml $(OBJS): $(HDRS) $(ruby_headers) \ $(hdrdir)/ruby/encoding.h \ $(hdrdir)/ruby/oniguruma.h psych-5.0.2/ext/psych/extconf.rb000066400000000000000000000034051436167465700166120ustar00rootroot00000000000000# -*- coding: us-ascii -*- # frozen_string_literal: true require 'mkmf' if $mswin or $mingw or $cygwin $CPPFLAGS << " -DYAML_DECLARE_STATIC" end yaml_source = with_config("libyaml-source-dir") if yaml_source yaml_source = yaml_source.gsub(/\$\((\w+)\)|\$\{(\w+)\}/) {ENV[$1||$2]} yaml_source = yaml_source.chomp("/") yaml_configure = "#{File.expand_path(yaml_source)}/configure" unless File.exist?(yaml_configure) raise "Configure script not found in #{yaml_source.quote}" end puts("Configuring libyaml source in #{yaml_source.quote}") yaml = "libyaml" Dir.mkdir(yaml) unless File.directory?(yaml) shared = $enable_shared || !$static args = [ yaml_configure, "--enable-#{shared ? 'shared' : 'static'}", "--host=#{RbConfig::CONFIG['host'].sub(/-unknown-/, '-').sub(/arm64/, 'arm')}", "CC=#{RbConfig::CONFIG['CC']}", *(["CFLAGS=-w"] if RbConfig::CONFIG["GCC"] == "yes"), ] puts(args.quote.join(' ')) unless system(*args, chdir: yaml) raise "failed to configure libyaml" end inc = yaml_source.start_with?("#$srcdir/") ? "$(srcdir)#{yaml_source[$srcdir.size..-1]}" : yaml_source $INCFLAGS << " -I#{yaml}/include -I#{inc}/include" puts("INCFLAGS=#$INCFLAGS") libyaml = "libyaml.#$LIBEXT" $cleanfiles << libyaml $LOCAL_LIBS.prepend("$(LIBYAML) ") else # default to pre-installed libyaml pkg_config('yaml-0.1') dir_config('libyaml') find_header('yaml.h') or abort "yaml.h not found" find_library('yaml', 'yaml_get_version') or abort "libyaml not found" end create_makefile 'psych' do |mk| mk << "LIBYAML = #{libyaml}".strip << "\n" mk << "LIBYAML_OBJDIR = libyaml/src#{shared ? '/.libs' : ''}\n" mk << "OBJEXT = #$OBJEXT" mk << "RANLIB = #{config_string('RANLIB') || config_string('NULLCMD')}\n" end # :startdoc: psych-5.0.2/ext/psych/psych.c000066400000000000000000000013611436167465700161100ustar00rootroot00000000000000#include /* call-seq: Psych.libyaml_version * * Returns the version of libyaml being used */ static VALUE libyaml_version(VALUE module) { int major, minor, patch; VALUE list[3]; yaml_get_version(&major, &minor, &patch); list[0] = INT2NUM(major); list[1] = INT2NUM(minor); list[2] = INT2NUM(patch); return rb_ary_new4((long)3, list); } VALUE mPsych; void Init_psych(void) { #ifdef HAVE_RB_EXT_RACTOR_SAFE RB_EXT_RACTOR_SAFE(true); #endif mPsych = rb_define_module("Psych"); rb_define_singleton_method(mPsych, "libyaml_version", libyaml_version, 0); Init_psych_parser(); Init_psych_emitter(); Init_psych_to_ruby(); Init_psych_yaml_tree(); } /* vim: set noet sws=4 sw=4: */ psych-5.0.2/ext/psych/psych.h000066400000000000000000000003561436167465700161200ustar00rootroot00000000000000#ifndef PSYCH_H #define PSYCH_H #include #include #include #include #include #include #include extern VALUE mPsych; #endif psych-5.0.2/ext/psych/psych_emitter.c000066400000000000000000000330731436167465700176460ustar00rootroot00000000000000#include #if !defined(RARRAY_CONST_PTR) #define RARRAY_CONST_PTR(s) (const VALUE *)RARRAY_PTR(s) #endif #if !defined(RARRAY_AREF) #define RARRAY_AREF(a, i) RARRAY_CONST_PTR(a)[i] #endif VALUE cPsychEmitter; static ID id_io; static ID id_write; static ID id_line_width; static ID id_indentation; static ID id_canonical; static void emit(yaml_emitter_t * emitter, yaml_event_t * event) { if(!yaml_emitter_emit(emitter, event)) rb_raise(rb_eRuntimeError, "%s", emitter->problem); } static int writer(void *ctx, unsigned char *buffer, size_t size) { VALUE self = (VALUE)ctx, io = rb_attr_get(self, id_io); VALUE str = rb_enc_str_new((const char *)buffer, (long)size, rb_utf8_encoding()); VALUE wrote = rb_funcall(io, id_write, 1, str); return (int)NUM2INT(wrote); } static void dealloc(void * ptr) { yaml_emitter_t * emitter; emitter = (yaml_emitter_t *)ptr; yaml_emitter_delete(emitter); xfree(emitter); } #if 0 static size_t memsize(const void *ptr) { const yaml_emitter_t *emitter = ptr; /* TODO: calculate emitter's size */ return 0; } #endif static const rb_data_type_t psych_emitter_type = { "Psych/emitter", {0, dealloc, 0,}, 0, 0, #ifdef RUBY_TYPED_FREE_IMMEDIATELY RUBY_TYPED_FREE_IMMEDIATELY, #endif }; static VALUE allocate(VALUE klass) { yaml_emitter_t * emitter; VALUE obj = TypedData_Make_Struct(klass, yaml_emitter_t, &psych_emitter_type, emitter); yaml_emitter_initialize(emitter); yaml_emitter_set_unicode(emitter, 1); yaml_emitter_set_indent(emitter, 2); return obj; } /* call-seq: Psych::Emitter.new(io, options = Psych::Emitter::OPTIONS) * * Create a new Psych::Emitter that writes to +io+. */ static VALUE initialize(int argc, VALUE *argv, VALUE self) { yaml_emitter_t * emitter; VALUE io, options; VALUE line_width; VALUE indent; VALUE canonical; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); if (rb_scan_args(argc, argv, "11", &io, &options) == 2) { line_width = rb_funcall(options, id_line_width, 0); indent = rb_funcall(options, id_indentation, 0); canonical = rb_funcall(options, id_canonical, 0); yaml_emitter_set_width(emitter, NUM2INT(line_width)); yaml_emitter_set_indent(emitter, NUM2INT(indent)); yaml_emitter_set_canonical(emitter, Qtrue == canonical ? 1 : 0); } rb_ivar_set(self, id_io, io); yaml_emitter_set_output(emitter, writer, (void *)self); return self; } /* call-seq: emitter.start_stream(encoding) * * Start a stream emission with +encoding+ * * See Psych::Handler#start_stream */ static VALUE start_stream(VALUE self, VALUE encoding) { yaml_emitter_t * emitter; yaml_event_t event; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); Check_Type(encoding, T_FIXNUM); yaml_stream_start_event_initialize(&event, (yaml_encoding_t)NUM2INT(encoding)); emit(emitter, &event); return self; } /* call-seq: emitter.end_stream * * End a stream emission * * See Psych::Handler#end_stream */ static VALUE end_stream(VALUE self) { yaml_emitter_t * emitter; yaml_event_t event; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); yaml_stream_end_event_initialize(&event); emit(emitter, &event); return self; } /* call-seq: emitter.start_document(version, tags, implicit) * * Start a document emission with YAML +version+, +tags+, and an +implicit+ * start. * * See Psych::Handler#start_document */ static VALUE start_document(VALUE self, VALUE version, VALUE tags, VALUE imp) { yaml_emitter_t * emitter; yaml_tag_directive_t * head = NULL; yaml_tag_directive_t * tail = NULL; yaml_event_t event; yaml_version_directive_t version_directive; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); Check_Type(version, T_ARRAY); if(RARRAY_LEN(version) > 0) { VALUE major = rb_ary_entry(version, (long)0); VALUE minor = rb_ary_entry(version, (long)1); version_directive.major = NUM2INT(major); version_directive.minor = NUM2INT(minor); } if(RTEST(tags)) { long i = 0; long len; rb_encoding * encoding = rb_utf8_encoding(); Check_Type(tags, T_ARRAY); len = RARRAY_LEN(tags); head = xcalloc((size_t)len, sizeof(yaml_tag_directive_t)); tail = head; for(i = 0; i < len && i < RARRAY_LEN(tags); i++) { VALUE tuple = RARRAY_AREF(tags, i); VALUE name; VALUE value; Check_Type(tuple, T_ARRAY); if(RARRAY_LEN(tuple) < 2) { xfree(head); rb_raise(rb_eRuntimeError, "tag tuple must be of length 2"); } name = RARRAY_AREF(tuple, 0); value = RARRAY_AREF(tuple, 1); StringValue(name); StringValue(value); name = rb_str_export_to_enc(name, encoding); value = rb_str_export_to_enc(value, encoding); tail->handle = (yaml_char_t *)StringValueCStr(name); tail->prefix = (yaml_char_t *)StringValueCStr(value); tail++; } } yaml_document_start_event_initialize( &event, (RARRAY_LEN(version) > 0) ? &version_directive : NULL, head, tail, imp ? 1 : 0 ); emit(emitter, &event); if(head) xfree(head); return self; } /* call-seq: emitter.end_document(implicit) * * End a document emission with an +implicit+ ending. * * See Psych::Handler#end_document */ static VALUE end_document(VALUE self, VALUE imp) { yaml_emitter_t * emitter; yaml_event_t event; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); yaml_document_end_event_initialize(&event, imp ? 1 : 0); emit(emitter, &event); return self; } /* call-seq: emitter.scalar(value, anchor, tag, plain, quoted, style) * * Emit a scalar with +value+, +anchor+, +tag+, and a +plain+ or +quoted+ * string type with +style+. * * See Psych::Handler#scalar */ static VALUE scalar( VALUE self, VALUE value, VALUE anchor, VALUE tag, VALUE plain, VALUE quoted, VALUE style ) { yaml_emitter_t * emitter; yaml_event_t event; rb_encoding *encoding; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); Check_Type(value, T_STRING); encoding = rb_utf8_encoding(); value = rb_str_export_to_enc(value, encoding); if(!NIL_P(anchor)) { Check_Type(anchor, T_STRING); anchor = rb_str_export_to_enc(anchor, encoding); } if(!NIL_P(tag)) { Check_Type(tag, T_STRING); tag = rb_str_export_to_enc(tag, encoding); } yaml_scalar_event_initialize( &event, (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)), (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)), (yaml_char_t*)StringValuePtr(value), (int)RSTRING_LEN(value), plain ? 1 : 0, quoted ? 1 : 0, (yaml_scalar_style_t)NUM2INT(style) ); emit(emitter, &event); return self; } /* call-seq: emitter.start_sequence(anchor, tag, implicit, style) * * Start emitting a sequence with +anchor+, a +tag+, +implicit+ sequence * start and end, along with +style+. * * See Psych::Handler#start_sequence */ static VALUE start_sequence( VALUE self, VALUE anchor, VALUE tag, VALUE implicit, VALUE style ) { yaml_emitter_t * emitter; yaml_event_t event; rb_encoding * encoding = rb_utf8_encoding(); if(!NIL_P(anchor)) { Check_Type(anchor, T_STRING); anchor = rb_str_export_to_enc(anchor, encoding); } if(!NIL_P(tag)) { Check_Type(tag, T_STRING); tag = rb_str_export_to_enc(tag, encoding); } TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); yaml_sequence_start_event_initialize( &event, (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)), (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)), implicit ? 1 : 0, (yaml_sequence_style_t)NUM2INT(style) ); emit(emitter, &event); return self; } /* call-seq: emitter.end_sequence * * End sequence emission. * * See Psych::Handler#end_sequence */ static VALUE end_sequence(VALUE self) { yaml_emitter_t * emitter; yaml_event_t event; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); yaml_sequence_end_event_initialize(&event); emit(emitter, &event); return self; } /* call-seq: emitter.start_mapping(anchor, tag, implicit, style) * * Start emitting a YAML map with +anchor+, +tag+, an +implicit+ start * and end, and +style+. * * See Psych::Handler#start_mapping */ static VALUE start_mapping( VALUE self, VALUE anchor, VALUE tag, VALUE implicit, VALUE style ) { yaml_emitter_t * emitter; yaml_event_t event; rb_encoding *encoding; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); encoding = rb_utf8_encoding(); if(!NIL_P(anchor)) { Check_Type(anchor, T_STRING); anchor = rb_str_export_to_enc(anchor, encoding); } if(!NIL_P(tag)) { Check_Type(tag, T_STRING); tag = rb_str_export_to_enc(tag, encoding); } yaml_mapping_start_event_initialize( &event, (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)), (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)), implicit ? 1 : 0, (yaml_mapping_style_t)NUM2INT(style) ); emit(emitter, &event); return self; } /* call-seq: emitter.end_mapping * * Emit the end of a mapping. * * See Psych::Handler#end_mapping */ static VALUE end_mapping(VALUE self) { yaml_emitter_t * emitter; yaml_event_t event; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); yaml_mapping_end_event_initialize(&event); emit(emitter, &event); return self; } /* call-seq: emitter.alias(anchor) * * Emit an alias with +anchor+. * * See Psych::Handler#alias */ static VALUE alias(VALUE self, VALUE anchor) { yaml_emitter_t * emitter; yaml_event_t event; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); if(!NIL_P(anchor)) { Check_Type(anchor, T_STRING); anchor = rb_str_export_to_enc(anchor, rb_utf8_encoding()); } yaml_alias_event_initialize( &event, (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)) ); emit(emitter, &event); return self; } /* call-seq: emitter.canonical = true * * Set the output style to canonical, or not. */ static VALUE set_canonical(VALUE self, VALUE style) { yaml_emitter_t * emitter; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); yaml_emitter_set_canonical(emitter, Qtrue == style ? 1 : 0); return style; } /* call-seq: emitter.canonical * * Get the output style, canonical or not. */ static VALUE canonical(VALUE self) { yaml_emitter_t * emitter; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); return (emitter->canonical == 0) ? Qfalse : Qtrue; } /* call-seq: emitter.indentation = level * * Set the indentation level to +level+. The level must be less than 10 and * greater than 1. */ static VALUE set_indentation(VALUE self, VALUE level) { yaml_emitter_t * emitter; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); yaml_emitter_set_indent(emitter, NUM2INT(level)); return level; } /* call-seq: emitter.indentation * * Get the indentation level. */ static VALUE indentation(VALUE self) { yaml_emitter_t * emitter; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); return INT2NUM(emitter->best_indent); } /* call-seq: emitter.line_width * * Get the preferred line width. */ static VALUE line_width(VALUE self) { yaml_emitter_t * emitter; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); return INT2NUM(emitter->best_width); } /* call-seq: emitter.line_width = width * * Set the preferred line with to +width+. */ static VALUE set_line_width(VALUE self, VALUE width) { yaml_emitter_t * emitter; TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter); yaml_emitter_set_width(emitter, NUM2INT(width)); return width; } void Init_psych_emitter(void) { #undef rb_intern VALUE psych = rb_define_module("Psych"); VALUE handler = rb_define_class_under(psych, "Handler", rb_cObject); cPsychEmitter = rb_define_class_under(psych, "Emitter", handler); rb_define_alloc_func(cPsychEmitter, allocate); rb_define_method(cPsychEmitter, "initialize", initialize, -1); rb_define_method(cPsychEmitter, "start_stream", start_stream, 1); rb_define_method(cPsychEmitter, "end_stream", end_stream, 0); rb_define_method(cPsychEmitter, "start_document", start_document, 3); rb_define_method(cPsychEmitter, "end_document", end_document, 1); rb_define_method(cPsychEmitter, "scalar", scalar, 6); rb_define_method(cPsychEmitter, "start_sequence", start_sequence, 4); rb_define_method(cPsychEmitter, "end_sequence", end_sequence, 0); rb_define_method(cPsychEmitter, "start_mapping", start_mapping, 4); rb_define_method(cPsychEmitter, "end_mapping", end_mapping, 0); rb_define_method(cPsychEmitter, "alias", alias, 1); rb_define_method(cPsychEmitter, "canonical", canonical, 0); rb_define_method(cPsychEmitter, "canonical=", set_canonical, 1); rb_define_method(cPsychEmitter, "indentation", indentation, 0); rb_define_method(cPsychEmitter, "indentation=", set_indentation, 1); rb_define_method(cPsychEmitter, "line_width", line_width, 0); rb_define_method(cPsychEmitter, "line_width=", set_line_width, 1); id_io = rb_intern("io"); id_write = rb_intern("write"); id_line_width = rb_intern("line_width"); id_indentation = rb_intern("indentation"); id_canonical = rb_intern("canonical"); } /* vim: set noet sws=4 sw=4: */ psych-5.0.2/ext/psych/psych_emitter.h000066400000000000000000000001541436167465700176450ustar00rootroot00000000000000#ifndef PSYCH_EMITTER_H #define PSYCH_EMITTER_H #include void Init_psych_emitter(void); #endif psych-5.0.2/ext/psych/psych_parser.c000066400000000000000000000352121436167465700174660ustar00rootroot00000000000000#include VALUE cPsychParser; static ID id_read; static ID id_path; static ID id_empty; static ID id_start_stream; static ID id_end_stream; static ID id_start_document; static ID id_end_document; static ID id_alias; static ID id_scalar; static ID id_start_sequence; static ID id_end_sequence; static ID id_start_mapping; static ID id_end_mapping; static ID id_event_location; #define PSYCH_TRANSCODE(_str, _yaml_enc, _internal_enc) \ do { \ rb_enc_associate_index((_str), (_yaml_enc)); \ if(_internal_enc) \ (_str) = rb_str_export_to_enc((_str), (_internal_enc)); \ } while (0) static int io_reader(void * data, unsigned char *buf, size_t size, size_t *read) { VALUE io = (VALUE)data; VALUE string = rb_funcall(io, id_read, 1, SIZET2NUM(size)); *read = 0; if(! NIL_P(string)) { void * str = (void *)StringValuePtr(string); *read = (size_t)RSTRING_LEN(string); memcpy(buf, str, *read); } return 1; } static void dealloc(void * ptr) { yaml_parser_t * parser; parser = (yaml_parser_t *)ptr; yaml_parser_delete(parser); xfree(parser); } #if 0 static size_t memsize(const void *ptr) { const yaml_parser_t *parser = ptr; /* TODO: calculate parser's size */ return 0; } #endif static const rb_data_type_t psych_parser_type = { "Psych/parser", {0, dealloc, 0,}, 0, 0, #ifdef RUBY_TYPED_FREE_IMMEDIATELY RUBY_TYPED_FREE_IMMEDIATELY, #endif }; static VALUE allocate(VALUE klass) { yaml_parser_t * parser; VALUE obj = TypedData_Make_Struct(klass, yaml_parser_t, &psych_parser_type, parser); yaml_parser_initialize(parser); return obj; } static VALUE make_exception(yaml_parser_t * parser, VALUE path) { if (parser->error == YAML_MEMORY_ERROR) { return rb_eNoMemError; } else { size_t line, column; VALUE ePsychSyntaxError; line = parser->context_mark.line + 1; column = parser->context_mark.column + 1; ePsychSyntaxError = rb_const_get(mPsych, rb_intern("SyntaxError")); return rb_funcall(ePsychSyntaxError, rb_intern("new"), 6, path, SIZET2NUM(line), SIZET2NUM(column), SIZET2NUM(parser->problem_offset), parser->problem ? rb_usascii_str_new2(parser->problem) : Qnil, parser->context ? rb_usascii_str_new2(parser->context) : Qnil); } } static VALUE transcode_string(VALUE src, int * parser_encoding) { int utf8 = rb_utf8_encindex(); int utf16le = rb_enc_find_index("UTF-16LE"); int utf16be = rb_enc_find_index("UTF-16BE"); int source_encoding = rb_enc_get_index(src); if (source_encoding == utf8) { *parser_encoding = YAML_UTF8_ENCODING; return src; } if (source_encoding == utf16le) { *parser_encoding = YAML_UTF16LE_ENCODING; return src; } if (source_encoding == utf16be) { *parser_encoding = YAML_UTF16BE_ENCODING; return src; } src = rb_str_export_to_enc(src, rb_utf8_encoding()); RB_GC_GUARD(src); *parser_encoding = YAML_UTF8_ENCODING; return src; } static VALUE transcode_io(VALUE src, int * parser_encoding) { VALUE io_external_encoding; int io_external_enc_index; io_external_encoding = rb_funcall(src, rb_intern("external_encoding"), 0); /* if no encoding is returned, assume ascii8bit. */ if (NIL_P(io_external_encoding)) { io_external_enc_index = rb_ascii8bit_encindex(); } else { io_external_enc_index = rb_to_encoding_index(io_external_encoding); } /* Treat US-ASCII as utf_8 */ if (io_external_enc_index == rb_usascii_encindex()) { *parser_encoding = YAML_UTF8_ENCODING; return src; } if (io_external_enc_index == rb_utf8_encindex()) { *parser_encoding = YAML_UTF8_ENCODING; return src; } if (io_external_enc_index == rb_enc_find_index("UTF-16LE")) { *parser_encoding = YAML_UTF16LE_ENCODING; return src; } if (io_external_enc_index == rb_enc_find_index("UTF-16BE")) { *parser_encoding = YAML_UTF16BE_ENCODING; return src; } /* Just guess on ASCII-8BIT */ if (io_external_enc_index == rb_ascii8bit_encindex()) { *parser_encoding = YAML_ANY_ENCODING; return src; } /* If the external encoding is something we don't know how to handle, * fall back to YAML_ANY_ENCODING. */ *parser_encoding = YAML_ANY_ENCODING; return src; } static VALUE protected_start_stream(VALUE pointer) { VALUE *args = (VALUE *)pointer; return rb_funcall(args[0], id_start_stream, 1, args[1]); } static VALUE protected_start_document(VALUE pointer) { VALUE *args = (VALUE *)pointer; return rb_funcall3(args[0], id_start_document, 3, args + 1); } static VALUE protected_end_document(VALUE pointer) { VALUE *args = (VALUE *)pointer; return rb_funcall(args[0], id_end_document, 1, args[1]); } static VALUE protected_alias(VALUE pointer) { VALUE *args = (VALUE *)pointer; return rb_funcall(args[0], id_alias, 1, args[1]); } static VALUE protected_scalar(VALUE pointer) { VALUE *args = (VALUE *)pointer; return rb_funcall3(args[0], id_scalar, 6, args + 1); } static VALUE protected_start_sequence(VALUE pointer) { VALUE *args = (VALUE *)pointer; return rb_funcall3(args[0], id_start_sequence, 4, args + 1); } static VALUE protected_end_sequence(VALUE handler) { return rb_funcall(handler, id_end_sequence, 0); } static VALUE protected_start_mapping(VALUE pointer) { VALUE *args = (VALUE *)pointer; return rb_funcall3(args[0], id_start_mapping, 4, args + 1); } static VALUE protected_end_mapping(VALUE handler) { return rb_funcall(handler, id_end_mapping, 0); } static VALUE protected_empty(VALUE handler) { return rb_funcall(handler, id_empty, 0); } static VALUE protected_end_stream(VALUE handler) { return rb_funcall(handler, id_end_stream, 0); } static VALUE protected_event_location(VALUE pointer) { VALUE *args = (VALUE *)pointer; return rb_funcall3(args[0], id_event_location, 4, args + 1); } static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) { yaml_parser_t * parser; yaml_event_t event; int done = 0; int state = 0; int parser_encoding = YAML_ANY_ENCODING; int encoding = rb_utf8_encindex(); rb_encoding * internal_enc = rb_default_internal_encoding(); TypedData_Get_Struct(self, yaml_parser_t, &psych_parser_type, parser); yaml_parser_delete(parser); yaml_parser_initialize(parser); if (rb_respond_to(yaml, id_read)) { yaml = transcode_io(yaml, &parser_encoding); yaml_parser_set_encoding(parser, parser_encoding); yaml_parser_set_input(parser, io_reader, (void *)yaml); } else { StringValue(yaml); yaml = transcode_string(yaml, &parser_encoding); yaml_parser_set_encoding(parser, parser_encoding); yaml_parser_set_input_string( parser, (const unsigned char *)RSTRING_PTR(yaml), (size_t)RSTRING_LEN(yaml) ); } while(!done) { VALUE event_args[5]; VALUE start_line, start_column, end_line, end_column; if(parser->error || !yaml_parser_parse(parser, &event)) { VALUE exception; exception = make_exception(parser, path); yaml_parser_delete(parser); yaml_parser_initialize(parser); rb_exc_raise(exception); } start_line = SIZET2NUM(event.start_mark.line); start_column = SIZET2NUM(event.start_mark.column); end_line = SIZET2NUM(event.end_mark.line); end_column = SIZET2NUM(event.end_mark.column); event_args[0] = handler; event_args[1] = start_line; event_args[2] = start_column; event_args[3] = end_line; event_args[4] = end_column; rb_protect(protected_event_location, (VALUE)event_args, &state); switch(event.type) { case YAML_STREAM_START_EVENT: { VALUE args[2]; args[0] = handler; args[1] = INT2NUM(event.data.stream_start.encoding); rb_protect(protected_start_stream, (VALUE)args, &state); } break; case YAML_DOCUMENT_START_EVENT: { VALUE args[4]; /* Get a list of tag directives (if any) */ VALUE tag_directives = rb_ary_new(); /* Grab the document version */ VALUE version = event.data.document_start.version_directive ? rb_ary_new3( (long)2, INT2NUM(event.data.document_start.version_directive->major), INT2NUM(event.data.document_start.version_directive->minor) ) : rb_ary_new(); if(event.data.document_start.tag_directives.start) { yaml_tag_directive_t *start = event.data.document_start.tag_directives.start; yaml_tag_directive_t *end = event.data.document_start.tag_directives.end; for(; start != end; start++) { VALUE handle = Qnil; VALUE prefix = Qnil; if(start->handle) { handle = rb_str_new2((const char *)start->handle); PSYCH_TRANSCODE(handle, encoding, internal_enc); } if(start->prefix) { prefix = rb_str_new2((const char *)start->prefix); PSYCH_TRANSCODE(prefix, encoding, internal_enc); } rb_ary_push(tag_directives, rb_ary_new3((long)2, handle, prefix)); } } args[0] = handler; args[1] = version; args[2] = tag_directives; args[3] = event.data.document_start.implicit == 1 ? Qtrue : Qfalse; rb_protect(protected_start_document, (VALUE)args, &state); } break; case YAML_DOCUMENT_END_EVENT: { VALUE args[2]; args[0] = handler; args[1] = event.data.document_end.implicit == 1 ? Qtrue : Qfalse; rb_protect(protected_end_document, (VALUE)args, &state); } break; case YAML_ALIAS_EVENT: { VALUE args[2]; VALUE alias = Qnil; if(event.data.alias.anchor) { alias = rb_str_new2((const char *)event.data.alias.anchor); PSYCH_TRANSCODE(alias, encoding, internal_enc); } args[0] = handler; args[1] = alias; rb_protect(protected_alias, (VALUE)args, &state); } break; case YAML_SCALAR_EVENT: { VALUE args[7]; VALUE anchor = Qnil; VALUE tag = Qnil; VALUE plain_implicit, quoted_implicit, style; VALUE val = rb_str_new( (const char *)event.data.scalar.value, (long)event.data.scalar.length ); PSYCH_TRANSCODE(val, encoding, internal_enc); if(event.data.scalar.anchor) { anchor = rb_str_new2((const char *)event.data.scalar.anchor); PSYCH_TRANSCODE(anchor, encoding, internal_enc); } if(event.data.scalar.tag) { tag = rb_str_new2((const char *)event.data.scalar.tag); PSYCH_TRANSCODE(tag, encoding, internal_enc); } plain_implicit = event.data.scalar.plain_implicit == 0 ? Qfalse : Qtrue; quoted_implicit = event.data.scalar.quoted_implicit == 0 ? Qfalse : Qtrue; style = INT2NUM(event.data.scalar.style); args[0] = handler; args[1] = val; args[2] = anchor; args[3] = tag; args[4] = plain_implicit; args[5] = quoted_implicit; args[6] = style; rb_protect(protected_scalar, (VALUE)args, &state); } break; case YAML_SEQUENCE_START_EVENT: { VALUE args[5]; VALUE anchor = Qnil; VALUE tag = Qnil; VALUE implicit, style; if(event.data.sequence_start.anchor) { anchor = rb_str_new2((const char *)event.data.sequence_start.anchor); PSYCH_TRANSCODE(anchor, encoding, internal_enc); } tag = Qnil; if(event.data.sequence_start.tag) { tag = rb_str_new2((const char *)event.data.sequence_start.tag); PSYCH_TRANSCODE(tag, encoding, internal_enc); } implicit = event.data.sequence_start.implicit == 0 ? Qfalse : Qtrue; style = INT2NUM(event.data.sequence_start.style); args[0] = handler; args[1] = anchor; args[2] = tag; args[3] = implicit; args[4] = style; rb_protect(protected_start_sequence, (VALUE)args, &state); } break; case YAML_SEQUENCE_END_EVENT: rb_protect(protected_end_sequence, handler, &state); break; case YAML_MAPPING_START_EVENT: { VALUE args[5]; VALUE anchor = Qnil; VALUE tag = Qnil; VALUE implicit, style; if(event.data.mapping_start.anchor) { anchor = rb_str_new2((const char *)event.data.mapping_start.anchor); PSYCH_TRANSCODE(anchor, encoding, internal_enc); } if(event.data.mapping_start.tag) { tag = rb_str_new2((const char *)event.data.mapping_start.tag); PSYCH_TRANSCODE(tag, encoding, internal_enc); } implicit = event.data.mapping_start.implicit == 0 ? Qfalse : Qtrue; style = INT2NUM(event.data.mapping_start.style); args[0] = handler; args[1] = anchor; args[2] = tag; args[3] = implicit; args[4] = style; rb_protect(protected_start_mapping, (VALUE)args, &state); } break; case YAML_MAPPING_END_EVENT: rb_protect(protected_end_mapping, handler, &state); break; case YAML_NO_EVENT: rb_protect(protected_empty, handler, &state); break; case YAML_STREAM_END_EVENT: rb_protect(protected_end_stream, handler, &state); done = 1; break; } yaml_event_delete(&event); if (state) rb_jump_tag(state); } return self; } /* * call-seq: * parser.mark # => # * * Returns a Psych::Parser::Mark object that contains line, column, and index * information. */ static VALUE mark(VALUE self) { VALUE mark_klass; VALUE args[3]; yaml_parser_t * parser; TypedData_Get_Struct(self, yaml_parser_t, &psych_parser_type, parser); mark_klass = rb_const_get_at(cPsychParser, rb_intern("Mark")); args[0] = SIZET2NUM(parser->mark.index); args[1] = SIZET2NUM(parser->mark.line); args[2] = SIZET2NUM(parser->mark.column); return rb_class_new_instance(3, args, mark_klass); } void Init_psych_parser(void) { #undef rb_intern #if 0 mPsych = rb_define_module("Psych"); #endif cPsychParser = rb_define_class_under(mPsych, "Parser", rb_cObject); rb_define_alloc_func(cPsychParser, allocate); /* Any encoding: Let the parser choose the encoding */ rb_define_const(cPsychParser, "ANY", INT2NUM(YAML_ANY_ENCODING)); /* UTF-8 Encoding */ rb_define_const(cPsychParser, "UTF8", INT2NUM(YAML_UTF8_ENCODING)); /* UTF-16-LE Encoding with BOM */ rb_define_const(cPsychParser, "UTF16LE", INT2NUM(YAML_UTF16LE_ENCODING)); /* UTF-16-BE Encoding with BOM */ rb_define_const(cPsychParser, "UTF16BE", INT2NUM(YAML_UTF16BE_ENCODING)); rb_require("psych/syntax_error"); rb_define_private_method(cPsychParser, "_native_parse", parse, 3); rb_define_method(cPsychParser, "mark", mark, 0); id_read = rb_intern("read"); id_path = rb_intern("path"); id_empty = rb_intern("empty"); id_start_stream = rb_intern("start_stream"); id_end_stream = rb_intern("end_stream"); id_start_document = rb_intern("start_document"); id_end_document = rb_intern("end_document"); id_alias = rb_intern("alias"); id_scalar = rb_intern("scalar"); id_start_sequence = rb_intern("start_sequence"); id_end_sequence = rb_intern("end_sequence"); id_start_mapping = rb_intern("start_mapping"); id_end_mapping = rb_intern("end_mapping"); id_event_location = rb_intern("event_location"); } /* vim: set noet sws=4 sw=4: */ psych-5.0.2/ext/psych/psych_parser.h000066400000000000000000000001251436167465700174660ustar00rootroot00000000000000#ifndef PSYCH_PARSER_H #define PSYCH_PARSER_H void Init_psych_parser(void); #endif psych-5.0.2/ext/psych/psych_to_ruby.c000066400000000000000000000020521436167465700176510ustar00rootroot00000000000000#include VALUE cPsychVisitorsToRuby; /* call-seq: vis.build_exception(klass, message) * * Create an exception with class +klass+ and +message+ */ static VALUE build_exception(VALUE self, VALUE klass, VALUE mesg) { VALUE e = rb_obj_alloc(klass); rb_iv_set(e, "mesg", mesg); return e; } /* call-seq: vis.path2class(path) * * Convert +path+ string to a class */ static VALUE path2class(VALUE self, VALUE path) { return rb_path_to_class(path); } void Init_psych_to_ruby(void) { VALUE psych = rb_define_module("Psych"); VALUE class_loader = rb_define_class_under(psych, "ClassLoader", rb_cObject); VALUE visitors = rb_define_module_under(psych, "Visitors"); VALUE visitor = rb_define_class_under(visitors, "Visitor", rb_cObject); cPsychVisitorsToRuby = rb_define_class_under(visitors, "ToRuby", visitor); rb_define_private_method(cPsychVisitorsToRuby, "build_exception", build_exception, 2); rb_define_private_method(class_loader, "path2class", path2class, 1); } /* vim: set noet sws=4 sw=4: */ psych-5.0.2/ext/psych/psych_to_ruby.h000066400000000000000000000001541436167465700176570ustar00rootroot00000000000000#ifndef PSYCH_TO_RUBY_H #define PSYCH_TO_RUBY_H #include void Init_psych_to_ruby(void); #endif psych-5.0.2/ext/psych/psych_yaml_tree.c000066400000000000000000000006121436167465700201470ustar00rootroot00000000000000#include VALUE cPsychVisitorsYamlTree; void Init_psych_yaml_tree(void) { VALUE psych = rb_define_module("Psych"); VALUE visitors = rb_define_module_under(psych, "Visitors"); VALUE visitor = rb_define_class_under(visitors, "Visitor", rb_cObject); cPsychVisitorsYamlTree = rb_define_class_under(visitors, "YAMLTree", visitor); } /* vim: set noet sws=4 sw=4: */ psych-5.0.2/ext/psych/psych_yaml_tree.h000066400000000000000000000001621436167465700201540ustar00rootroot00000000000000#ifndef PSYCH_YAML_TREE_H #define PSYCH_YAML_TREE_H #include void Init_psych_yaml_tree(void); #endif psych-5.0.2/lib/000077500000000000000000000000001436167465700134355ustar00rootroot00000000000000psych-5.0.2/lib/psych.rb000066400000000000000000000611701436167465700151150ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'psych/versions' case RUBY_ENGINE when 'jruby' require_relative 'psych_jars' if JRuby::Util.respond_to?(:load_ext) JRuby::Util.load_ext('org.jruby.ext.psych.PsychLibrary') else require 'java'; require 'jruby' org.jruby.ext.psych.PsychLibrary.new.load(JRuby.runtime, false) end else require 'psych.so' end require_relative 'psych/nodes' require_relative 'psych/streaming' require_relative 'psych/visitors' require_relative 'psych/handler' require_relative 'psych/tree_builder' require_relative 'psych/parser' require_relative 'psych/omap' require_relative 'psych/set' require_relative 'psych/coder' require_relative 'psych/core_ext' require_relative 'psych/stream' require_relative 'psych/json/tree_builder' require_relative 'psych/json/stream' require_relative 'psych/handlers/document_stream' require_relative 'psych/class_loader' ### # = Overview # # Psych is a YAML parser and emitter. # Psych leverages libyaml [Home page: https://pyyaml.org/wiki/LibYAML] # or [git repo: https://github.com/yaml/libyaml] for its YAML parsing # and emitting capabilities. In addition to wrapping libyaml, Psych also # knows how to serialize and de-serialize most Ruby objects to and from # the YAML format. # # = I NEED TO PARSE OR EMIT YAML RIGHT NOW! # # # Parse some YAML # Psych.load("--- foo") # => "foo" # # # Emit some YAML # Psych.dump("foo") # => "--- foo\n...\n" # { :a => 'b'}.to_yaml # => "---\n:a: b\n" # # Got more time on your hands? Keep on reading! # # == YAML Parsing # # Psych provides a range of interfaces for parsing a YAML document ranging from # low level to high level, depending on your parsing needs. At the lowest # level, is an event based parser. Mid level is access to the raw YAML AST, # and at the highest level is the ability to unmarshal YAML to Ruby objects. # # == YAML Emitting # # Psych provides a range of interfaces ranging from low to high level for # producing YAML documents. Very similar to the YAML parsing interfaces, Psych # provides at the lowest level, an event based system, mid-level is building # a YAML AST, and the highest level is converting a Ruby object straight to # a YAML document. # # == High-level API # # === Parsing # # The high level YAML parser provided by Psych simply takes YAML as input and # returns a Ruby data structure. For information on using the high level parser # see Psych.load # # ==== Reading from a string # # Psych.safe_load("--- a") # => 'a' # Psych.safe_load("---\n - a\n - b") # => ['a', 'b'] # # From a trusted string: # Psych.load("--- !ruby/range\nbegin: 0\nend: 42\nexcl: false\n") # => 0..42 # # ==== Reading from a file # # Psych.safe_load_file("data.yml", permitted_classes: [Date]) # Psych.load_file("trusted_database.yml") # # ==== Exception handling # # begin # # The second argument changes only the exception contents # Psych.parse("--- `", "file.txt") # rescue Psych::SyntaxError => ex # ex.file # => 'file.txt' # ex.message # => "(file.txt): found character that cannot start any token" # end # # === Emitting # # The high level emitter has the easiest interface. Psych simply takes a Ruby # data structure and converts it to a YAML document. See Psych.dump for more # information on dumping a Ruby data structure. # # ==== Writing to a string # # # Dump an array, get back a YAML string # Psych.dump(['a', 'b']) # => "---\n- a\n- b\n" # # # Dump an array to an IO object # Psych.dump(['a', 'b'], StringIO.new) # => # # # # Dump an array with indentation set # Psych.dump(['a', ['b']], :indentation => 3) # => "---\n- a\n- - b\n" # # # Dump an array to an IO with indentation set # Psych.dump(['a', ['b']], StringIO.new, :indentation => 3) # # ==== Writing to a file # # Currently there is no direct API for dumping Ruby structure to file: # # File.open('database.yml', 'w') do |file| # file.write(Psych.dump(['a', 'b'])) # end # # == Mid-level API # # === Parsing # # Psych provides access to an AST produced from parsing a YAML document. This # tree is built using the Psych::Parser and Psych::TreeBuilder. The AST can # be examined and manipulated freely. Please see Psych::parse_stream, # Psych::Nodes, and Psych::Nodes::Node for more information on dealing with # YAML syntax trees. # # ==== Reading from a string # # # Returns Psych::Nodes::Stream # Psych.parse_stream("---\n - a\n - b") # # # Returns Psych::Nodes::Document # Psych.parse("---\n - a\n - b") # # ==== Reading from a file # # # Returns Psych::Nodes::Stream # Psych.parse_stream(File.read('database.yml')) # # # Returns Psych::Nodes::Document # Psych.parse_file('database.yml') # # ==== Exception handling # # begin # # The second argument changes only the exception contents # Psych.parse("--- `", "file.txt") # rescue Psych::SyntaxError => ex # ex.file # => 'file.txt' # ex.message # => "(file.txt): found character that cannot start any token" # end # # === Emitting # # At the mid level is building an AST. This AST is exactly the same as the AST # used when parsing a YAML document. Users can build an AST by hand and the # AST knows how to emit itself as a YAML document. See Psych::Nodes, # Psych::Nodes::Node, and Psych::TreeBuilder for more information on building # a YAML AST. # # ==== Writing to a string # # # We need Psych::Nodes::Stream (not Psych::Nodes::Document) # stream = Psych.parse_stream("---\n - a\n - b") # # stream.to_yaml # => "---\n- a\n- b\n" # # ==== Writing to a file # # # We need Psych::Nodes::Stream (not Psych::Nodes::Document) # stream = Psych.parse_stream(File.read('database.yml')) # # File.open('database.yml', 'w') do |file| # file.write(stream.to_yaml) # end # # == Low-level API # # === Parsing # # The lowest level parser should be used when the YAML input is already known, # and the developer does not want to pay the price of building an AST or # automatic detection and conversion to Ruby objects. See Psych::Parser for # more information on using the event based parser. # # ==== Reading to Psych::Nodes::Stream structure # # parser = Psych::Parser.new(TreeBuilder.new) # => # # parser = Psych.parser # it's an alias for the above # # parser.parse("---\n - a\n - b") # => # # parser.handler # => # # parser.handler.root # => # # # ==== Receiving an events stream # # recorder = Psych::Handlers::Recorder.new # parser = Psych::Parser.new(recorder) # # parser.parse("---\n - a\n - b") # recorder.events # => [list of [event, args] lists] # # event is one of: Psych::Handler::EVENTS # # args are the arguments passed to the event # # === Emitting # # The lowest level emitter is an event based system. Events are sent to a # Psych::Emitter object. That object knows how to convert the events to a YAML # document. This interface should be used when document format is known in # advance or speed is a concern. See Psych::Emitter for more information. # # ==== Writing to a Ruby structure # # Psych.parser.parse("--- a") # => # # # parser.handler.first # => # # parser.handler.first.to_ruby # => ["a"] # # parser.handler.root.first # => # # parser.handler.root.first.to_ruby # => "a" # # # You can instantiate an Emitter manually # Psych::Visitors::ToRuby.new.accept(parser.handler.root.first) # # => "a" module Psych # The version of libyaml Psych is using LIBYAML_VERSION = Psych.libyaml_version.join('.').freeze ### # Load +yaml+ in to a Ruby data structure. If multiple documents are # provided, the object contained in the first document will be returned. # +filename+ will be used in the exception message if any exception # is raised while parsing. If +yaml+ is empty, it returns # the specified +fallback+ return value, which defaults to +false+. # # Raises a Psych::SyntaxError when a YAML syntax error is detected. # # Example: # # Psych.unsafe_load("--- a") # => 'a' # Psych.unsafe_load("---\n - a\n - b") # => ['a', 'b'] # # begin # Psych.unsafe_load("--- `", filename: "file.txt") # rescue Psych::SyntaxError => ex # ex.file # => 'file.txt' # ex.message # => "(file.txt): found character that cannot start any token" # end # # When the optional +symbolize_names+ keyword argument is set to a # true value, returns symbols for keys in Hash objects (default: strings). # # Psych.unsafe_load("---\n foo: bar") # => {"foo"=>"bar"} # Psych.unsafe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"} # # Raises a TypeError when `yaml` parameter is NilClass # # NOTE: This method *should not* be used to parse untrusted documents, such as # YAML documents that are supplied via user input. Instead, please use the # load method or the safe_load method. # def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false result = parse(yaml, filename: filename) return fallback unless result result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer) end ### # Safely load the yaml string in +yaml+. By default, only the following # classes are allowed to be deserialized: # # * TrueClass # * FalseClass # * NilClass # * Integer # * Float # * String # * Array # * Hash # # Recursive data structures are not allowed by default. Arbitrary classes # can be allowed by adding those classes to the +permitted_classes+ keyword argument. They are # additive. For example, to allow Date deserialization: # # Psych.safe_load(yaml, permitted_classes: [Date]) # # Now the Date class can be loaded in addition to the classes listed above. # # Aliases can be explicitly allowed by changing the +aliases+ keyword argument. # For example: # # x = [] # x << x # yaml = Psych.dump x # Psych.safe_load yaml # => raises an exception # Psych.safe_load yaml, aliases: true # => loads the aliases # # A Psych::DisallowedClass exception will be raised if the yaml contains a # class that isn't in the +permitted_classes+ list. # # A Psych::AliasesNotEnabled exception will be raised if the yaml contains aliases # but the +aliases+ keyword argument is set to false. # # +filename+ will be used in the exception message if any exception is raised # while parsing. # # When the optional +symbolize_names+ keyword argument is set to a # true value, returns symbols for keys in Hash objects (default: strings). # # Psych.safe_load("---\n foo: bar") # => {"foo"=>"bar"} # Psych.safe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"} # def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false result = parse(yaml, filename: filename) return fallback unless result class_loader = ClassLoader::Restricted.new(permitted_classes.map(&:to_s), permitted_symbols.map(&:to_s)) scanner = ScalarScanner.new class_loader, strict_integer: strict_integer visitor = if aliases Visitors::ToRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze else Visitors::NoAliasRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze end result = visitor.accept result result end ### # Load +yaml+ in to a Ruby data structure. If multiple documents are # provided, the object contained in the first document will be returned. # +filename+ will be used in the exception message if any exception # is raised while parsing. If +yaml+ is empty, it returns # the specified +fallback+ return value, which defaults to +false+. # # Raises a Psych::SyntaxError when a YAML syntax error is detected. # # Example: # # Psych.load("--- a") # => 'a' # Psych.load("---\n - a\n - b") # => ['a', 'b'] # # begin # Psych.load("--- `", filename: "file.txt") # rescue Psych::SyntaxError => ex # ex.file # => 'file.txt' # ex.message # => "(file.txt): found character that cannot start any token" # end # # When the optional +symbolize_names+ keyword argument is set to a # true value, returns symbols for keys in Hash objects (default: strings). # # Psych.load("---\n foo: bar") # => {"foo"=>"bar"} # Psych.load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"} # # Raises a TypeError when `yaml` parameter is NilClass. This method is # similar to `safe_load` except that `Symbol` objects are allowed by default. # def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false safe_load yaml, permitted_classes: permitted_classes, permitted_symbols: permitted_symbols, aliases: aliases, filename: filename, fallback: fallback, symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer end ### # Parse a YAML string in +yaml+. Returns the Psych::Nodes::Document. # +filename+ is used in the exception message if a Psych::SyntaxError is # raised. # # Raises a Psych::SyntaxError when a YAML syntax error is detected. # # Example: # # Psych.parse("---\n - a\n - b") # => # # # begin # Psych.parse("--- `", filename: "file.txt") # rescue Psych::SyntaxError => ex # ex.file # => 'file.txt' # ex.message # => "(file.txt): found character that cannot start any token" # end # # See Psych::Nodes for more information about YAML AST. def self.parse yaml, filename: nil parse_stream(yaml, filename: filename) do |node| return node end false end ### # Parse a file at +filename+. Returns the Psych::Nodes::Document. # # Raises a Psych::SyntaxError when a YAML syntax error is detected. def self.parse_file filename, fallback: false result = File.open filename, 'r:bom|utf-8' do |f| parse f, filename: filename end result || fallback end ### # Returns a default parser def self.parser Psych::Parser.new(TreeBuilder.new) end ### # Parse a YAML string in +yaml+. Returns the Psych::Nodes::Stream. # This method can handle multiple YAML documents contained in +yaml+. # +filename+ is used in the exception message if a Psych::SyntaxError is # raised. # # If a block is given, a Psych::Nodes::Document node will be yielded to the # block as it's being parsed. # # Raises a Psych::SyntaxError when a YAML syntax error is detected. # # Example: # # Psych.parse_stream("---\n - a\n - b") # => # # # Psych.parse_stream("--- a\n--- b") do |node| # node # => # # end # # begin # Psych.parse_stream("--- `", filename: "file.txt") # rescue Psych::SyntaxError => ex # ex.file # => 'file.txt' # ex.message # => "(file.txt): found character that cannot start any token" # end # # Raises a TypeError when NilClass is passed. # # See Psych::Nodes for more information about YAML AST. def self.parse_stream yaml, filename: nil, &block if block_given? parser = Psych::Parser.new(Handlers::DocumentStream.new(&block)) parser.parse yaml, filename else parser = self.parser parser.parse yaml, filename parser.handler.root end end ### # call-seq: # Psych.dump(o) -> string of yaml # Psych.dump(o, options) -> string of yaml # Psych.dump(o, io) -> io object passed in # Psych.dump(o, io, options) -> io object passed in # # Dump Ruby object +o+ to a YAML string. Optional +options+ may be passed in # to control the output format. If an IO object is passed in, the YAML will # be dumped to that IO object. # # Currently supported options are: # # [:indentation] Number of space characters used to indent. # Acceptable value should be in 0..9 range, # otherwise option is ignored. # # Default: 2. # [:line_width] Max character to wrap line at. # # Default: 0 (meaning "wrap at 81"). # [:canonical] Write "canonical" YAML form (very verbose, yet # strictly formal). # # Default: false. # [:header] Write %YAML [version] at the beginning of document. # # Default: false. # # Example: # # # Dump an array, get back a YAML string # Psych.dump(['a', 'b']) # => "---\n- a\n- b\n" # # # Dump an array to an IO object # Psych.dump(['a', 'b'], StringIO.new) # => # # # # Dump an array with indentation set # Psych.dump(['a', ['b']], indentation: 3) # => "---\n- a\n- - b\n" # # # Dump an array to an IO with indentation set # Psych.dump(['a', ['b']], StringIO.new, indentation: 3) def self.dump o, io = nil, options = {} if Hash === io options = io io = nil end visitor = Psych::Visitors::YAMLTree.create options visitor << o visitor.tree.yaml io, options end ### # call-seq: # Psych.safe_dump(o) -> string of yaml # Psych.safe_dump(o, options) -> string of yaml # Psych.safe_dump(o, io) -> io object passed in # Psych.safe_dump(o, io, options) -> io object passed in # # Safely dump Ruby object +o+ to a YAML string. Optional +options+ may be passed in # to control the output format. If an IO object is passed in, the YAML will # be dumped to that IO object. By default, only the following # classes are allowed to be serialized: # # * TrueClass # * FalseClass # * NilClass # * Integer # * Float # * String # * Array # * Hash # # Arbitrary classes can be allowed by adding those classes to the +permitted_classes+ # keyword argument. They are additive. For example, to allow Date serialization: # # Psych.safe_dump(yaml, permitted_classes: [Date]) # # Now the Date class can be dumped in addition to the classes listed above. # # A Psych::DisallowedClass exception will be raised if the object contains a # class that isn't in the +permitted_classes+ list. # # Currently supported options are: # # [:indentation] Number of space characters used to indent. # Acceptable value should be in 0..9 range, # otherwise option is ignored. # # Default: 2. # [:line_width] Max character to wrap line at. # # Default: 0 (meaning "wrap at 81"). # [:canonical] Write "canonical" YAML form (very verbose, yet # strictly formal). # # Default: false. # [:header] Write %YAML [version] at the beginning of document. # # Default: false. # # Example: # # # Dump an array, get back a YAML string # Psych.safe_dump(['a', 'b']) # => "---\n- a\n- b\n" # # # Dump an array to an IO object # Psych.safe_dump(['a', 'b'], StringIO.new) # => # # # # Dump an array with indentation set # Psych.safe_dump(['a', ['b']], indentation: 3) # => "---\n- a\n- - b\n" # # # Dump an array to an IO with indentation set # Psych.safe_dump(['a', ['b']], StringIO.new, indentation: 3) def self.safe_dump o, io = nil, options = {} if Hash === io options = io io = nil end visitor = Psych::Visitors::RestrictedYAMLTree.create options visitor << o visitor.tree.yaml io, options end ### # Dump a list of objects as separate documents to a document stream. # # Example: # # Psych.dump_stream("foo\n ", {}) # => "--- ! \"foo\\n \"\n--- {}\n" def self.dump_stream *objects visitor = Psych::Visitors::YAMLTree.create({}) objects.each do |o| visitor << o end visitor.tree.yaml end ### # Dump Ruby +object+ to a JSON string. def self.to_json object visitor = Psych::Visitors::JSONTree.create visitor << object visitor.tree.yaml end ### # Load multiple documents given in +yaml+. Returns the parsed documents # as a list. If a block is given, each document will be converted to Ruby # and passed to the block during parsing # # Example: # # Psych.load_stream("--- foo\n...\n--- bar\n...") # => ['foo', 'bar'] # # list = [] # Psych.load_stream("--- foo\n...\n--- bar\n...") do |ruby| # list << ruby # end # list # => ['foo', 'bar'] # def self.load_stream yaml, filename: nil, fallback: [], **kwargs result = if block_given? parse_stream(yaml, filename: filename) do |node| yield node.to_ruby(**kwargs) end else parse_stream(yaml, filename: filename).children.map { |node| node.to_ruby(**kwargs) } end return fallback if result.is_a?(Array) && result.empty? result end ### # Load the document contained in +filename+. Returns the yaml contained in # +filename+ as a Ruby object, or if the file is empty, it returns # the specified +fallback+ return value, which defaults to +false+. # # NOTE: This method *should not* be used to parse untrusted documents, such as # YAML documents that are supplied via user input. Instead, please use the # safe_load_file method. def self.unsafe_load_file filename, **kwargs File.open(filename, 'r:bom|utf-8') { |f| self.unsafe_load f, filename: filename, **kwargs } end ### # Safely loads the document contained in +filename+. Returns the yaml contained in # +filename+ as a Ruby object, or if the file is empty, it returns # the specified +fallback+ return value, which defaults to +false+. # See safe_load for options. def self.safe_load_file filename, **kwargs File.open(filename, 'r:bom|utf-8') { |f| self.safe_load f, filename: filename, **kwargs } end ### # Loads the document contained in +filename+. Returns the yaml contained in # +filename+ as a Ruby object, or if the file is empty, it returns # the specified +fallback+ return value, which defaults to +false+. # See load for options. def self.load_file filename, **kwargs File.open(filename, 'r:bom|utf-8') { |f| self.load f, filename: filename, **kwargs } end # :stopdoc: def self.add_domain_type domain, type_tag, &block key = ['tag', domain, type_tag].join ':' domain_types[key] = [key, block] domain_types["tag:#{type_tag}"] = [key, block] end def self.add_builtin_type type_tag, &block domain = 'yaml.org,2002' key = ['tag', domain, type_tag].join ':' domain_types[key] = [key, block] end def self.remove_type type_tag domain_types.delete type_tag end def self.add_tag tag, klass load_tags[tag] = klass.name dump_tags[klass] = tag end # Workaround for emulating `warn '...', uplevel: 1` in Ruby 2.4 or lower. def self.warn_with_uplevel(message, uplevel: 1) at = parse_caller(caller[uplevel]).join(':') warn "#{at}: #{message}" end def self.parse_caller(at) if /^(.+?):(\d+)(?::in `.*')?/ =~ at file = $1 line = $2.to_i [file, line] end end private_class_method :warn_with_uplevel, :parse_caller class << self if defined?(Ractor) require 'forwardable' extend Forwardable class Config attr_accessor :load_tags, :dump_tags, :domain_types def initialize @load_tags = {} @dump_tags = {} @domain_types = {} end end def config Ractor.current[:PsychConfig] ||= Config.new end def_delegators :config, :load_tags, :dump_tags, :domain_types, :load_tags=, :dump_tags=, :domain_types= else attr_accessor :load_tags attr_accessor :dump_tags attr_accessor :domain_types end end self.load_tags = {} self.dump_tags = {} self.domain_types = {} # :startdoc: end psych-5.0.2/lib/psych/000077500000000000000000000000001436167465700145635ustar00rootroot00000000000000psych-5.0.2/lib/psych/class_loader.rb000066400000000000000000000040121436167465700175400ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'omap' require_relative 'set' module Psych class ClassLoader # :nodoc: BIG_DECIMAL = 'BigDecimal' COMPLEX = 'Complex' DATE = 'Date' DATE_TIME = 'DateTime' EXCEPTION = 'Exception' OBJECT = 'Object' PSYCH_OMAP = 'Psych::Omap' PSYCH_SET = 'Psych::Set' RANGE = 'Range' RATIONAL = 'Rational' REGEXP = 'Regexp' STRUCT = 'Struct' SYMBOL = 'Symbol' def initialize @cache = CACHE.dup end def load klassname return nil if !klassname || klassname.empty? find klassname end def symbolize sym symbol sym.to_sym end constants.each do |const| konst = const_get const class_eval <<~RUBY, __FILE__, __LINE__ + 1 def #{const.to_s.downcase} load #{konst.inspect} end RUBY end private def find klassname @cache[klassname] ||= resolve(klassname) end def resolve klassname name = klassname retried = false begin path2class(name) rescue ArgumentError, NameError => ex unless retried name = "Struct::#{name}" retried = ex retry end raise retried end end CACHE = Hash[constants.map { |const| val = const_get const begin [val, ::Object.const_get(val)] rescue nil end }.compact].freeze class Restricted < ClassLoader def initialize classes, symbols @classes = classes @symbols = symbols super() end def symbolize sym return super if @symbols.empty? if @symbols.include? sym super else raise DisallowedClass.new('load', 'Symbol') end end private def find klassname if @classes.include? klassname super else raise DisallowedClass.new('load', klassname) end end end end end psych-5.0.2/lib/psych/coder.rb000066400000000000000000000040621436167465700162060ustar00rootroot00000000000000# frozen_string_literal: true module Psych ### # If an object defines +encode_with+, then an instance of Psych::Coder will # be passed to the method when the object is being serialized. The Coder # automatically assumes a Psych::Nodes::Mapping is being emitted. Other # objects like Sequence and Scalar may be emitted if +seq=+ or +scalar=+ are # called, respectively. class Coder attr_accessor :tag, :style, :implicit, :object attr_reader :type, :seq def initialize tag @map = {} @seq = [] @implicit = false @type = :map @tag = tag @style = Psych::Nodes::Mapping::BLOCK @scalar = nil @object = nil end def scalar *args if args.length > 0 warn "#{caller[0]}: Coder#scalar(a,b,c) is deprecated" if $VERBOSE @tag, @scalar, _ = args @type = :scalar end @scalar end # Emit a map. The coder will be yielded to the block. def map tag = @tag, style = @style @tag = tag @style = style yield self if block_given? @map end # Emit a scalar with +value+ and +tag+ def represent_scalar tag, value self.tag = tag self.scalar = value end # Emit a sequence with +list+ and +tag+ def represent_seq tag, list @tag = tag self.seq = list end # Emit a sequence with +map+ and +tag+ def represent_map tag, map @tag = tag self.map = map end # Emit an arbitrary object +obj+ and +tag+ def represent_object tag, obj @tag = tag @type = :object @object = obj end # Emit a scalar with +value+ def scalar= value @type = :scalar @scalar = value end # Emit a map with +value+ def map= map @type = :map @map = map end def []= k, v @type = :map @map[k] = v end alias :add :[]= def [] k @type = :map @map[k] end # Emit a sequence of +list+ def seq= list @type = :seq @seq = list end end end psych-5.0.2/lib/psych/core_ext.rb000066400000000000000000000005521436167465700167220ustar00rootroot00000000000000# frozen_string_literal: true class Object def self.yaml_tag url Psych.add_tag(url, self) end ### # call-seq: to_yaml(options = {}) # # Convert an object to YAML. See Psych.dump for more information on the # available +options+. def to_yaml options = {} Psych.dump self, options end end if defined?(::IRB) require_relative 'y' end psych-5.0.2/lib/psych/exception.rb000066400000000000000000000013261436167465700171100ustar00rootroot00000000000000# frozen_string_literal: true module Psych class Exception < RuntimeError end class BadAlias < Exception end # Subclasses `BadAlias` for backwards compatibility class AliasesNotEnabled < BadAlias def initialize super "Alias parsing was not enabled. To enable it, pass `aliases: true` to `Psych::load` or `Psych::safe_load`." end end # Subclasses `BadAlias` for backwards compatibility class AnchorNotDefined < BadAlias def initialize anchor_name super "An alias referenced an unknown anchor: #{anchor_name}" end end class DisallowedClass < Exception def initialize action, klass_name super "Tried to #{action} unspecified class: #{klass_name}" end end end psych-5.0.2/lib/psych/handler.rb000066400000000000000000000163111436167465700165270ustar00rootroot00000000000000# frozen_string_literal: true module Psych ### # Psych::Handler is an abstract base class that defines the events used # when dealing with Psych::Parser. Clients who want to use Psych::Parser # should implement a class that inherits from Psych::Handler and define # events that they can handle. # # Psych::Handler defines all events that Psych::Parser can possibly send to # event handlers. # # See Psych::Parser for more details class Handler ### # Configuration options for dumping YAML. class DumperOptions attr_accessor :line_width, :indentation, :canonical def initialize @line_width = 0 @indentation = 2 @canonical = false end end # Default dumping options OPTIONS = DumperOptions.new # Events that a Handler should respond to. EVENTS = [ :alias, :empty, :end_document, :end_mapping, :end_sequence, :end_stream, :scalar, :start_document, :start_mapping, :start_sequence, :start_stream ] ### # Called with +encoding+ when the YAML stream starts. This method is # called once per stream. A stream may contain multiple documents. # # See the constants in Psych::Parser for the possible values of +encoding+. def start_stream encoding end ### # Called when the document starts with the declared +version+, # +tag_directives+, if the document is +implicit+. # # +version+ will be an array of integers indicating the YAML version being # dealt with, +tag_directives+ is a list of tuples indicating the prefix # and suffix of each tag, and +implicit+ is a boolean indicating whether # the document is started implicitly. # # === Example # # Given the following YAML: # # %YAML 1.1 # %TAG ! tag:tenderlovemaking.com,2009: # --- !squee # # The parameters for start_document must be this: # # version # => [1, 1] # tag_directives # => [["!", "tag:tenderlovemaking.com,2009:"]] # implicit # => false def start_document version, tag_directives, implicit end ### # Called with the document ends. +implicit+ is a boolean value indicating # whether or not the document has an implicit ending. # # === Example # # Given the following YAML: # # --- # hello world # # +implicit+ will be true. Given this YAML: # # --- # hello world # ... # # +implicit+ will be false. def end_document implicit end ### # Called when an alias is found to +anchor+. +anchor+ will be the name # of the anchor found. # # === Example # # Here we have an example of an array that references itself in YAML: # # --- &ponies # - first element # - *ponies # # &ponies is the anchor, *ponies is the alias. In this case, alias is # called with "ponies". def alias anchor end ### # Called when a scalar +value+ is found. The scalar may have an # +anchor+, a +tag+, be implicitly +plain+ or implicitly +quoted+ # # +value+ is the string value of the scalar # +anchor+ is an associated anchor or nil # +tag+ is an associated tag or nil # +plain+ is a boolean value # +quoted+ is a boolean value # +style+ is an integer indicating the string style # # See the constants in Psych::Nodes::Scalar for the possible values of # +style+ # # === Example # # Here is a YAML document that exercises most of the possible ways this # method can be called: # # --- # - !str "foo" # - &anchor fun # - many # lines # - | # many # newlines # # The above YAML document contains a list with four strings. Here are # the parameters sent to this method in the same order: # # # value anchor tag plain quoted style # ["foo", nil, "!str", false, false, 3 ] # ["fun", "anchor", nil, true, false, 1 ] # ["many lines", nil, nil, true, false, 1 ] # ["many\nnewlines\n", nil, nil, false, true, 4 ] # def scalar value, anchor, tag, plain, quoted, style end ### # Called when a sequence is started. # # +anchor+ is the anchor associated with the sequence or nil. # +tag+ is the tag associated with the sequence or nil. # +implicit+ a boolean indicating whether or not the sequence was implicitly # started. # +style+ is an integer indicating the list style. # # See the constants in Psych::Nodes::Sequence for the possible values of # +style+. # # === Example # # Here is a YAML document that exercises most of the possible ways this # method can be called: # # --- # - !!seq [ # a # ] # - &pewpew # - b # # The above YAML document consists of three lists, an outer list that # contains two inner lists. Here is a matrix of the parameters sent # to represent these lists: # # # anchor tag implicit style # [nil, nil, true, 1 ] # [nil, "tag:yaml.org,2002:seq", false, 2 ] # ["pewpew", nil, true, 1 ] def start_sequence anchor, tag, implicit, style end ### # Called when a sequence ends. def end_sequence end ### # Called when a map starts. # # +anchor+ is the anchor associated with the map or +nil+. # +tag+ is the tag associated with the map or +nil+. # +implicit+ is a boolean indicating whether or not the map was implicitly # started. # +style+ is an integer indicating the mapping style. # # See the constants in Psych::Nodes::Mapping for the possible values of # +style+. # # === Example # # Here is a YAML document that exercises most of the possible ways this # method can be called: # # --- # k: !!map { hello: world } # v: &pewpew # hello: world # # The above YAML document consists of three maps, an outer map that contains # two inner maps. Below is a matrix of the parameters sent in order to # represent these three maps: # # # anchor tag implicit style # [nil, nil, true, 1 ] # [nil, "tag:yaml.org,2002:map", false, 2 ] # ["pewpew", nil, true, 1 ] def start_mapping anchor, tag, implicit, style end ### # Called when a map ends def end_mapping end ### # Called when an empty event happens. (Which, as far as I can tell, is # never). def empty end ### # Called when the YAML stream ends def end_stream end ### # Called before each event with line/column information. def event_location(start_line, start_column, end_line, end_column) end ### # Is this handler a streaming handler? def streaming? false end end end psych-5.0.2/lib/psych/handlers/000077500000000000000000000000001436167465700163635ustar00rootroot00000000000000psych-5.0.2/lib/psych/handlers/document_stream.rb000066400000000000000000000010141436167465700220750ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../tree_builder' module Psych module Handlers class DocumentStream < Psych::TreeBuilder # :nodoc: def initialize &block super @block = block end def start_document version, tag_directives, implicit n = Nodes::Document.new version, tag_directives, implicit push n end def end_document implicit_end = !streaming? @last.implicit_end = implicit_end @block.call pop end end end end psych-5.0.2/lib/psych/handlers/recorder.rb000066400000000000000000000015731436167465700205230ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../handler' module Psych module Handlers ### # This handler will capture an event and record the event. Recorder events # are available vial Psych::Handlers::Recorder#events. # # For example: # # recorder = Psych::Handlers::Recorder.new # parser = Psych::Parser.new recorder # parser.parse '--- foo' # # recorder.events # => [list of events] # # # Replay the events # # emitter = Psych::Emitter.new $stdout # recorder.events.each do |m, args| # emitter.send m, *args # end class Recorder < Psych::Handler attr_reader :events def initialize @events = [] super end EVENTS.each do |event| define_method event do |*args| @events << [event, args] end end end end end psych-5.0.2/lib/psych/json/000077500000000000000000000000001436167465700155345ustar00rootroot00000000000000psych-5.0.2/lib/psych/json/ruby_events.rb000066400000000000000000000007501436167465700204300ustar00rootroot00000000000000# frozen_string_literal: true module Psych module JSON module RubyEvents # :nodoc: def visit_Time o formatted = format_time o @emitter.scalar formatted, nil, nil, false, true, Nodes::Scalar::DOUBLE_QUOTED end def visit_DateTime o visit_Time o.to_time end def visit_String o @emitter.scalar o.to_s, nil, nil, false, true, Nodes::Scalar::DOUBLE_QUOTED end alias :visit_Symbol :visit_String end end end psych-5.0.2/lib/psych/json/stream.rb000066400000000000000000000006221436167465700173540ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'ruby_events' require_relative 'yaml_events' module Psych module JSON class Stream < Psych::Visitors::JSONTree include Psych::JSON::RubyEvents include Psych::Streaming extend Psych::Streaming::ClassMethods class Emitter < Psych::Stream::Emitter # :nodoc: include Psych::JSON::YAMLEvents end end end end psych-5.0.2/lib/psych/json/tree_builder.rb000066400000000000000000000005431436167465700205300ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'yaml_events' module Psych module JSON ### # Psych::JSON::TreeBuilder is an event based AST builder. Events are sent # to an instance of Psych::JSON::TreeBuilder and a JSON AST is constructed. class TreeBuilder < Psych::TreeBuilder include Psych::JSON::YAMLEvents end end end psych-5.0.2/lib/psych/json/yaml_events.rb000066400000000000000000000014131436167465700204060ustar00rootroot00000000000000# frozen_string_literal: true module Psych module JSON module YAMLEvents # :nodoc: def start_document version, tag_directives, implicit super(version, tag_directives, !streaming?) end def end_document implicit_end = !streaming? super(implicit_end) end def start_mapping anchor, tag, implicit, style super(anchor, nil, true, Nodes::Mapping::FLOW) end def start_sequence anchor, tag, implicit, style super(anchor, nil, true, Nodes::Sequence::FLOW) end def scalar value, anchor, tag, plain, quoted, style if "tag:yaml.org,2002:null" == tag super('null', nil, nil, true, false, Nodes::Scalar::PLAIN) else super end end end end end psych-5.0.2/lib/psych/nodes.rb000066400000000000000000000046001436167465700162200ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'nodes/node' require_relative 'nodes/stream' require_relative 'nodes/document' require_relative 'nodes/sequence' require_relative 'nodes/scalar' require_relative 'nodes/mapping' require_relative 'nodes/alias' module Psych ### # = Overview # # When using Psych.load to deserialize a YAML document, the document is # translated to an intermediary AST. That intermediary AST is then # translated in to a Ruby object graph. # # In the opposite direction, when using Psych.dump, the Ruby object graph is # translated to an intermediary AST which is then converted to a YAML # document. # # Psych::Nodes contains all of the classes that make up the nodes of a YAML # AST. You can manually build an AST and use one of the visitors (see # Psych::Visitors) to convert that AST to either a YAML document or to a # Ruby object graph. # # Here is an example of building an AST that represents a list with one # scalar: # # # Create our nodes # stream = Psych::Nodes::Stream.new # doc = Psych::Nodes::Document.new # seq = Psych::Nodes::Sequence.new # scalar = Psych::Nodes::Scalar.new('foo') # # # Build up our tree # stream.children << doc # doc.children << seq # seq.children << scalar # # The stream is the root of the tree. We can then convert the tree to YAML: # # stream.to_yaml => "---\n- foo\n" # # Or convert it to Ruby: # # stream.to_ruby => [["foo"]] # # == YAML AST Requirements # # A valid YAML AST *must* have one Psych::Nodes::Stream at the root. A # Psych::Nodes::Stream node must have 1 or more Psych::Nodes::Document nodes # as children. # # Psych::Nodes::Document nodes must have one and *only* one child. That child # may be one of: # # * Psych::Nodes::Sequence # * Psych::Nodes::Mapping # * Psych::Nodes::Scalar # # Psych::Nodes::Sequence and Psych::Nodes::Mapping nodes may have many # children, but Psych::Nodes::Mapping nodes should have an even number of # children. # # All of these are valid children for Psych::Nodes::Sequence and # Psych::Nodes::Mapping nodes: # # * Psych::Nodes::Sequence # * Psych::Nodes::Mapping # * Psych::Nodes::Scalar # * Psych::Nodes::Alias # # Psych::Nodes::Scalar and Psych::Nodes::Alias are both terminal nodes and # should not have any children. module Nodes end end psych-5.0.2/lib/psych/nodes/000077500000000000000000000000001436167465700156735ustar00rootroot00000000000000psych-5.0.2/lib/psych/nodes/alias.rb000066400000000000000000000010131436167465700173040ustar00rootroot00000000000000# frozen_string_literal: true module Psych module Nodes ### # This class represents a {YAML Alias}[http://yaml.org/spec/1.1/#alias]. # It points to an +anchor+. # # A Psych::Nodes::Alias is a terminal node and may have no children. class Alias < Psych::Nodes::Node # The anchor this alias links to attr_accessor :anchor # Create a new Alias that points to an +anchor+ def initialize anchor @anchor = anchor end def alias?; true; end end end end psych-5.0.2/lib/psych/nodes/document.rb000066400000000000000000000035011436167465700200350ustar00rootroot00000000000000# frozen_string_literal: true module Psych module Nodes ### # This represents a YAML Document. This node must be a child of # Psych::Nodes::Stream. A Psych::Nodes::Document must have one child, # and that child may be one of the following: # # * Psych::Nodes::Sequence # * Psych::Nodes::Mapping # * Psych::Nodes::Scalar class Document < Psych::Nodes::Node # The version of the YAML document attr_accessor :version # A list of tag directives for this document attr_accessor :tag_directives # Was this document implicitly created? attr_accessor :implicit # Is the end of the document implicit? attr_accessor :implicit_end ### # Create a new Psych::Nodes::Document object. # # +version+ is a list indicating the YAML version. # +tags_directives+ is a list of tag directive declarations # +implicit+ is a flag indicating whether the document will be implicitly # started. # # == Example: # This creates a YAML document object that represents a YAML 1.1 document # with one tag directive, and has an implicit start: # # Psych::Nodes::Document.new( # [1,1], # [["!", "tag:tenderlovemaking.com,2009:"]], # true # ) # # == See Also # See also Psych::Handler#start_document def initialize version = [], tag_directives = [], implicit = false super() @version = version @tag_directives = tag_directives @implicit = implicit @implicit_end = true end ### # Returns the root node. A Document may only have one root node: # http://yaml.org/spec/1.1/#id898031 def root children.first end def document?; true; end end end end psych-5.0.2/lib/psych/nodes/mapping.rb000066400000000000000000000030511436167465700176520ustar00rootroot00000000000000# frozen_string_literal: true module Psych module Nodes ### # This class represents a {YAML Mapping}[http://yaml.org/spec/1.1/#mapping]. # # A Psych::Nodes::Mapping node may have 0 or more children, but must have # an even number of children. Here are the valid children a # Psych::Nodes::Mapping node may have: # # * Psych::Nodes::Sequence # * Psych::Nodes::Mapping # * Psych::Nodes::Scalar # * Psych::Nodes::Alias class Mapping < Psych::Nodes::Node # Any Map Style ANY = 0 # Block Map Style BLOCK = 1 # Flow Map Style FLOW = 2 # The optional anchor for this mapping attr_accessor :anchor # The optional tag for this mapping attr_accessor :tag # Is this an implicit mapping? attr_accessor :implicit # The style of this mapping attr_accessor :style ### # Create a new Psych::Nodes::Mapping object. # # +anchor+ is the anchor associated with the map or +nil+. # +tag+ is the tag associated with the map or +nil+. # +implicit+ is a boolean indicating whether or not the map was implicitly # started. # +style+ is an integer indicating the mapping style. # # == See Also # See also Psych::Handler#start_mapping def initialize anchor = nil, tag = nil, implicit = true, style = BLOCK super() @anchor = anchor @tag = tag @implicit = implicit @style = style end def mapping?; true; end end end end psych-5.0.2/lib/psych/nodes/node.rb000066400000000000000000000036511436167465700171520ustar00rootroot00000000000000# frozen_string_literal: true require 'stringio' require_relative '../class_loader' require_relative '../scalar_scanner' module Psych module Nodes ### # The base class for any Node in a YAML parse tree. This class should # never be instantiated. class Node include Enumerable # The children of this node attr_reader :children # An associated tag attr_reader :tag # The line number where this node start attr_accessor :start_line # The column number where this node start attr_accessor :start_column # The line number where this node ends attr_accessor :end_line # The column number where this node ends attr_accessor :end_column # Create a new Psych::Nodes::Node def initialize @children = [] end ### # Iterate over each node in the tree. Yields each node to +block+ depth # first. def each &block return enum_for :each unless block_given? Visitors::DepthFirst.new(block).accept self end ### # Convert this node to Ruby. # # See also Psych::Visitors::ToRuby def to_ruby(symbolize_names: false, freeze: false, strict_integer: false) Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer).accept(self) end alias :transform :to_ruby ### # Convert this node to YAML. # # See also Psych::Visitors::Emitter def yaml io = nil, options = {} real_io = io || StringIO.new(''.encode('utf-8')) Visitors::Emitter.new(real_io, options).accept self return real_io.string unless io io end alias :to_yaml :yaml def alias?; false; end def document?; false; end def mapping?; false; end def scalar?; false; end def sequence?; false; end def stream?; false; end end end end psych-5.0.2/lib/psych/nodes/scalar.rb000066400000000000000000000032041436167465700174640ustar00rootroot00000000000000# frozen_string_literal: true module Psych module Nodes ### # This class represents a {YAML Scalar}[http://yaml.org/spec/1.1/#id858081]. # # This node type is a terminal node and should not have any children. class Scalar < Psych::Nodes::Node # Any style scalar, the emitter chooses ANY = 0 # Plain scalar style PLAIN = 1 # Single quoted style SINGLE_QUOTED = 2 # Double quoted style DOUBLE_QUOTED = 3 # Literal style LITERAL = 4 # Folded style FOLDED = 5 # The scalar value attr_accessor :value # The anchor value (if there is one) attr_accessor :anchor # The tag value (if there is one) attr_accessor :tag # Is this a plain scalar? attr_accessor :plain # Is this scalar quoted? attr_accessor :quoted # The style of this scalar attr_accessor :style ### # Create a new Psych::Nodes::Scalar object. # # +value+ is the string value of the scalar # +anchor+ is an associated anchor or nil # +tag+ is an associated tag or nil # +plain+ is a boolean value # +quoted+ is a boolean value # +style+ is an integer indicating the string style # # == See Also # # See also Psych::Handler#scalar def initialize value, anchor = nil, tag = nil, plain = true, quoted = false, style = ANY @value = value @anchor = anchor @tag = tag @plain = plain @quoted = quoted @style = style end def scalar?; true; end end end end psych-5.0.2/lib/psych/nodes/sequence.rb000066400000000000000000000040411436167465700200270ustar00rootroot00000000000000# frozen_string_literal: true module Psych module Nodes ### # This class represents a # {YAML sequence}[http://yaml.org/spec/1.1/#sequence/syntax]. # # A YAML sequence is basically a list, and looks like this: # # %YAML 1.1 # --- # - I am # - a Sequence # # A YAML sequence may have an anchor like this: # # %YAML 1.1 # --- # &A [ # "This sequence", # "has an anchor" # ] # # A YAML sequence may also have a tag like this: # # %YAML 1.1 # --- # !!seq [ # "This sequence", # "has a tag" # ] # # This class represents a sequence in a YAML document. A # Psych::Nodes::Sequence node may have 0 or more children. Valid children # for this node are: # # * Psych::Nodes::Sequence # * Psych::Nodes::Mapping # * Psych::Nodes::Scalar # * Psych::Nodes::Alias class Sequence < Psych::Nodes::Node # Any Styles, emitter chooses ANY = 0 # Block style sequence BLOCK = 1 # Flow style sequence FLOW = 2 # The anchor for this sequence (if any) attr_accessor :anchor # The tag name for this sequence (if any) attr_accessor :tag # Is this sequence started implicitly? attr_accessor :implicit # The sequence style used attr_accessor :style ### # Create a new object representing a YAML sequence. # # +anchor+ is the anchor associated with the sequence or nil. # +tag+ is the tag associated with the sequence or nil. # +implicit+ a boolean indicating whether or not the sequence was # implicitly started. # +style+ is an integer indicating the list style. # # See Psych::Handler#start_sequence def initialize anchor = nil, tag = nil, implicit = true, style = BLOCK super() @anchor = anchor @tag = tag @implicit = implicit @style = style end def sequence?; true; end end end end psych-5.0.2/lib/psych/nodes/stream.rb000066400000000000000000000020011436167465700175040ustar00rootroot00000000000000# frozen_string_literal: true module Psych module Nodes ### # Represents a YAML stream. This is the root node for any YAML parse # tree. This node must have one or more child nodes. The only valid # child node for a Psych::Nodes::Stream node is Psych::Nodes::Document. class Stream < Psych::Nodes::Node # Encodings supported by Psych (and libyaml) # Any encoding ANY = Psych::Parser::ANY # UTF-8 encoding UTF8 = Psych::Parser::UTF8 # UTF-16LE encoding UTF16LE = Psych::Parser::UTF16LE # UTF-16BE encoding UTF16BE = Psych::Parser::UTF16BE # The encoding used for this stream attr_accessor :encoding ### # Create a new Psych::Nodes::Stream node with an +encoding+ that # defaults to Psych::Nodes::Stream::UTF8. # # See also Psych::Handler#start_stream def initialize encoding = UTF8 super() @encoding = encoding end def stream?; true; end end end end psych-5.0.2/lib/psych/omap.rb000066400000000000000000000001131436167465700160370ustar00rootroot00000000000000# frozen_string_literal: true module Psych class Omap < ::Hash end end psych-5.0.2/lib/psych/parser.rb000066400000000000000000000040341436167465700164050ustar00rootroot00000000000000# frozen_string_literal: true module Psych ### # YAML event parser class. This class parses a YAML document and calls # events on the handler that is passed to the constructor. The events can # be used for things such as constructing a YAML AST or deserializing YAML # documents. It can even be fed back to Psych::Emitter to emit the same # document that was parsed. # # See Psych::Handler for documentation on the events that Psych::Parser emits. # # Here is an example that prints out ever scalar found in a YAML document: # # # Handler for detecting scalar values # class ScalarHandler < Psych::Handler # def scalar value, anchor, tag, plain, quoted, style # puts value # end # end # # parser = Psych::Parser.new(ScalarHandler.new) # parser.parse(yaml_document) # # Here is an example that feeds the parser back in to Psych::Emitter. The # YAML document is read from STDIN and written back out to STDERR: # # parser = Psych::Parser.new(Psych::Emitter.new($stderr)) # parser.parse($stdin) # # Psych uses Psych::Parser in combination with Psych::TreeBuilder to # construct an AST of the parsed YAML document. class Parser class Mark < Struct.new(:index, :line, :column) end # The handler on which events will be called attr_accessor :handler # Set the encoding for this parser to +encoding+ attr_writer :external_encoding ### # Creates a new Psych::Parser instance with +handler+. YAML events will # be called on +handler+. See Psych::Parser for more details. def initialize handler = Handler.new @handler = handler @external_encoding = ANY end ### # call-seq: # parser.parse(yaml) # # Parse the YAML document contained in +yaml+. Events will be called on # the handler set on the parser instance. # # See Psych::Parser and Psych::Parser#handler def parse yaml, path = yaml.respond_to?(:path) ? yaml.path : "" _native_parse @handler, yaml, path end end end psych-5.0.2/lib/psych/scalar_scanner.rb000066400000000000000000000112771436167465700200760ustar00rootroot00000000000000# frozen_string_literal: true module Psych ### # Scan scalars for built in types class ScalarScanner # Taken from http://yaml.org/type/timestamp.html TIME = /^-?\d{4}-\d{1,2}-\d{1,2}(?:[Tt]|\s+)\d{1,2}:\d\d:\d\d(?:\.\d*)?(?:\s*(?:Z|[-+]\d{1,2}:?(?:\d\d)?))?$/ # Taken from http://yaml.org/type/float.html # Base 60, [-+]inf and NaN are handled separately FLOAT = /^(?:[-+]?([0-9][0-9_,]*)?\.[0-9]*([eE][-+][0-9]+)?(?# base 10))$/x # Taken from http://yaml.org/type/int.html INTEGER_STRICT = /^(?:[-+]?0b[0-1_]+ (?# base 2) |[-+]?0[0-7_]+ (?# base 8) |[-+]?(0|[1-9][0-9_]*) (?# base 10) |[-+]?0x[0-9a-fA-F_]+ (?# base 16))$/x # Same as above, but allows commas. # Not to YML spec, but kept for backwards compatibility INTEGER_LEGACY = /^(?:[-+]?0b[0-1_,]+ (?# base 2) |[-+]?0[0-7_,]+ (?# base 8) |[-+]?(?:0|[1-9](?:[0-9]|,[0-9]|_[0-9])*) (?# base 10) |[-+]?0x[0-9a-fA-F_,]+ (?# base 16))$/x attr_reader :class_loader # Create a new scanner def initialize class_loader, strict_integer: false @symbol_cache = {} @class_loader = class_loader @strict_integer = strict_integer end # Tokenize +string+ returning the Ruby object def tokenize string return nil if string.empty? return @symbol_cache[string] if @symbol_cache.key?(string) integer_regex = @strict_integer ? INTEGER_STRICT : INTEGER_LEGACY # Check for a String type, being careful not to get caught by hash keys, hex values, and # special floats (e.g., -.inf). if string.match?(%r{^[^\d.:-]?[[:alpha:]_\s!@#$%\^&*(){}<>|/\\~;=]+}) || string.match?(/\n/) return string if string.length > 5 if string.match?(/^[^ytonf~]/i) string elsif string == '~' || string.match?(/^null$/i) nil elsif string.match?(/^(yes|true|on)$/i) true elsif string.match?(/^(no|false|off)$/i) false else string end elsif string.match?(TIME) begin parse_time string rescue ArgumentError string end elsif string.match?(/^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/) require 'date' begin class_loader.date.strptime(string, '%F', Date::GREGORIAN) rescue ArgumentError string end elsif string.match?(/^\+?\.inf$/i) Float::INFINITY elsif string.match?(/^-\.inf$/i) -Float::INFINITY elsif string.match?(/^\.nan$/i) Float::NAN elsif string.match?(/^:./) if string =~ /^:(["'])(.*)\1/ @symbol_cache[string] = class_loader.symbolize($2.sub(/^:/, '')) else @symbol_cache[string] = class_loader.symbolize(string.sub(/^:/, '')) end elsif string.match?(/^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}$/) i = 0 string.split(':').each_with_index do |n,e| i += (n.to_i * 60 ** (e - 2).abs) end i elsif string.match?(/^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}\.[0-9_]*$/) i = 0 string.split(':').each_with_index do |n,e| i += (n.to_f * 60 ** (e - 2).abs) end i elsif string.match?(FLOAT) if string.match?(/\A[-+]?\.\Z/) string else Float(string.delete(',_').gsub(/\.([Ee]|$)/, '\1')) end elsif string.match?(integer_regex) parse_int string else string end end ### # Parse and return an int from +string+ def parse_int string Integer(string.delete(',_')) end ### # Parse and return a Time from +string+ def parse_time string klass = class_loader.load 'Time' date, time = *(string.split(/[ tT]/, 2)) (yy, m, dd) = date.match(/^(-?\d{4})-(\d{1,2})-(\d{1,2})/).captures.map { |x| x.to_i } md = time.match(/(\d+:\d+:\d+)(?:\.(\d*))?\s*(Z|[-+]\d+(:\d\d)?)?/) (hh, mm, ss) = md[1].split(':').map { |x| x.to_i } us = (md[2] ? Rational("0.#{md[2]}") : 0) * 1000000 time = klass.utc(yy, m, dd, hh, mm, ss, us) return time if 'Z' == md[3] return klass.at(time.to_i, us) unless md[3] tz = md[3].match(/^([+\-]?\d{1,2})\:?(\d{1,2})?$/)[1..-1].compact.map { |digit| Integer(digit, 10) } offset = tz.first * 3600 if offset < 0 offset -= ((tz[1] || 0) * 60) else offset += ((tz[1] || 0) * 60) end klass.new(yy, m, dd, hh, mm, ss+us/(1_000_000r), offset) end end end psych-5.0.2/lib/psych/set.rb000066400000000000000000000001121436167465700156750ustar00rootroot00000000000000# frozen_string_literal: true module Psych class Set < ::Hash end end psych-5.0.2/lib/psych/stream.rb000066400000000000000000000016331436167465700164060ustar00rootroot00000000000000# frozen_string_literal: true module Psych ### # Psych::Stream is a streaming YAML emitter. It will not buffer your YAML, # but send it straight to an IO. # # Here is an example use: # # stream = Psych::Stream.new($stdout) # stream.start # stream.push({:foo => 'bar'}) # stream.finish # # YAML will be immediately emitted to $stdout with no buffering. # # Psych::Stream#start will take a block and ensure that Psych::Stream#finish # is called, so you can do this form: # # stream = Psych::Stream.new($stdout) # stream.start do |em| # em.push(:foo => 'bar') # end # class Stream < Psych::Visitors::YAMLTree class Emitter < Psych::Emitter # :nodoc: def end_document implicit_end = !streaming? super end def streaming? true end end include Psych::Streaming extend Psych::Streaming::ClassMethods end end psych-5.0.2/lib/psych/streaming.rb000066400000000000000000000012331436167465700171000ustar00rootroot00000000000000# frozen_string_literal: true module Psych module Streaming module ClassMethods ### # Create a new streaming emitter. Emitter will print to +io+. See # Psych::Stream for an example. def new io emitter = const_get(:Emitter).new(io) class_loader = ClassLoader.new ss = ScalarScanner.new class_loader super(emitter, ss, {}) end end ### # Start streaming using +encoding+ def start encoding = Nodes::Stream::UTF8 super.tap { yield self if block_given? } ensure finish if block_given? end private def register target, obj end end end psych-5.0.2/lib/psych/syntax_error.rb000066400000000000000000000011141436167465700176440ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'exception' module Psych class SyntaxError < Psych::Exception attr_reader :file, :line, :column, :offset, :problem, :context def initialize file, line, col, offset, problem, context err = [problem, context].compact.join ' ' filename = file || '' message = "(%s): %s at line %d column %d" % [filename, err, line, col] @file = file @line = line @column = col @offset = offset @problem = problem @context = context super(message) end end end psych-5.0.2/lib/psych/tree_builder.rb000066400000000000000000000060241436167465700175570ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'handler' module Psych ### # This class works in conjunction with Psych::Parser to build an in-memory # parse tree that represents a YAML document. # # == Example # # parser = Psych::Parser.new Psych::TreeBuilder.new # parser.parse('--- foo') # tree = parser.handler.root # # See Psych::Handler for documentation on the event methods used in this # class. class TreeBuilder < Psych::Handler # Returns the root node for the built tree attr_reader :root # Create a new TreeBuilder instance def initialize @stack = [] @last = nil @root = nil @start_line = nil @start_column = nil @end_line = nil @end_column = nil end def event_location(start_line, start_column, end_line, end_column) @start_line = start_line @start_column = start_column @end_line = end_line @end_column = end_column end %w{ Sequence Mapping }.each do |node| class_eval <<~RUBY, __FILE__, __LINE__ + 1 def start_#{node.downcase}(anchor, tag, implicit, style) n = Nodes::#{node}.new(anchor, tag, implicit, style) set_start_location(n) @last.children << n push n end def end_#{node.downcase} n = pop set_end_location(n) n end RUBY end ### # Handles start_document events with +version+, +tag_directives+, # and +implicit+ styling. # # See Psych::Handler#start_document def start_document version, tag_directives, implicit n = Nodes::Document.new version, tag_directives, implicit set_start_location(n) @last.children << n push n end ### # Handles end_document events with +version+, +tag_directives+, # and +implicit+ styling. # # See Psych::Handler#start_document def end_document implicit_end = !streaming? @last.implicit_end = implicit_end n = pop set_end_location(n) n end def start_stream encoding @root = Nodes::Stream.new(encoding) set_start_location(@root) push @root end def end_stream n = pop set_end_location(n) n end def scalar value, anchor, tag, plain, quoted, style s = Nodes::Scalar.new(value,anchor,tag,plain,quoted,style) set_location(s) @last.children << s s end def alias anchor a = Nodes::Alias.new(anchor) set_location(a) @last.children << a a end private def push value @stack.push value @last = value end def pop x = @stack.pop @last = @stack.last x end def set_location(node) set_start_location(node) set_end_location(node) end def set_start_location(node) node.start_line = @start_line node.start_column = @start_column end def set_end_location(node) node.end_line = @end_line node.end_column = @end_column end end end psych-5.0.2/lib/psych/versions.rb000066400000000000000000000002741436167465700167630ustar00rootroot00000000000000# frozen_string_literal: true module Psych # The version of Psych you are using VERSION = '5.0.2' if RUBY_ENGINE == 'jruby' DEFAULT_SNAKEYAML_VERSION = '1.33'.freeze end end psych-5.0.2/lib/psych/visitors.rb000066400000000000000000000003761436167465700170000ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'visitors/visitor' require_relative 'visitors/to_ruby' require_relative 'visitors/emitter' require_relative 'visitors/yaml_tree' require_relative 'visitors/json_tree' require_relative 'visitors/depth_first' psych-5.0.2/lib/psych/visitors/000077500000000000000000000000001436167465700164455ustar00rootroot00000000000000psych-5.0.2/lib/psych/visitors/depth_first.rb000066400000000000000000000011621436167465700213050ustar00rootroot00000000000000# frozen_string_literal: true module Psych module Visitors class DepthFirst < Psych::Visitors::Visitor def initialize block @block = block end private def nary o o.children.each { |x| visit x } @block.call o end alias :visit_Psych_Nodes_Stream :nary alias :visit_Psych_Nodes_Document :nary alias :visit_Psych_Nodes_Sequence :nary alias :visit_Psych_Nodes_Mapping :nary def terminal o @block.call o end alias :visit_Psych_Nodes_Scalar :terminal alias :visit_Psych_Nodes_Alias :terminal end end end psych-5.0.2/lib/psych/visitors/emitter.rb000066400000000000000000000027121436167465700204450ustar00rootroot00000000000000# frozen_string_literal: true module Psych module Visitors class Emitter < Psych::Visitors::Visitor def initialize io, options = {} opts = [:indentation, :canonical, :line_width].find_all { |opt| options.key?(opt) } if opts.empty? @handler = Psych::Emitter.new io else du = Handler::DumperOptions.new opts.each { |option| du.send :"#{option}=", options[option] } @handler = Psych::Emitter.new io, du end end def visit_Psych_Nodes_Stream o @handler.start_stream o.encoding o.children.each { |c| accept c } @handler.end_stream end def visit_Psych_Nodes_Document o @handler.start_document o.version, o.tag_directives, o.implicit o.children.each { |c| accept c } @handler.end_document o.implicit_end end def visit_Psych_Nodes_Scalar o @handler.scalar o.value, o.anchor, o.tag, o.plain, o.quoted, o.style end def visit_Psych_Nodes_Sequence o @handler.start_sequence o.anchor, o.tag, o.implicit, o.style o.children.each { |c| accept c } @handler.end_sequence end def visit_Psych_Nodes_Mapping o @handler.start_mapping o.anchor, o.tag, o.implicit, o.style o.children.each { |c| accept c } @handler.end_mapping end def visit_Psych_Nodes_Alias o @handler.alias o.anchor end end end end psych-5.0.2/lib/psych/visitors/json_tree.rb000066400000000000000000000011211436167465700207550ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../json/ruby_events' module Psych module Visitors class JSONTree < YAMLTree include Psych::JSON::RubyEvents def self.create options = {} emitter = Psych::JSON::TreeBuilder.new class_loader = ClassLoader.new ss = ScalarScanner.new class_loader new(emitter, ss, options) end def accept target if target.respond_to?(:encode_with) dump_coder target else send(@dispatch_cache[target.class], target) end end end end end psych-5.0.2/lib/psych/visitors/to_ruby.rb000066400000000000000000000306151436167465700204620ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../scalar_scanner' require_relative '../class_loader' require_relative '../exception' unless defined?(Regexp::NOENCODING) Regexp::NOENCODING = 32 end module Psych module Visitors ### # This class walks a YAML AST, converting each node to Ruby class ToRuby < Psych::Visitors::Visitor def self.create(symbolize_names: false, freeze: false, strict_integer: false) class_loader = ClassLoader.new scanner = ScalarScanner.new class_loader, strict_integer: strict_integer new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze) end attr_reader :class_loader def initialize ss, class_loader, symbolize_names: false, freeze: false super() @st = {} @ss = ss @load_tags = Psych.load_tags @domain_types = Psych.domain_types @class_loader = class_loader @symbolize_names = symbolize_names @freeze = freeze end def accept target result = super unless @domain_types.empty? || !target.tag key = target.tag.sub(/^[!\/]*/, '').sub(/(,\d+)\//, '\1:') key = "tag:#{key}" unless key =~ /^(?:tag:|x-private)/ if @domain_types.key? key value, block = @domain_types[key] result = block.call value, result end end result = deduplicate(result).freeze if @freeze result end def deserialize o if klass = resolve_class(@load_tags[o.tag]) instance = klass.allocate if instance.respond_to?(:init_with) coder = Psych::Coder.new(o.tag) coder.scalar = o.value instance.init_with coder end return instance end return o.value if o.quoted return @ss.tokenize(o.value) unless o.tag case o.tag when '!binary', 'tag:yaml.org,2002:binary' o.value.unpack('m').first when /^!(?:str|ruby\/string)(?::(.*))?$/, 'tag:yaml.org,2002:str' klass = resolve_class($1) if klass klass.allocate.replace o.value else o.value end when '!ruby/object:BigDecimal' require 'bigdecimal' unless defined? BigDecimal class_loader.big_decimal._load o.value when "!ruby/object:DateTime" class_loader.date_time require 'date' unless defined? DateTime t = @ss.parse_time(o.value) DateTime.civil(*t.to_a[0, 6].reverse, Rational(t.utc_offset, 86400)) + (t.subsec/86400) when '!ruby/encoding' ::Encoding.find o.value when "!ruby/object:Complex" class_loader.complex Complex(o.value) when "!ruby/object:Rational" class_loader.rational Rational(o.value) when "!ruby/class", "!ruby/module" resolve_class o.value when "tag:yaml.org,2002:float", "!float" Float(@ss.tokenize(o.value)) when "!ruby/regexp" klass = class_loader.regexp o.value =~ /^\/(.*)\/([mixn]*)$/m source = $1 options = 0 lang = nil ($2 || '').split('').each do |option| case option when 'x' then options |= Regexp::EXTENDED when 'i' then options |= Regexp::IGNORECASE when 'm' then options |= Regexp::MULTILINE when 'n' then options |= Regexp::NOENCODING else lang = option end end klass.new(*[source, options, lang].compact) when "!ruby/range" klass = class_loader.range args = o.value.split(/([.]{2,3})/, 2).map { |s| accept Nodes::Scalar.new(s) } args.push(args.delete_at(1) == '...') klass.new(*args) when /^!ruby\/sym(bol)?:?(.*)?$/ class_loader.symbolize o.value else @ss.tokenize o.value end end private :deserialize def visit_Psych_Nodes_Scalar o register o, deserialize(o) end def visit_Psych_Nodes_Sequence o if klass = resolve_class(@load_tags[o.tag]) instance = klass.allocate if instance.respond_to?(:init_with) coder = Psych::Coder.new(o.tag) coder.seq = o.children.map { |c| accept c } instance.init_with coder end return instance end case o.tag when nil register_empty(o) when '!omap', 'tag:yaml.org,2002:omap' map = register(o, Psych::Omap.new) o.children.each { |a| map[accept(a.children.first)] = accept a.children.last } map when /^!(?:seq|ruby\/array):(.*)$/ klass = resolve_class($1) list = register(o, klass.allocate) o.children.each { |c| list.push accept c } list else register_empty(o) end end def visit_Psych_Nodes_Mapping o if @load_tags[o.tag] return revive(resolve_class(@load_tags[o.tag]), o) end return revive_hash(register(o, {}), o) unless o.tag case o.tag when /^!ruby\/struct:?(.*)?$/ klass = resolve_class($1) if $1 if klass s = register(o, klass.allocate) members = {} struct_members = s.members.map { |x| class_loader.symbolize x } o.children.each_slice(2) do |k,v| member = accept(k) value = accept(v) if struct_members.include?(class_loader.symbolize(member)) s.send("#{member}=", value) else members[member.to_s.sub(/^@/, '')] = value end end init_with(s, members, o) else klass = class_loader.struct members = o.children.map { |c| accept c } h = Hash[*members] s = klass.new(*h.map { |k,v| class_loader.symbolize k }).new(*h.map { |k,v| v }) register(o, s) s end when /^!ruby\/object:?(.*)?$/ name = $1 || 'Object' if name == 'Complex' class_loader.complex h = Hash[*o.children.map { |c| accept c }] register o, Complex(h['real'], h['image']) elsif name == 'Rational' class_loader.rational h = Hash[*o.children.map { |c| accept c }] register o, Rational(h['numerator'], h['denominator']) elsif name == 'Hash' revive_hash(register(o, {}), o) else obj = revive((resolve_class(name) || class_loader.object), o) obj end when /^!(?:str|ruby\/string)(?::(.*))?$/, 'tag:yaml.org,2002:str' klass = resolve_class($1) members = {} string = nil o.children.each_slice(2) do |k,v| key = accept k value = accept v if key == 'str' if klass string = klass.allocate.replace value else string = value end register(o, string) else members[key] = value end end init_with(string, members.map { |k,v| [k.to_s.sub(/^@/, ''),v] }, o) when /^!ruby\/array:(.*)$/ klass = resolve_class($1) list = register(o, klass.allocate) members = Hash[o.children.map { |c| accept c }.each_slice(2).to_a] list.replace members['internal'] members['ivars'].each do |ivar, v| list.instance_variable_set ivar, v end list when '!ruby/range' klass = class_loader.range h = Hash[*o.children.map { |c| accept c }] register o, klass.new(h['begin'], h['end'], h['excl']) when /^!ruby\/exception:?(.*)?$/ h = Hash[*o.children.map { |c| accept c }] e = build_exception((resolve_class($1) || class_loader.exception), h.delete('message')) e.set_backtrace h.delete('backtrace') if h.key? 'backtrace' init_with(e, h, o) when '!set', 'tag:yaml.org,2002:set' set = class_loader.psych_set.new @st[o.anchor] = set if o.anchor o.children.each_slice(2) do |k,v| set[accept(k)] = accept(v) end set when /^!ruby\/hash-with-ivars(?::(.*))?$/ hash = $1 ? resolve_class($1).allocate : {} register o, hash o.children.each_slice(2) do |key, value| case key.value when 'elements' revive_hash hash, value when 'ivars' value.children.each_slice(2) do |k,v| hash.instance_variable_set accept(k), accept(v) end end end hash when /^!map:(.*)$/, /^!ruby\/hash:(.*)$/ revive_hash register(o, resolve_class($1).allocate), o when '!omap', 'tag:yaml.org,2002:omap' map = register(o, class_loader.psych_omap.new) o.children.each_slice(2) do |l,r| map[accept(l)] = accept r end map when /^!ruby\/marshalable:(.*)$/ name = $1 klass = resolve_class(name) obj = register(o, klass.allocate) if obj.respond_to?(:init_with) init_with(obj, revive_hash({}, o), o) elsif obj.respond_to?(:marshal_load) marshal_data = o.children.map(&method(:accept)) obj.marshal_load(marshal_data) obj else raise ArgumentError, "Cannot deserialize #{name}" end else revive_hash(register(o, {}), o) end end def visit_Psych_Nodes_Document o accept o.root end def visit_Psych_Nodes_Stream o o.children.map { |c| accept c } end def visit_Psych_Nodes_Alias o @st.fetch(o.anchor) { raise AnchorNotDefined, o.anchor } end private def register node, object @st[node.anchor] = object if node.anchor object end def register_empty object list = register(object, []) object.children.each { |c| list.push accept c } list end def revive_hash hash, o, tagged= false o.children.each_slice(2) { |k,v| key = accept(k) val = accept(v) if key == '<<' && k.tag != "tag:yaml.org,2002:str" case v when Nodes::Alias, Nodes::Mapping begin hash.merge! val rescue TypeError hash[key] = val end when Nodes::Sequence begin h = {} val.reverse_each do |value| h.merge! value end hash.merge! h rescue TypeError hash[key] = val end else hash[key] = val end else if !tagged && @symbolize_names && key.is_a?(String) key = key.to_sym elsif !@freeze key = deduplicate(key) end hash[key] = val end } hash end if RUBY_VERSION < '2.7' def deduplicate key if key.is_a?(String) # It is important to untaint the string, otherwise it won't # be deduplicated into an fstring, but simply frozen. -(key.untaint) else key end end else def deduplicate key if key.is_a?(String) -key else key end end end def merge_key hash, key, val end def revive klass, node s = register(node, klass.allocate) init_with(s, revive_hash({}, node, true), node) end def init_with o, h, node c = Psych::Coder.new(node.tag) c.map = h if o.respond_to?(:init_with) o.init_with c else h.each { |k,v| o.instance_variable_set(:"@#{k}", v) } end o end # Convert +klassname+ to a Class def resolve_class klassname class_loader.load klassname end end class NoAliasRuby < ToRuby def visit_Psych_Nodes_Alias o raise AliasesNotEnabled end end end end psych-5.0.2/lib/psych/visitors/visitor.rb000066400000000000000000000012761436167465700204770ustar00rootroot00000000000000# frozen_string_literal: true module Psych module Visitors class Visitor def accept target visit target end private # @api private def self.dispatch_cache Hash.new do |hash, klass| hash[klass] = :"visit_#{klass.name.gsub('::', '_')}" end.compare_by_identity end if defined?(Ractor) def dispatch @dispatch_cache ||= (Ractor.current[:Psych_Visitors_Visitor] ||= Visitor.dispatch_cache) end else DISPATCH = dispatch_cache def dispatch DISPATCH end end def visit target send dispatch[target.class], target end end end end psych-5.0.2/lib/psych/visitors/yaml_tree.rb000066400000000000000000000403771436167465700207660ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../tree_builder' require_relative '../scalar_scanner' require_relative '../class_loader' module Psych module Visitors ### # YAMLTree builds a YAML ast given a Ruby object. For example: # # builder = Psych::Visitors::YAMLTree.new # builder << { :foo => 'bar' } # builder.tree # => # v } @emitter.end_sequence end def visit_Encoding o tag = "!ruby/encoding" @emitter.scalar o.name, nil, tag, false, false, Nodes::Scalar::ANY end def visit_Object o tag = Psych.dump_tags[o.class] unless tag klass = o.class == Object ? nil : o.class.name tag = ['!ruby/object', klass].compact.join(':') end map = @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK) register(o, map) dump_ivars o @emitter.end_mapping end alias :visit_Delegator :visit_Object def visit_Struct o tag = ['!ruby/struct', o.class.name].compact.join(':') register o, @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK) o.members.each do |member| @emitter.scalar member.to_s, nil, nil, true, false, Nodes::Scalar::ANY accept o[member] end dump_ivars o @emitter.end_mapping end def visit_Exception o dump_exception o, o.message.to_s end def visit_NameError o dump_exception o, o.message.to_s end def visit_Regexp o register o, @emitter.scalar(o.inspect, nil, '!ruby/regexp', false, false, Nodes::Scalar::ANY) end def visit_Date o register o, visit_Integer(o.gregorian) end def visit_DateTime o t = o.italy formatted = format_time t, t.offset.zero? tag = '!ruby/object:DateTime' register o, @emitter.scalar(formatted, nil, tag, false, false, Nodes::Scalar::ANY) end def visit_Time o formatted = format_time o register o, @emitter.scalar(formatted, nil, nil, true, false, Nodes::Scalar::ANY) end def visit_Rational o register o, @emitter.start_mapping(nil, '!ruby/object:Rational', false, Nodes::Mapping::BLOCK) [ 'denominator', o.denominator.to_s, 'numerator', o.numerator.to_s ].each do |m| @emitter.scalar m, nil, nil, true, false, Nodes::Scalar::ANY end @emitter.end_mapping end def visit_Complex o register o, @emitter.start_mapping(nil, '!ruby/object:Complex', false, Nodes::Mapping::BLOCK) ['real', o.real.to_s, 'image', o.imag.to_s].each do |m| @emitter.scalar m, nil, nil, true, false, Nodes::Scalar::ANY end @emitter.end_mapping end def visit_Integer o @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY end alias :visit_TrueClass :visit_Integer alias :visit_FalseClass :visit_Integer def visit_Float o if o.nan? @emitter.scalar '.nan', nil, nil, true, false, Nodes::Scalar::ANY elsif o.infinite? @emitter.scalar((o.infinite? > 0 ? '.inf' : '-.inf'), nil, nil, true, false, Nodes::Scalar::ANY) else @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY end end def visit_BigDecimal o @emitter.scalar o._dump, nil, '!ruby/object:BigDecimal', false, false, Nodes::Scalar::ANY end def visit_String o plain = true quote = true style = Nodes::Scalar::PLAIN tag = nil if binary?(o) o = [o].pack('m0') tag = '!binary' # FIXME: change to below when syck is removed #tag = 'tag:yaml.org,2002:binary' style = Nodes::Scalar::LITERAL plain = false quote = false elsif o =~ /\n(?!\Z)/ # match \n except blank line at the end of string style = Nodes::Scalar::LITERAL elsif o == '<<' style = Nodes::Scalar::SINGLE_QUOTED tag = 'tag:yaml.org,2002:str' plain = false quote = false elsif o == 'y' || o == 'n' style = Nodes::Scalar::DOUBLE_QUOTED elsif @line_width && o.length > @line_width style = Nodes::Scalar::FOLDED elsif o =~ /^[^[:word:]][^"]*$/ style = Nodes::Scalar::DOUBLE_QUOTED elsif not String === @ss.tokenize(o) or /\A0[0-7]*[89]/ =~ o style = Nodes::Scalar::SINGLE_QUOTED end is_primitive = o.class == ::String ivars = is_primitive ? [] : o.instance_variables if ivars.empty? unless is_primitive tag = "!ruby/string:#{o.class}" plain = false quote = false end @emitter.scalar o, nil, tag, plain, quote, style else maptag = '!ruby/string'.dup maptag << ":#{o.class}" unless o.class == ::String register o, @emitter.start_mapping(nil, maptag, false, Nodes::Mapping::BLOCK) @emitter.scalar 'str', nil, nil, true, false, Nodes::Scalar::ANY @emitter.scalar o, nil, tag, plain, quote, style dump_ivars o @emitter.end_mapping end end def visit_Module o raise TypeError, "can't dump anonymous module: #{o}" unless o.name register o, @emitter.scalar(o.name, nil, '!ruby/module', false, false, Nodes::Scalar::SINGLE_QUOTED) end def visit_Class o raise TypeError, "can't dump anonymous class: #{o}" unless o.name register o, @emitter.scalar(o.name, nil, '!ruby/class', false, false, Nodes::Scalar::SINGLE_QUOTED) end def visit_Range o register o, @emitter.start_mapping(nil, '!ruby/range', false, Nodes::Mapping::BLOCK) ['begin', o.begin, 'end', o.end, 'excl', o.exclude_end?].each do |m| accept m end @emitter.end_mapping end def visit_Hash o if o.class == ::Hash register(o, @emitter.start_mapping(nil, nil, true, Psych::Nodes::Mapping::BLOCK)) o.each do |k,v| accept k accept v end @emitter.end_mapping else visit_hash_subclass o end end def visit_Psych_Set o register(o, @emitter.start_mapping(nil, '!set', false, Psych::Nodes::Mapping::BLOCK)) o.each do |k,v| accept k accept v end @emitter.end_mapping end def visit_Array o if o.class == ::Array visit_Enumerator o else visit_array_subclass o end end def visit_Enumerator o register o, @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK) o.each { |c| accept c } @emitter.end_sequence end def visit_NilClass o @emitter.scalar('', nil, 'tag:yaml.org,2002:null', true, false, Nodes::Scalar::ANY) end def visit_Symbol o if o.empty? @emitter.scalar "", nil, '!ruby/symbol', false, false, Nodes::Scalar::ANY else @emitter.scalar ":#{o}", nil, nil, true, false, Nodes::Scalar::ANY end end def visit_BasicObject o tag = Psych.dump_tags[o.class] tag ||= "!ruby/marshalable:#{o.class.name}" map = @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK) register(o, map) o.marshal_dump.each(&method(:accept)) @emitter.end_mapping end private def binary? string string.encoding == Encoding::ASCII_8BIT && !string.ascii_only? end def visit_array_subclass o tag = "!ruby/array:#{o.class}" ivars = o.instance_variables if ivars.empty? node = @emitter.start_sequence(nil, tag, false, Nodes::Sequence::BLOCK) register o, node o.each { |c| accept c } @emitter.end_sequence else node = @emitter.start_mapping(nil, tag, false, Nodes::Sequence::BLOCK) register o, node # Dump the internal list accept 'internal' @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK) o.each { |c| accept c } @emitter.end_sequence # Dump the ivars accept 'ivars' @emitter.start_mapping(nil, nil, true, Nodes::Sequence::BLOCK) ivars.each do |ivar| accept ivar accept o.instance_variable_get ivar end @emitter.end_mapping @emitter.end_mapping end end def visit_hash_subclass o ivars = o.instance_variables if ivars.any? tag = "!ruby/hash-with-ivars:#{o.class}" node = @emitter.start_mapping(nil, tag, false, Psych::Nodes::Mapping::BLOCK) register(o, node) # Dump the ivars accept 'ivars' @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK o.instance_variables.each do |ivar| accept ivar accept o.instance_variable_get ivar end @emitter.end_mapping # Dump the elements accept 'elements' @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK o.each do |k,v| accept k accept v end @emitter.end_mapping @emitter.end_mapping else tag = "!ruby/hash:#{o.class}" node = @emitter.start_mapping(nil, tag, false, Psych::Nodes::Mapping::BLOCK) register(o, node) o.each do |k,v| accept k accept v end @emitter.end_mapping end end def dump_list o end def dump_exception o, msg tag = ['!ruby/exception', o.class.name].join ':' @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK if msg @emitter.scalar 'message', nil, nil, true, false, Nodes::Scalar::ANY accept msg end @emitter.scalar 'backtrace', nil, nil, true, false, Nodes::Scalar::ANY accept o.backtrace dump_ivars o @emitter.end_mapping end def format_time time, utc = time.utc? if utc time.strftime("%Y-%m-%d %H:%M:%S.%9N Z") else time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z") end end def register target, yaml_obj @st.register target, yaml_obj yaml_obj end def dump_coder o @coders << o tag = Psych.dump_tags[o.class] unless tag klass = o.class == Object ? nil : o.class.name tag = ['!ruby/object', klass].compact.join(':') end c = Psych::Coder.new(tag) o.encode_with(c) emit_coder c, o end def emit_coder c, o case c.type when :scalar @emitter.scalar c.scalar, nil, c.tag, c.tag.nil?, false, c.style when :seq @emitter.start_sequence nil, c.tag, c.tag.nil?, c.style c.seq.each do |thing| accept thing end @emitter.end_sequence when :map register o, @emitter.start_mapping(nil, c.tag, c.implicit, c.style) c.map.each do |k,v| accept k accept v end @emitter.end_mapping when :object accept c.object end end def dump_ivars target target.instance_variables.each do |iv| @emitter.scalar("#{iv.to_s.sub(/^@/, '')}", nil, nil, true, false, Nodes::Scalar::ANY) accept target.instance_variable_get(iv) end end end class RestrictedYAMLTree < YAMLTree DEFAULT_PERMITTED_CLASSES = { TrueClass => true, FalseClass => true, NilClass => true, Integer => true, Float => true, String => true, Array => true, Hash => true, }.compare_by_identity.freeze def initialize emitter, ss, options super @permitted_classes = DEFAULT_PERMITTED_CLASSES.dup Array(options[:permitted_classes]).each do |klass| @permitted_classes[klass] = true end @permitted_symbols = {}.compare_by_identity Array(options[:permitted_symbols]).each do |symbol| @permitted_symbols[symbol] = true end @aliases = options.fetch(:aliases, false) end def accept target if !@aliases && @st.key?(target) raise BadAlias, "Tried to dump an aliased object" end unless @permitted_classes[target.class] raise DisallowedClass.new('dump', target.class.name || target.class.inspect) end super end def visit_Symbol sym unless @permitted_symbols[sym] raise DisallowedClass.new('dump', "Symbol(#{sym.inspect})") end super end end end end psych-5.0.2/lib/psych/y.rb000066400000000000000000000002761436167465700153650ustar00rootroot00000000000000# frozen_string_literal: true module Kernel ### # An alias for Psych.dump_stream meant to be used with IRB. def y *objects puts Psych.dump_stream(*objects) end private :y end psych-5.0.2/lib/psych_jars.rb000066400000000000000000000002251436167465700161260ustar00rootroot00000000000000# frozen_string_literal: true require 'psych.jar' require 'jar-dependencies' require_jar('org.yaml', 'snakeyaml', Psych::DEFAULT_SNAKEYAML_VERSION) psych-5.0.2/psych.gemspec000066400000000000000000000066211436167465700153670ustar00rootroot00000000000000# -*- encoding: utf-8 -*- # frozen_string_literal: true version_module = Module.new do version_rb = File.join(__dir__, "lib/psych/versions.rb") module_eval(File.read(version_rb), version_rb) end Gem::Specification.new do |s| s.name = "psych" s.version = version_module::Psych::VERSION s.authors = ["Aaron Patterson", "SHIBATA Hiroshi", "Charles Oliver Nutter"] s.email = ["aaron@tenderlovemaking.com", "hsbt@ruby-lang.org", "headius@headius.com"] s.summary = "Psych is a YAML parser and emitter" s.description = <<-DESCRIPTION Psych is a YAML parser and emitter. Psych leverages libyaml[https://pyyaml.org/wiki/LibYAML] for its YAML parsing and emitting capabilities. In addition to wrapping libyaml, Psych also knows how to serialize and de-serialize most Ruby objects to and from the YAML format. DESCRIPTION s.homepage = "https://github.com/ruby/psych" s.licenses = ["MIT"] s.require_paths = ["lib"] # for ruby core repository. It was generated by `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } s.files = [ ".gitignore", "Gemfile", "LICENSE", "Mavenfile", "README.md", "Rakefile", "bin/console", "bin/setup", "ext/psych/depend", "ext/psych/extconf.rb", "ext/psych/psych.c", "ext/psych/psych.h", "ext/psych/psych_emitter.c", "ext/psych/psych_emitter.h", "ext/psych/psych_parser.c", "ext/psych/psych_parser.h", "ext/psych/psych_to_ruby.c", "ext/psych/psych_to_ruby.h", "ext/psych/psych_yaml_tree.c", "ext/psych/psych_yaml_tree.h", "lib/psych.rb", "lib/psych/class_loader.rb", "lib/psych/coder.rb", "lib/psych/core_ext.rb", "lib/psych/exception.rb", "lib/psych/handler.rb", "lib/psych/handlers/document_stream.rb", "lib/psych/handlers/recorder.rb", "lib/psych/json/ruby_events.rb", "lib/psych/json/stream.rb", "lib/psych/json/tree_builder.rb", "lib/psych/json/yaml_events.rb", "lib/psych/nodes.rb", "lib/psych/nodes/alias.rb", "lib/psych/nodes/document.rb", "lib/psych/nodes/mapping.rb", "lib/psych/nodes/node.rb", "lib/psych/nodes/scalar.rb", "lib/psych/nodes/sequence.rb", "lib/psych/nodes/stream.rb", "lib/psych/omap.rb", "lib/psych/parser.rb", "lib/psych/scalar_scanner.rb", "lib/psych/set.rb", "lib/psych/stream.rb", "lib/psych/streaming.rb", "lib/psych/syntax_error.rb", "lib/psych/tree_builder.rb", "lib/psych/versions.rb", "lib/psych/visitors.rb","lib/psych/visitors/depth_first.rb", "lib/psych/visitors/emitter.rb", "lib/psych/visitors/json_tree.rb", "lib/psych/visitors/to_ruby.rb", "lib/psych/visitors/visitor.rb", "lib/psych/visitors/yaml_tree.rb", "lib/psych/y.rb", "psych.gemspec" ] s.rdoc_options = ["--main", "README.md"] s.extra_rdoc_files = ["README.md"] s.required_ruby_version = Gem::Requirement.new(">= 2.4.0") s.required_rubygems_version = Gem::Requirement.new(">= 0") if RUBY_ENGINE == 'jruby' s.platform = 'java' s.files.concat [ "ext/java/org/jruby/ext/psych/PsychEmitter.java", "ext/java/org/jruby/ext/psych/PsychLibrary.java", "ext/java/org/jruby/ext/psych/PsychParser.java", "ext/java/org/jruby/ext/psych/PsychToRuby.java", "lib/psych_jars.rb", "lib/psych.jar" ] s.requirements = "jar org.yaml:snakeyaml, #{version_module::Psych::DEFAULT_SNAKEYAML_VERSION}" s.add_dependency 'jar-dependencies', '>= 0.1.7' else s.extensions = ["ext/psych/extconf.rb"] s.add_dependency 'stringio' end s.metadata['msys2_mingw_dependencies'] = 'libyaml' end psych-5.0.2/test/000077500000000000000000000000001436167465700136465ustar00rootroot00000000000000psych-5.0.2/test/lib/000077500000000000000000000000001436167465700144145ustar00rootroot00000000000000psych-5.0.2/test/lib/core_assertions.rb000066400000000000000000000610651436167465700201530ustar00rootroot00000000000000# frozen_string_literal: true module Test module Unit module Assertions def _assertions= n # :nodoc: @_assertions = n end def _assertions # :nodoc: @_assertions ||= 0 end ## # Returns a proc that will output +msg+ along with the default message. def message msg = nil, ending = nil, &default proc { msg = msg.call.chomp(".") if Proc === msg custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty? "#{custom_message}#{default.call}#{ending || "."}" } end end module CoreAssertions require_relative 'envutil' require 'pp' def mu_pp(obj) #:nodoc: obj.pretty_inspect.chomp end def assert_file AssertFile end FailDesc = proc do |status, message = "", out = ""| now = Time.now proc do EnvUtil.failure_description(status, now, message, out) end end def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [], message = nil, success: nil, **opt) args = Array(args).dup args.insert((Hash === args[0] ? 1 : 0), '--disable=gems') stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, true, true, **opt) desc = FailDesc[status, message, stderr] if block_given? raise "test_stdout ignored, use block only or without block" if test_stdout != [] raise "test_stderr ignored, use block only or without block" if test_stderr != [] yield(stdout.lines.map {|l| l.chomp }, stderr.lines.map {|l| l.chomp }, status) else all_assertions(desc) do |a| [["stdout", test_stdout, stdout], ["stderr", test_stderr, stderr]].each do |key, exp, act| a.for(key) do if exp.is_a?(Regexp) assert_match(exp, act) elsif exp.all? {|e| String === e} assert_equal(exp, act.lines.map {|l| l.chomp }) else assert_pattern_list(exp, act) end end end unless success.nil? a.for("success?") do if success assert_predicate(status, :success?) else assert_not_predicate(status, :success?) end end end end status end end if defined?(RubyVM::InstructionSequence) def syntax_check(code, fname, line) code = code.dup.force_encoding(Encoding::UTF_8) RubyVM::InstructionSequence.compile(code, fname, fname, line) :ok ensure raise if SyntaxError === $! end else def syntax_check(code, fname, line) code = code.b code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) { "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ok}\n" } code = code.force_encoding(Encoding::UTF_8) catch {|tag| eval(code, binding, fname, line - 1)} end end def assert_no_memory_leak(args, prepare, code, message=nil, limit: 2.0, rss: false, **opt) # TODO: consider choosing some appropriate limit for MJIT and stop skipping this once it does not randomly fail pend 'assert_no_memory_leak may consider MJIT memory usage as leak' if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? require_relative 'memory_status' raise Test::Unit::PendedError, "unsupported platform" unless defined?(Memory::Status) token = "\e[7;1m#{$$.to_s}:#{Time.now.strftime('%s.%L')}:#{rand(0x10000).to_s(16)}:\e[m" token_dump = token.dump token_re = Regexp.quote(token) envs = args.shift if Array === args and Hash === args.first args = [ "--disable=gems", "-r", File.expand_path("../memory_status", __FILE__), *args, "-v", "-", ] if defined? Memory::NO_MEMORY_LEAK_ENVS then envs ||= {} newenvs = envs.merge(Memory::NO_MEMORY_LEAK_ENVS) { |_, _, _| break } envs = newenvs if newenvs end args.unshift(envs) if envs cmd = [ 'END {STDERR.puts '"#{token_dump}"'"FINAL=#{Memory::Status.new}"}', prepare, 'STDERR.puts('"#{token_dump}"'"START=#{$initial_status = Memory::Status.new}")', '$initial_size = $initial_status.size', code, 'GC.start', ].join("\n") _, err, status = EnvUtil.invoke_ruby(args, cmd, true, true, **opt) before = err.sub!(/^#{token_re}START=(\{.*\})\n/, '') && Memory::Status.parse($1) after = err.sub!(/^#{token_re}FINAL=(\{.*\})\n/, '') && Memory::Status.parse($1) assert(status.success?, FailDesc[status, message, err]) ([:size, (rss && :rss)] & after.members).each do |n| b = before[n] a = after[n] next unless a > 0 and b > 0 assert_operator(a.fdiv(b), :<, limit, message(message) {"#{n}: #{b} => #{a}"}) end rescue LoadError pend end # :call-seq: # assert_nothing_raised( *args, &block ) # #If any exceptions are given as arguments, the assertion will #fail if one of those exceptions are raised. Otherwise, the test fails #if any exceptions are raised. # #The final argument may be a failure message. # # assert_nothing_raised RuntimeError do # raise Exception #Assertion passes, Exception is not a RuntimeError # end # # assert_nothing_raised do # raise Exception #Assertion fails # end def assert_nothing_raised(*args) self._assertions += 1 if Module === args.last msg = nil else msg = args.pop end begin line = __LINE__; yield rescue Test::Unit::PendedError raise rescue Exception => e bt = e.backtrace as = e.instance_of?(Test::Unit::AssertionFailedError) if as ans = /\A#{Regexp.quote(__FILE__)}:#{line}:in /o bt.reject! {|ln| ans =~ ln} end if ((args.empty? && !as) || args.any? {|a| a.instance_of?(Module) ? e.is_a?(a) : e.class == a }) msg = message(msg) { "Exception raised:\n<#{mu_pp(e)}>\n" + "Backtrace:\n" + e.backtrace.map{|frame| " #{frame}"}.join("\n") } raise Test::Unit::AssertionFailedError, msg.call, bt else raise end end end def prepare_syntax_check(code, fname = nil, mesg = nil, verbose: nil) fname ||= caller_locations(2, 1)[0] mesg ||= fname.to_s verbose, $VERBOSE = $VERBOSE, verbose case when Array === fname fname, line = *fname when defined?(fname.path) && defined?(fname.lineno) fname, line = fname.path, fname.lineno else line = 1 end yield(code, fname, line, message(mesg) { if code.end_with?("\n") "```\n#{code}```\n" else "```\n#{code}\n```\n""no-newline" end }) ensure $VERBOSE = verbose end def assert_valid_syntax(code, *args, **opt) prepare_syntax_check(code, *args, **opt) do |src, fname, line, mesg| yield if defined?(yield) assert_nothing_raised(SyntaxError, mesg) do assert_equal(:ok, syntax_check(src, fname, line), mesg) end end end def assert_normal_exit(testsrc, message = '', child_env: nil, **opt) assert_valid_syntax(testsrc, caller_locations(1, 1)[0]) if child_env child_env = [child_env] else child_env = [] end out, _, status = EnvUtil.invoke_ruby(child_env + %W'-W0', testsrc, true, :merge_to_stdout, **opt) assert !status.signaled?, FailDesc[status, message, out] end def assert_ruby_status(args, test_stdin="", message=nil, **opt) out, _, status = EnvUtil.invoke_ruby(args, test_stdin, true, :merge_to_stdout, **opt) desc = FailDesc[status, message, out] assert(!status.signaled?, desc) message ||= "ruby exit status is not success:" assert(status.success?, desc) end ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV TERM") def separated_runner(out = nil) include(*Test::Unit::TestCase.ancestors.select {|c| !c.is_a?(Class) }) out = out ? IO.new(out, 'w') : STDOUT at_exit { out.puts [Marshal.dump($!)].pack('m'), "assertions=#{self._assertions}" } Test::Unit::Runner.class_variable_set(:@@stop_auto_run, true) if defined?(Test::Unit::Runner) end def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt) unless file and line loc, = caller_locations(1,1) file ||= loc.path line ||= loc.lineno end capture_stdout = true unless /mswin|mingw/ =~ RUBY_PLATFORM capture_stdout = false opt[:out] = Test::Unit::Runner.output if defined?(Test::Unit::Runner) res_p, res_c = IO.pipe opt[:ios] = [res_c] end src = < marshal_error ignore_stderr = nil res = nil end if res and !(SystemExit === res) if bt = res.backtrace bt.each do |l| l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"} end bt.concat(caller) else res.set_backtrace(caller) end raise res end # really is it succeed? unless ignore_stderr # the body of assert_separately must not output anything to detect error assert(stderr.empty?, FailDesc[status, "assert_separately failed with error message", stderr]) end assert(status.success?, FailDesc[status, "assert_separately failed", stderr]) raise marshal_error if marshal_error end # Run Ractor-related test without influencing the main test suite def assert_ractor(src, args: [], require: nil, require_relative: nil, file: nil, line: nil, ignore_stderr: nil, **opt) return unless defined?(Ractor) require = "require #{require.inspect}" if require if require_relative dir = File.dirname(caller_locations[0,1][0].absolute_path) full_path = File.expand_path(require_relative, dir) require = "#{require}; require #{full_path.inspect}" end assert_separately(args, file, line, <<~RUBY, ignore_stderr: ignore_stderr, **opt) #{require} previous_verbose = $VERBOSE $VERBOSE = nil Ractor.new {} # trigger initial warning $VERBOSE = previous_verbose #{src} RUBY end # :call-seq: # assert_throw( tag, failure_message = nil, &block ) # #Fails unless the given block throws +tag+, returns the caught #value otherwise. # #An optional failure message may be provided as the final argument. # # tag = Object.new # assert_throw(tag, "#{tag} was not thrown!") do # throw tag # end def assert_throw(tag, msg = nil) ret = catch(tag) do begin yield(tag) rescue UncaughtThrowError => e thrown = e.tag end msg = message(msg) { "Expected #{mu_pp(tag)} to have been thrown"\ "#{%Q[, not #{thrown}] if thrown}" } assert(false, msg) end assert(true) ret end # :call-seq: # assert_raise( *args, &block ) # #Tests if the given block raises an exception. Acceptable exception #types may be given as optional arguments. If the last argument is a #String, it will be used as the error message. # # assert_raise do #Fails, no Exceptions are raised # end # # assert_raise NameError do # puts x #Raises NameError, so assertion succeeds # end def assert_raise(*exp, &b) case exp.last when String, Proc msg = exp.pop end begin yield rescue Test::Unit::PendedError => e return e if exp.include? Test::Unit::PendedError raise e rescue Exception => e expected = exp.any? { |ex| if ex.instance_of? Module then e.kind_of? ex else e.instance_of? ex end } assert expected, proc { flunk(message(msg) {"#{mu_pp(exp)} exception expected, not #{mu_pp(e)}"}) } return e ensure unless e exp = exp.first if exp.size == 1 flunk(message(msg) {"#{mu_pp(exp)} expected but nothing was raised"}) end end end # :call-seq: # assert_raise_with_message(exception, expected, msg = nil, &block) # #Tests if the given block raises an exception with the expected #message. # # assert_raise_with_message(RuntimeError, "foo") do # nil #Fails, no Exceptions are raised # end # # assert_raise_with_message(RuntimeError, "foo") do # raise ArgumentError, "foo" #Fails, different Exception is raised # end # # assert_raise_with_message(RuntimeError, "foo") do # raise "bar" #Fails, RuntimeError is raised but the message differs # end # # assert_raise_with_message(RuntimeError, "foo") do # raise "foo" #Raises RuntimeError with the message, so assertion succeeds # end def assert_raise_with_message(exception, expected, msg = nil, &block) case expected when String assert = :assert_equal when Regexp assert = :assert_match else raise TypeError, "Expected #{expected.inspect} to be a kind of String or Regexp, not #{expected.class}" end ex = m = nil EnvUtil.with_default_internal(expected.encoding) do ex = assert_raise(exception, msg || proc {"Exception(#{exception}) with message matches to #{expected.inspect}"}) do yield end m = ex.message end msg = message(msg, "") {"Expected Exception(#{exception}) was raised, but the message doesn't match"} if assert == :assert_equal assert_equal(expected, m, msg) else msg = message(msg) { "Expected #{mu_pp expected} to match #{mu_pp m}" } assert expected =~ m, msg block.binding.eval("proc{|_|$~=_}").call($~) end ex end MINI_DIR = File.join(File.dirname(File.expand_path(__FILE__)), "minitest") #:nodoc: # :call-seq: # assert(test, [failure_message]) # #Tests if +test+ is true. # #+msg+ may be a String or a Proc. If +msg+ is a String, it will be used #as the failure message. Otherwise, the result of calling +msg+ will be #used as the message if the assertion fails. # #If no +msg+ is given, a default message will be used. # # assert(false, "This was expected to be true") def assert(test, *msgs) case msg = msgs.first when String, Proc when nil msgs.shift else bt = caller.reject { |s| s.start_with?(MINI_DIR) } raise ArgumentError, "assertion message must be String or Proc, but #{msg.class} was given.", bt end unless msgs.empty? super end # :call-seq: # assert_respond_to( object, method, failure_message = nil ) # #Tests if the given Object responds to +method+. # #An optional failure message may be provided as the final argument. # # assert_respond_to("hello", :reverse) #Succeeds # assert_respond_to("hello", :does_not_exist) #Fails def assert_respond_to(obj, (meth, *priv), msg = nil) unless priv.empty? msg = message(msg) { "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}#{" privately" if priv[0]}" } return assert obj.respond_to?(meth, *priv), msg end #get rid of overcounting if caller_locations(1, 1)[0].path.start_with?(MINI_DIR) return if obj.respond_to?(meth) end super(obj, meth, msg) end # :call-seq: # assert_not_respond_to( object, method, failure_message = nil ) # #Tests if the given Object does not respond to +method+. # #An optional failure message may be provided as the final argument. # # assert_not_respond_to("hello", :reverse) #Fails # assert_not_respond_to("hello", :does_not_exist) #Succeeds def assert_not_respond_to(obj, (meth, *priv), msg = nil) unless priv.empty? msg = message(msg) { "Expected #{mu_pp(obj)} (#{obj.class}) to not respond to ##{meth}#{" privately" if priv[0]}" } return assert !obj.respond_to?(meth, *priv), msg end #get rid of overcounting if caller_locations(1, 1)[0].path.start_with?(MINI_DIR) return unless obj.respond_to?(meth) end refute_respond_to(obj, meth, msg) end # pattern_list is an array which contains regexp and :*. # :* means any sequence. # # pattern_list is anchored. # Use [:*, regexp, :*] for non-anchored match. def assert_pattern_list(pattern_list, actual, message=nil) rest = actual anchored = true pattern_list.each_with_index {|pattern, i| if pattern == :* anchored = false else if anchored match = /\A#{pattern}/.match(rest) else match = pattern.match(rest) end unless match msg = message(msg) { expect_msg = "Expected #{mu_pp pattern}\n" if /\n[^\n]/ =~ rest actual_mesg = +"to match\n" rest.scan(/.*\n+/) { actual_mesg << ' ' << $&.inspect << "+\n" } actual_mesg.sub!(/\+\n\z/, '') else actual_mesg = "to match " + mu_pp(rest) end actual_mesg << "\nafter #{i} patterns with #{actual.length - rest.length} characters" expect_msg + actual_mesg } assert false, msg end rest = match.post_match anchored = true end } if anchored assert_equal("", rest) end end def assert_warning(pat, msg = nil) result = nil stderr = EnvUtil.with_default_internal(pat.encoding) { EnvUtil.verbose_warning { result = yield } } msg = message(msg) {diff pat, stderr} assert(pat === stderr, msg) result end def assert_warn(*args) assert_warning(*args) {$VERBOSE = false; yield} end def assert_deprecated_warning(mesg = /deprecated/) assert_warning(mesg) do Warning[:deprecated] = true yield end end def assert_deprecated_warn(mesg = /deprecated/) assert_warn(mesg) do Warning[:deprecated] = true yield end end class << (AssertFile = Struct.new(:failure_message).new) include Assertions include CoreAssertions def assert_file_predicate(predicate, *args) if /\Anot_/ =~ predicate predicate = $' neg = " not" end result = File.__send__(predicate, *args) result = !result if neg mesg = "Expected file ".dup << args.shift.inspect mesg << "#{neg} to be #{predicate}" mesg << mu_pp(args).sub(/\A\[(.*)\]\z/m, '(\1)') unless args.empty? mesg << " #{failure_message}" if failure_message assert(result, mesg) end alias method_missing assert_file_predicate def for(message) clone.tap {|a| a.failure_message = message} end end class AllFailures attr_reader :failures def initialize @count = 0 @failures = {} end def for(key) @count += 1 yield rescue Exception => e @failures[key] = [@count, e] end def foreach(*keys) keys.each do |key| @count += 1 begin yield key rescue Exception => e @failures[key] = [@count, e] end end end def message i = 0 total = @count.to_s fmt = "%#{total.size}d" @failures.map {|k, (n, v)| v = v.message "\n#{i+=1}. [#{fmt%n}/#{total}] Assertion for #{k.inspect}\n#{v.b.gsub(/^/, ' | ').force_encoding(v.encoding)}" }.join("\n") end def pass? @failures.empty? end end # threads should respond to shift method. # Array can be used. def assert_join_threads(threads, message = nil) errs = [] values = [] while th = threads.shift begin values << th.value rescue Exception errs << [th, $!] th = nil end end values ensure if th&.alive? th.raise(Timeout::Error.new) th.join rescue errs << [th, $!] end if !errs.empty? msg = "exceptions on #{errs.length} threads:\n" + errs.map {|t, err| "#{t.inspect}:\n" + RUBY_VERSION >= "2.5.0" ? err.full_message(highlight: false, order: :top) : err.message }.join("\n---\n") if message msg = "#{message}\n#{msg}" end raise Test::Unit::AssertionFailedError, msg end end def assert_all?(obj, m = nil, &blk) failed = [] obj.each do |*a, &b| unless blk.call(*a, &b) failed << (a.size > 1 ? a : a[0]) end end assert(failed.empty?, message(m) {failed.pretty_inspect}) end def assert_all_assertions(msg = nil) all = AllFailures.new yield all ensure assert(all.pass?, message(msg) {all.message.chomp(".")}) end alias all_assertions assert_all_assertions def assert_all_assertions_foreach(msg = nil, *keys, &block) all = AllFailures.new all.foreach(*keys, &block) ensure assert(all.pass?, message(msg) {all.message.chomp(".")}) end alias all_assertions_foreach assert_all_assertions_foreach def message(msg = nil, *args, &default) # :nodoc: if Proc === msg super(nil, *args) do ary = [msg.call, (default.call if default)].compact.reject(&:empty?) if 1 < ary.length ary[0...-1] = ary[0...-1].map {|str| str.sub(/(? true, :UseHeader => true, :SortKeys => true ) )) rescue Psych::DisallowedClass, Psych::BadAlias, Psych::AliasesNotEnabled assert_to_yaml obj, yaml, :unsafe_load end # # Test parser only # def assert_parse_only( obj, yaml ) begin assert_equal obj, Psych::load( yaml ) rescue Psych::DisallowedClass, Psych::BadAlias, Psych::AliasesNotEnabled assert_equal obj, Psych::unsafe_load( yaml ) end assert_equal obj, Psych::parse( yaml ).transform end def assert_cycle( obj ) v = Visitors::YAMLTree.create v << obj if obj.nil? assert_nil Psych.load(v.tree.yaml) assert_nil Psych::load(Psych.dump(obj)) assert_nil Psych::load(obj.to_yaml) else begin assert_equal(obj, Psych.load(v.tree.yaml)) assert_equal(obj, Psych::load(Psych.dump(obj))) assert_equal(obj, Psych::load(obj.to_yaml)) rescue Psych::DisallowedClass, Psych::BadAlias, Psych::AliasesNotEnabled assert_equal(obj, Psych.unsafe_load(v.tree.yaml)) assert_equal(obj, Psych::unsafe_load(Psych.dump(obj))) assert_equal(obj, Psych::unsafe_load(obj.to_yaml)) end end end # # Make a time with the time zone # def mktime( year, mon, day, hour, min, sec, usec, zone = "Z" ) usec = Rational(usec.to_s) * 1000000 val = Time::utc( year.to_i, mon.to_i, day.to_i, hour.to_i, min.to_i, sec.to_i, usec ) if zone != "Z" hour = zone[0,3].to_i * 3600 min = zone[3,2].to_i * 60 ofs = (hour + min) val = Time.at( val.tv_sec - ofs, val.tv_nsec / 1000.0 ) end return val end end end # backport so that tests will run on 2.0.0 unless Tempfile.respond_to? :create def Tempfile.create(basename, *rest) tmpfile = nil Dir::Tmpname.create(basename, *rest) do |tmpname, n, opts| mode = File::RDWR|File::CREAT|File::EXCL perm = 0600 if opts mode |= opts.delete(:mode) || 0 opts[:perm] = perm perm = nil else opts = perm end tmpfile = File.open(tmpname, mode, opts) end if block_given? begin yield tmpfile ensure tmpfile.close if !tmpfile.closed? File.unlink tmpfile end else tmpfile end end end psych-5.0.2/test/psych/json/000077500000000000000000000000001436167465700157455ustar00rootroot00000000000000psych-5.0.2/test/psych/json/test_stream.rb000066400000000000000000000050301436167465700206220ustar00rootroot00000000000000# frozen_string_literal: true require 'psych/helper' module Psych module JSON class TestStream < TestCase def setup @io = StringIO.new @stream = Psych::JSON::Stream.new(@io) @stream.start end def test_explicit_documents @io = StringIO.new @stream = Psych::JSON::Stream.new(@io) @stream.start @stream.push({ 'foo' => 'bar' }) assert !@stream.finished?, 'stream not finished' @stream.finish assert @stream.finished?, 'stream finished' assert_match(/^---/, @io.string) assert_match(/\.\.\.$/, @io.string) end def test_null @stream.push(nil) assert_match(/^--- null/, @io.string) end def test_string @stream.push "foo" assert_match(/(["])foo\1/, @io.string) end def test_symbol @stream.push :foo assert_match(/(["])foo\1/, @io.string) end def test_int @stream.push 10 assert_match(/^--- 10/, @io.string) end def test_float @stream.push 1.2 assert_match(/^--- 1.2/, @io.string) end def test_hash hash = { 'one' => 'two' } @stream.push hash json = @io.string assert_match(/}$/, json) assert_match(/^--- \{/, json) assert_match(/["]one['"]/, json) assert_match(/["]two['"]/, json) end def test_list_to_json list = %w{ one two } @stream.push list json = @io.string assert_match(/\]$/, json) assert_match(/^--- \[/, json) assert_match(/["]one["]/, json) assert_match(/["]two["]/, json) end class Foo; end def test_json_dump_exclude_tag @stream << Foo.new json = @io.string refute_match('Foo', json) end class Bar def encode_with coder coder.represent_seq 'omg', %w{ a b c } end end def test_json_list_dump_exclude_tag @stream << Bar.new json = @io.string refute_match('omg', json) end def test_time time = Time.utc(2010, 10, 10) @stream.push({'a' => time }) json = @io.string assert_match "{\"a\": \"2010-10-10 00:00:00.000000000 Z\"}\n", json end def test_datetime time = Time.new(2010, 10, 10).to_datetime @stream.push({'a' => time }) json = @io.string assert_match "{\"a\": \"#{time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z")}\"}\n", json end end end end psych-5.0.2/test/psych/nodes/000077500000000000000000000000001436167465700161045ustar00rootroot00000000000000psych-5.0.2/test/psych/nodes/test_enumerable.rb000066400000000000000000000016551436167465700216160ustar00rootroot00000000000000# frozen_string_literal: true require 'psych/helper' module Psych module Nodes class TestEnumerable < TestCase def test_includes_enumerable yaml = '--- hello' assert_equal 3, Psych.parse_stream(yaml).to_a.length end def test_returns_enumerator yaml = '--- hello' assert_equal 3, Psych.parse_stream(yaml).each.map { |x| x }.length end def test_scalar assert_equal 3, calls('--- hello').length end def test_sequence assert_equal 4, calls("---\n- hello").length end def test_mapping assert_equal 5, calls("---\nhello: world").length end def test_alias assert_equal 5, calls("--- &yay\n- foo\n- *yay\n").length end private def calls yaml calls = [] Psych.parse_stream(yaml).each do |node| calls << node end calls end end end end psych-5.0.2/test/psych/test_alias_and_anchor.rb000066400000000000000000000040071436167465700216260ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' class ObjectWithInstanceVariables attr_accessor :var1, :var2 end class SubStringWithInstanceVariables < String attr_accessor :var1 end module Psych class TestAliasAndAnchor < TestCase def test_mri_compatibility yaml = < 'b' }, 'foo'] end def test_enumerator x = [1, 2, 3, 4] y = Psych.load Psych.dump x.to_enum assert_equal x, y end def test_another_subclass_with_attributes y = Y.new.tap {|o| o.val = 1} y << "foo" << "bar" y = Psych.unsafe_load Psych.dump y assert_equal %w{foo bar}, y assert_equal Y, y.class assert_equal 1, y.val end def test_subclass yaml = Psych.dump X.new assert_match X.name, yaml list = X.new list << 1 assert_equal X, list.class assert_equal 1, list.first end def test_subclass_with_attributes y = Psych.unsafe_load Psych.dump Y.new.tap {|o| o.val = 1} assert_equal Y, y.class assert_equal 1, y.val end def test_backwards_with_syck x = Psych.unsafe_load "--- !seq:#{X.name} []\n\n" assert_equal X, x.class end def test_self_referential @list << @list assert_cycle(@list) end def test_recursive_array @list << @list loaded = Psych.load(Psych.dump(@list), aliases: true) assert_same loaded, loaded.last end def test_recursive_array_uses_alias @list << @list assert_raise(AliasesNotEnabled) do Psych.load(Psych.dump(@list), aliases: false) end end def test_cycle assert_cycle(@list) end end end psych-5.0.2/test/psych/test_boolean.rb000066400000000000000000000017261436167465700200050ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych ### # Test booleans from YAML spec: # http://yaml.org/type/bool.html class TestBoolean < TestCase %w{ yes Yes YES true True TRUE on On ON }.each do |truth| define_method(:"test_#{truth}") do assert_equal true, Psych.load("--- #{truth}") end end %w{ no No NO false False FALSE off Off OFF }.each do |truth| define_method(:"test_#{truth}") do assert_equal false, Psych.load("--- #{truth}") end end ### # YAML spec says "y" and "Y" may be used as true, but Syck treats them # as literal strings def test_y assert_equal "y", Psych.load("--- y") assert_equal "Y", Psych.load("--- Y") end ### # YAML spec says "n" and "N" may be used as false, but Syck treats them # as literal strings def test_n assert_equal "n", Psych.load("--- n") assert_equal "N", Psych.load("--- N") end end end psych-5.0.2/test/psych/test_class.rb000066400000000000000000000011321436167465700174620ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestClass < TestCase module Foo end def test_cycle_anonymous_class assert_raise(::TypeError) do assert_cycle(Class.new) end end def test_cycle_anonymous_module assert_raise(::TypeError) do assert_cycle(Module.new) end end def test_cycle assert_cycle(TestClass) end def test_dump Psych.dump TestClass end def test_cycle_module assert_cycle(Foo) end def test_dump_module Psych.dump Foo end end end psych-5.0.2/test/psych/test_coder.rb000066400000000000000000000201621436167465700174550ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestCoder < TestCase class InitApi attr_accessor :implicit attr_accessor :style attr_accessor :tag attr_accessor :a, :b, :c def initialize @a = 1 @b = 2 @c = 3 end def init_with coder @a = coder['aa'] @b = coder['bb'] @implicit = coder.implicit @tag = coder.tag @style = coder.style end def encode_with coder coder['aa'] = @a coder['bb'] = @b end end class TaggingCoder < InitApi def encode_with coder super coder.tag = coder.tag.sub(/!/, '!hello') coder.implicit = false coder.style = Psych::Nodes::Mapping::FLOW end end class ScalarCoder def encode_with coder coder.scalar = "foo" end end class Represent yaml_tag 'foo' def encode_with coder coder.represent_scalar 'foo', 'bar' end end class RepresentWithInit yaml_tag name attr_accessor :str def init_with coder @str = coder.scalar end def encode_with coder coder.represent_scalar self.class.name, 'bar' end end class RepresentWithSeq yaml_tag name attr_accessor :seq def init_with coder @seq = coder.seq end def encode_with coder coder.represent_seq self.class.name, %w{ foo bar } end end class RepresentWithMap yaml_tag name attr_accessor :map def init_with coder @map = coder.map end def encode_with coder coder.represent_map self.class.name, { "string" => 'a', :symbol => 'b' } end end class RepresentWithObject def encode_with coder coder.represent_object self.class.name, 20 end end class Referential attr_reader :a def initialize @a = self end def encode_with(c) c['a'] = @a end def init_with(c) @a = c['a'] end end class CustomEncode def initialize(**opts) @opts = opts end def encode_with(coder) @opts.each { |k,v| coder.public_send :"#{k}=", v } end end def test_self_referential x = Referential.new copy = Psych.unsafe_load Psych.dump x assert_equal copy, copy.a end def test_represent_with_object thing = Psych.load(Psych.dump(RepresentWithObject.new)) assert_equal 20, thing end def test_json_dump_exclude_tag refute_match('TestCoder::InitApi', Psych.to_json(InitApi.new)) end def test_map_takes_block coder = Psych::Coder.new 'foo' tag = coder.tag style = coder.style coder.map { |map| map.add 'foo', 'bar' } assert_equal 'bar', coder['foo'] assert_equal tag, coder.tag assert_equal style, coder.style end def test_map_with_tag coder = Psych::Coder.new 'foo' coder.map('hello') { |map| map.add 'foo', 'bar' } assert_equal 'bar', coder['foo'] assert_equal 'hello', coder.tag end def test_map_with_tag_and_style coder = Psych::Coder.new 'foo' coder.map('hello', 'world') { |map| map.add 'foo', 'bar' } assert_equal 'bar', coder['foo'] assert_equal 'hello', coder.tag assert_equal 'world', coder.style end def test_represent_map thing = Psych.unsafe_load(Psych.dump(RepresentWithMap.new)) assert_equal({ "string" => 'a', :symbol => 'b' }, thing.map) end def test_represent_sequence thing = Psych.unsafe_load(Psych.dump(RepresentWithSeq.new)) assert_equal %w{ foo bar }, thing.seq end def test_represent_with_init thing = Psych.unsafe_load(Psych.dump(RepresentWithInit.new)) assert_equal 'bar', thing.str end def test_represent! assert_match(/foo/, Psych.dump(Represent.new)) assert_instance_of(Represent, Psych.unsafe_load(Psych.dump(Represent.new))) end def test_scalar_coder foo = Psych.load(Psych.dump(ScalarCoder.new)) assert_equal 'foo', foo end def test_load_dumped_tagging foo = InitApi.new bar = Psych.unsafe_load(Psych.dump(foo)) assert_equal false, bar.implicit assert_equal "!ruby/object:Psych::TestCoder::InitApi", bar.tag assert_equal Psych::Nodes::Mapping::BLOCK, bar.style end def test_dump_with_tag foo = TaggingCoder.new assert_match(/hello/, Psych.dump(foo)) assert_match(/\{aa/, Psych.dump(foo)) end def test_dump_encode_with foo = InitApi.new assert_match(/aa/, Psych.dump(foo)) end def test_dump_init_with foo = InitApi.new bar = Psych.unsafe_load(Psych.dump(foo)) assert_equal foo.a, bar.a assert_equal foo.b, bar.b assert_nil bar.c end def test_coder_style_map_default foo = Psych.dump a: 1, b: 2 assert_equal "---\n:a: 1\n:b: 2\n", foo end def test_coder_style_map_any pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ foo = Psych.dump CustomEncode.new \ map: {a: 1, b: 2}, style: Psych::Nodes::Mapping::ANY, tag: nil assert_equal "---\n:a: 1\n:b: 2\n", foo end def test_coder_style_map_block pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ foo = Psych.dump CustomEncode.new \ map: {a: 1, b: 2}, style: Psych::Nodes::Mapping::BLOCK, tag: nil assert_equal "---\n:a: 1\n:b: 2\n", foo end def test_coder_style_map_flow pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ foo = Psych.dump CustomEncode.new \ map: { a: 1, b: 2 }, style: Psych::Nodes::Mapping::FLOW, tag: nil assert_equal "--- {! ':a': 1, ! ':b': 2}\n", foo end def test_coder_style_seq_default foo = Psych.dump [ 1, 2, 3 ] assert_equal "---\n- 1\n- 2\n- 3\n", foo end def test_coder_style_seq_any foo = Psych.dump CustomEncode.new \ seq: [ 1, 2, 3 ], style: Psych::Nodes::Sequence::ANY, tag: nil assert_equal "---\n- 1\n- 2\n- 3\n", foo end def test_coder_style_seq_block foo = Psych.dump CustomEncode.new \ seq: [ 1, 2, 3 ], style: Psych::Nodes::Sequence::BLOCK, tag: nil assert_equal "---\n- 1\n- 2\n- 3\n", foo end def test_coder_style_seq_flow foo = Psych.dump CustomEncode.new \ seq: [ 1, 2, 3 ], style: Psych::Nodes::Sequence::FLOW, tag: nil assert_equal "--- [1, 2, 3]\n", foo end def test_coder_style_scalar_default foo = Psych.dump 'some scalar' assert_match(/\A--- some scalar\n(?:\.\.\.\n)?\z/, foo) end def test_coder_style_scalar_any foo = Psych.dump CustomEncode.new \ scalar: 'some scalar', style: Psych::Nodes::Scalar::ANY, tag: nil assert_match(/\A--- some scalar\n(?:\.\.\.\n)?\z/, foo) end def test_coder_style_scalar_plain foo = Psych.dump CustomEncode.new \ scalar: 'some scalar', style: Psych::Nodes::Scalar::PLAIN, tag: nil assert_match(/\A--- some scalar\n(?:\.\.\.\n)?\z/, foo) end def test_coder_style_scalar_single_quoted foo = Psych.dump CustomEncode.new \ scalar: 'some scalar', style: Psych::Nodes::Scalar::SINGLE_QUOTED, tag: nil assert_equal "--- ! 'some scalar'\n", foo end def test_coder_style_scalar_double_quoted foo = Psych.dump CustomEncode.new \ scalar: 'some scalar', style: Psych::Nodes::Scalar::DOUBLE_QUOTED, tag: nil assert_equal %Q'--- ! "some scalar"\n', foo end def test_coder_style_scalar_literal foo = Psych.dump CustomEncode.new \ scalar: 'some scalar', style: Psych::Nodes::Scalar::LITERAL, tag: nil assert_equal "--- ! |-\n some scalar\n", foo end def test_coder_style_scalar_folded foo = Psych.dump CustomEncode.new \ scalar: 'some scalar', style: Psych::Nodes::Scalar::FOLDED, tag: nil assert_equal "--- ! >-\n some scalar\n", foo end end end psych-5.0.2/test/psych/test_date_time.rb000066400000000000000000000042741436167465700203220ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' require 'date' module Psych class TestDateTime < TestCase def test_negative_year time = Time.utc(-1, 12, 16) assert_cycle time end def test_usec time = Time.utc(2017, 4, 13, 12, 0, 0, 5) assert_cycle time end def test_non_utc time = Time.new(2017, 4, 13, 12, 0, 0.5, "+09:00") assert_cycle time end def test_timezone_offset times = [Time.new(2017, 4, 13, 12, 0, 0, "+09:00"), Time.new(2017, 4, 13, 12, 0, 0, "-05:00")] cycled = Psych::unsafe_load(Psych.dump times) assert_match(/12:00:00 \+0900/, cycled.first.to_s) assert_match(/12:00:00 -0500/, cycled.last.to_s) end def test_new_datetime assert_cycle DateTime.new end def test_datetime_non_utc dt = DateTime.new(2017, 4, 13, 12, 0, 0.5, "+09:00") assert_cycle dt end def test_datetime_timezone_offset times = [DateTime.new(2017, 4, 13, 12, 0, 0, "+09:00"), DateTime.new(2017, 4, 13, 12, 0, 0, "-05:00")] cycled = Psych::unsafe_load(Psych.dump times) assert_match(/12:00:00\+09:00/, cycled.first.to_s) assert_match(/12:00:00-05:00/, cycled.last.to_s) end def test_julian_date d = Date.new(1582, 10, 4, Date::GREGORIAN) assert_cycle d end def test_proleptic_gregorian_date d = Date.new(1582, 10, 14, Date::GREGORIAN) assert_cycle d end def test_julian_datetime dt = DateTime.new(1582, 10, 4, 23, 58, 59, 0, Date::GREGORIAN) assert_cycle dt end def test_proleptic_gregorian_datetime dt = DateTime.new(1582, 10, 14, 23, 58, 59, 0, Date::GREGORIAN) assert_cycle dt end def test_invalid_date assert_cycle "2013-10-31T10:40:07-000000000000033" end def test_string_tag dt = DateTime.now yaml = Psych.dump dt assert_match(/DateTime/, yaml) end def test_round_trip dt = DateTime.now assert_cycle dt end def test_alias_with_time t = Time.now h = {:a => t, :b => t} yaml = Psych.dump h assert_match('&', yaml) assert_match('*', yaml) end end end psych-5.0.2/test/psych/test_deprecated.rb000066400000000000000000000040741436167465700204650ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestDeprecated < TestCase def teardown $VERBOSE = @orig_verbose Psych.domain_types.clear end class QuickEmitter; end def setup @orig_verbose, $VERBOSE = $VERBOSE, false end class QuickEmitterEncodeWith attr_reader :name attr_reader :value def initialize @name = 'hello!!' @value = 'Friday!' end def encode_with coder coder.map do |map| map.add 'name', @name map.add 'value', nil end end def to_yaml opts = {} raise end end ### # An object that defines both to_yaml and encode_with should only call # encode_with. def test_recursive_quick_emit_encode_with qeew = QuickEmitterEncodeWith.new hash = { :qe => qeew } hash2 = Psych.unsafe_load Psych.dump hash qe = hash2[:qe] assert_equal qeew.name, qe.name assert_instance_of QuickEmitterEncodeWith, qe assert_nil qe.value end class YamlInitAndInitWith attr_reader :name attr_reader :value def initialize @name = 'shaners' @value = 'Friday!' end def init_with coder coder.map.each { |ivar, val| instance_variable_set "@#{ivar}", 'TGIF!' } end def yaml_initialize tag, vals raise end end ### # An object that implements both yaml_initialize and init_with should not # receive the yaml_initialize call. def test_yaml_initialize_and_init_with hash = { :yi => YamlInitAndInitWith.new } hash2 = Psych.unsafe_load Psych.dump hash yi = hash2[:yi] assert_equal 'TGIF!', yi.name assert_equal 'TGIF!', yi.value assert_instance_of YamlInitAndInitWith, yi end def test_coder_scalar coder = Psych::Coder.new 'foo' coder.scalar('tag', 'some string', :plain) assert_equal 'tag', coder.tag assert_equal 'some string', coder.scalar assert_equal :scalar, coder.type end end end psych-5.0.2/test/psych/test_document.rb000066400000000000000000000020341436167465700201750ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestDocument < TestCase def setup super @stream = Psych.parse_stream(<<-eoyml) %YAML 1.1 %TAG ! tag:tenderlovemaking.com,2009: --- !fun eoyml @doc = @stream.children.first end def test_parse_tag assert_equal([['!', 'tag:tenderlovemaking.com,2009:']], @doc.tag_directives) end def test_emit_tag assert_match('%TAG ! tag:tenderlovemaking.com,2009:', @stream.yaml) end def test_emit_multitag @doc.tag_directives << ['!!', 'foo.com,2009:'] yaml = @stream.yaml assert_match('%TAG ! tag:tenderlovemaking.com,2009:', yaml) assert_match('%TAG !! foo.com,2009:', yaml) end def test_emit_bad_tag assert_raise(RuntimeError) do @doc.tag_directives = [['!']] @stream.yaml end end def test_parse_version assert_equal([1,1], @doc.version) end def test_emit_version assert_match('%YAML 1.1', @stream.yaml) end end end psych-5.0.2/test/psych/test_emitter.rb000066400000000000000000000053361436167465700200400ustar00rootroot00000000000000# -*- coding: utf-8 -*- # frozen_string_literal: true require_relative 'helper' module Psych class TestEmitter < TestCase def setup super @out = StringIO.new(''.dup) @emitter = Psych::Emitter.new @out end def test_line_width @emitter.line_width = 10 assert_equal 10, @emitter.line_width end def test_set_canonical @emitter.canonical = true assert_equal true, @emitter.canonical @emitter.canonical = false assert_equal false, @emitter.canonical end def test_indentation_set assert_equal 2, @emitter.indentation @emitter.indentation = 5 assert_equal 5, @emitter.indentation end def test_emit_utf_8 @emitter.start_stream Psych::Nodes::Stream::UTF8 @emitter.start_document [], [], false @emitter.scalar '日本語', nil, nil, false, true, 1 @emitter.end_document true @emitter.end_stream assert_match('日本語', @out.string) end def test_start_stream_arg_error assert_raise(TypeError) do @emitter.start_stream 'asdfasdf' end end def test_start_doc_arg_error @emitter.start_stream Psych::Nodes::Stream::UTF8 [ [nil, [], false], [[nil, nil], [], false], [[], 'foo', false], [[], ['foo'], false], [[], [nil,nil], false], [[1,1], [[nil, "tag:TALOS"]], 0], ].each do |args| assert_raise(TypeError) do @emitter.start_document(*args) end end end def test_scalar_arg_error @emitter.start_stream Psych::Nodes::Stream::UTF8 @emitter.start_document [], [], false [ [:foo, nil, nil, false, true, 1], ['foo', Object.new, nil, false, true, 1], ['foo', nil, Object.new, false, true, 1], ['foo', nil, nil, false, true, :foo], [nil, nil, nil, false, true, 1], ].each do |args| assert_raise(TypeError) do @emitter.scalar(*args) end end end def test_start_sequence_arg_error @emitter.start_stream Psych::Nodes::Stream::UTF8 @emitter.start_document [], [], false assert_raise(TypeError) do @emitter.start_sequence(nil, Object.new, true, 1) end assert_raise(TypeError) do @emitter.start_sequence(nil, nil, true, :foo) end end def test_resizing_tags @emitter.start_stream Psych::Nodes::Stream::UTF8 tags = [] version = [1,1] obj = Object.new obj.instance_variable_set(:@tags, tags) def obj.to_str (1..10).map{|x| @tags.push(["AAAA","BBBB"])} return "x" end tags.push([obj, "tag:TALOS"]) @emitter.start_document(version, tags, 0) assert(true) end end end psych-5.0.2/test/psych/test_encoding.rb000066400000000000000000000164551436167465700201610ustar00rootroot00000000000000# -*- coding: utf-8 -*- # frozen_string_literal: true require_relative 'helper' module Psych class TestEncoding < TestCase class EncodingCatcher < Handler attr_reader :strings def initialize @strings = [] end (Handler.instance_methods(true) - Object.instance_methods).each do |m| class_eval <<~RUBY, __FILE__, __LINE__ + 1 def #{m} *args @strings += args.flatten.find_all { |a| String === a } end RUBY end end def setup super @buffer = StringIO.new @handler = EncodingCatcher.new @parser = Psych::Parser.new @handler @utf8 = Encoding.find('UTF-8') @emitter = Psych::Emitter.new @buffer end def test_dump_load_encoding_object assert_cycle Encoding::US_ASCII assert_cycle Encoding::UTF_8 end def test_transcode_shiftjis str = "こんにちは!" loaded = Psych.load("--- こんにちは!".encode('SHIFT_JIS')) assert_equal str, loaded end def test_transcode_utf16le str = "こんにちは!" loaded = Psych.load("--- こんにちは!".encode('UTF-16LE')) assert_equal str, loaded end def test_transcode_utf16be str = "こんにちは!" loaded = Psych.load("--- こんにちは!".encode('UTF-16BE')) assert_equal str, loaded end def test_io_shiftjis Tempfile.create(['shiftjis', 'yml'], :encoding => 'SHIFT_JIS') {|t| t.write '--- こんにちは!' t.close # If the external encoding isn't utf8, utf16le, or utf16be, we cannot # process the file. File.open(t.path, 'r', :encoding => 'SHIFT_JIS') do |f| assert_raise Psych::SyntaxError do Psych.load(f) end end } end def test_io_utf16le Tempfile.create(['utf16le', 'yml']) {|t| t.binmode t.write '--- こんにちは!'.encode('UTF-16LE') t.close File.open(t.path, 'rb', :encoding => 'UTF-16LE') do |f| assert_equal "こんにちは!", Psych.load(f) end } end def test_io_utf16be Tempfile.create(['utf16be', 'yml']) {|t| t.binmode t.write '--- こんにちは!'.encode('UTF-16BE') t.close File.open(t.path, 'rb', :encoding => 'UTF-16BE') do |f| assert_equal "こんにちは!", Psych.load(f) end } end def test_io_utf8 Tempfile.create(['utf8', 'yml']) {|t| t.binmode t.write '--- こんにちは!'.encode('UTF-8') t.close File.open(t.path, 'rb', :encoding => 'UTF-8') do |f| assert_equal "こんにちは!", Psych.load(f) end } end def test_io_utf8_read_as_binary Tempfile.create(['utf8', 'yml']) {|t| t.binmode t.write '--- こんにちは!'.encode('UTF-8') t.close File.open(t.path, 'rb', :encoding => 'ascii-8bit') do |f| assert_equal "こんにちは!", Psych.load(f) end } end def test_emit_alias pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ @emitter.start_stream Psych::Parser::UTF8 @emitter.start_document [], [], true e = assert_raise(RuntimeError) do @emitter.alias 'ドラえもん'.encode('EUC-JP') end assert_match(/alias value/, e.message) end def test_to_yaml_is_valid with_default_external(Encoding::US_ASCII) do with_default_internal(nil) do s = "こんにちは!" # If no encoding is specified, use UTF-8 assert_equal Encoding::UTF_8, Psych.dump(s).encoding assert_equal s, Psych.load(Psych.dump(s)) end end end def test_start_mapping foo = 'foo' bar = 'バー' @emitter.start_stream Psych::Parser::UTF8 @emitter.start_document [], [], true @emitter.start_mapping( foo.encode('Shift_JIS'), bar.encode('UTF-16LE'), false, Nodes::Sequence::ANY) @emitter.end_mapping @emitter.end_document false @emitter.end_stream pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ @parser.parse @buffer.string assert_encodings @utf8, @handler.strings assert_equal [foo, bar], @handler.strings end def test_start_sequence foo = 'foo' bar = 'バー' @emitter.start_stream Psych::Parser::UTF8 @emitter.start_document [], [], true @emitter.start_sequence( foo.encode('Shift_JIS'), bar.encode('UTF-16LE'), false, Nodes::Sequence::ANY) @emitter.end_sequence @emitter.end_document false @emitter.end_stream pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ @parser.parse @buffer.string assert_encodings @utf8, @handler.strings assert_equal [foo, bar], @handler.strings end def test_doc_tag_encoding key = '鍵' @emitter.start_stream Psych::Parser::UTF8 @emitter.start_document( [1, 1], [['!'.encode('EUC-JP'), key.encode('EUC-JP')]], true ) @emitter.scalar 'foo', nil, nil, true, false, Nodes::Scalar::ANY @emitter.end_document false @emitter.end_stream pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ @parser.parse @buffer.string assert_encodings @utf8, @handler.strings assert_equal key, @handler.strings[1] end def test_emitter_encoding str = "壁に耳あり、障子に目あり" thing = Psych.load Psych.dump str.encode('EUC-JP') assert_equal str, thing end def test_default_internal with_default_internal(Encoding::EUC_JP) do str = "壁に耳あり、障子に目あり" assert_equal @utf8, str.encoding @parser.parse str assert_encodings Encoding::EUC_JP, @handler.strings assert_equal str, @handler.strings.first.encode('UTF-8') end end def test_scalar @parser.parse("--- a") assert_encodings @utf8, @handler.strings end def test_alias @parser.parse(<<-eoyml) %YAML 1.1 --- !!seq [ !!str "Without properties", &A !!str "Anchored", !!str "Tagged", *A, !!str "", ] eoyml assert_encodings @utf8, @handler.strings end def test_list_anchor list = %w{ a b } list << list @parser.parse(Psych.dump(list)) assert_encodings @utf8, @handler.strings end def test_map_anchor h = {} h['a'] = h @parser.parse(Psych.dump(h)) assert_encodings @utf8, @handler.strings end def test_map_tag @parser.parse(<<-eoyml) %YAML 1.1 --- !!map { a : b } eoyml assert_encodings @utf8, @handler.strings end def test_doc_tag @parser.parse(<<-eoyml) %YAML 1.1 %TAG ! tag:tenderlovemaking.com,2009: --- !fun eoyml assert_encodings @utf8, @handler.strings end def test_dump_non_ascii_string_to_file pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ Tempfile.create(['utf8', 'yml'], :encoding => 'UTF-8') do |t| h = {'one' => 'いち'} Psych.dump(h, t) t.close assert_equal h, Psych.load_file(t.path) end end private def assert_encodings encoding, strings strings.each do |str| assert_equal encoding, str.encoding, str end end end end psych-5.0.2/test/psych/test_exception.rb000066400000000000000000000100751436167465700203610ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestException < TestCase class Wups < Exception attr_reader :foo, :bar def initialize *args super @foo = 1 @bar = 2 end end def setup super @wups = Wups.new @orig_verbose, $VERBOSE = $VERBOSE, nil end def teardown $VERBOSE = @orig_verbose end def make_ex msg = 'oh no!' begin raise msg rescue ::Exception => e e end end def test_backtrace err = make_ex new_err = Psych.unsafe_load(Psych.dump(err)) assert_equal err.backtrace, new_err.backtrace end def test_naming_exception err = String.xxx rescue $! new_err = Psych.unsafe_load(Psych.dump(err)) assert_equal err.message, new_err.message end def test_load_takes_file ex = assert_raise(Psych::SyntaxError) do Psych.load '--- `' end assert_nil ex.file ex = assert_raise(Psych::SyntaxError) do Psych.load '--- `', filename: 'meow' end assert_equal 'meow', ex.file end def test_psych_parse_stream_takes_file ex = assert_raise(Psych::SyntaxError) do Psych.parse_stream '--- `' end assert_nil ex.file assert_match '()', ex.message ex = assert_raise(Psych::SyntaxError) do Psych.parse_stream '--- `', filename: 'omg!' end assert_equal 'omg!', ex.file assert_match 'omg!', ex.message end def test_load_stream_takes_file ex = assert_raise(Psych::SyntaxError) do Psych.load_stream '--- `' end assert_nil ex.file assert_match '()', ex.message ex = assert_raise(Psych::SyntaxError) do Psych.load_stream '--- `', filename: 'omg!' end assert_equal 'omg!', ex.file end def test_parse_file_exception Tempfile.create(['parsefile', 'yml']) {|t| t.binmode t.write '--- `' t.close ex = assert_raise(Psych::SyntaxError) do Psych.parse_file t.path end assert_equal t.path, ex.file } end def test_load_file_exception Tempfile.create(['loadfile', 'yml']) {|t| t.binmode t.write '--- `' t.close ex = assert_raise(Psych::SyntaxError) do Psych.load_file t.path end assert_equal t.path, ex.file } end def test_safe_load_file_exception Tempfile.create(['loadfile', 'yml']) {|t| t.binmode t.write '--- `' t.close ex = assert_raise(Psych::SyntaxError) do Psych.safe_load_file t.path end assert_equal t.path, ex.file } end def test_psych_parse_takes_file ex = assert_raise(Psych::SyntaxError) do Psych.parse '--- `' end assert_match '()', ex.message assert_nil ex.file ex = assert_raise(Psych::SyntaxError) do Psych.parse '--- `', filename: 'omg!' end assert_match 'omg!', ex.message end def test_attributes e = assert_raise(Psych::SyntaxError) { Psych.load '--- `foo' } assert_nil e.file assert_equal 1, e.line assert_equal 5, e.column # FIXME: offset isn't being set correctly by libyaml # assert_equal 5, e.offset assert e.problem assert e.context end def test_convert w = Psych.unsafe_load(Psych.dump(@wups)) assert_equal @wups.message, w.message assert_equal @wups.backtrace, w.backtrace assert_equal 1, w.foo assert_equal 2, w.bar end def test_psych_syntax_error Tempfile.create(['parsefile', 'yml']) do |t| t.binmode t.write '--- `' t.close begin Psych.parse_file t.path rescue StandardError assert true # count assertion ensure return unless $! ancestors = $!.class.ancestors.inspect flunk "Psych::SyntaxError not rescued by StandardError: #{ancestors}" end end end end end psych-5.0.2/test/psych/test_hash.rb000066400000000000000000000073311436167465700173070ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestHash < TestCase class X < Hash end class HashWithIvar < Hash def initialize @keys = [] super end def []=(k, v) @keys << k super(k, v) end end class HashWithCustomInit < Hash attr_reader :obj def initialize(obj) @obj = obj end end class HashWithCustomInitNoIvar < Hash def initialize(obj) # *shrug* end end def setup super @hash = { :a => 'b' } end def test_hash_with_ivar t1 = HashWithIvar.new t1[:foo] = :bar t2 = Psych.unsafe_load(Psych.dump(t1)) assert_equal t1, t2 assert_cycle t1 end def test_referenced_hash_with_ivar a = [1,2,3,4,5] t1 = [HashWithCustomInit.new(a)] t1 << t1.first assert_cycle t1 end def test_custom_initialized a = [1,2,3,4,5] t1 = HashWithCustomInit.new(a) t2 = Psych.unsafe_load(Psych.dump(t1)) assert_equal t1, t2 assert_cycle t1 end def test_custom_initialize_no_ivar t1 = HashWithCustomInitNoIvar.new(nil) t2 = Psych.unsafe_load(Psych.dump(t1)) assert_equal t1, t2 assert_cycle t1 end def test_hash_subclass_with_ivars x = X.new x[:a] = 'b' x.instance_variable_set :@foo, 'bar' dup = Psych.unsafe_load Psych.dump x assert_cycle x assert_equal 'bar', dup.instance_variable_get(:@foo) assert_equal X, dup.class end def test_load_with_class_syck_compatibility hash = Psych.unsafe_load "--- !ruby/object:Hash\n:user_id: 7\n:username: Lucas\n" assert_equal({ user_id: 7, username: 'Lucas'}, hash) end def test_empty_subclass assert_match "!ruby/hash:#{X}", Psych.dump(X.new) x = Psych.unsafe_load Psych.dump X.new assert_equal X, x.class end def test_map x = Psych.unsafe_load "--- !map:#{X} { }\n" assert_equal X, x.class end def test_self_referential @hash['self'] = @hash assert_cycle(@hash) end def test_cycles assert_cycle(@hash) end def test_ref_append hash = Psych.unsafe_load(<<~eoyml) --- foo: &foo hello: world bar: <<: *foo eoyml assert_equal({"foo"=>{"hello"=>"world"}, "bar"=>{"hello"=>"world"}}, hash) end def test_anchor_reuse hash = Psych.unsafe_load(<<~eoyml) --- foo: &foo hello: world bar: *foo eoyml assert_equal({"foo"=>{"hello"=>"world"}, "bar"=>{"hello"=>"world"}}, hash) assert_same(hash.fetch("foo"), hash.fetch("bar")) end def test_raises_if_anchor_not_defined assert_raise(Psych::AnchorNotDefined) do Psych.unsafe_load(<<~eoyml) --- foo: &foo hello: world bar: *not_foo eoyml end end def test_recursive_hash h = { } h["recursive_reference"] = h loaded = Psych.load(Psych.dump(h), aliases: true) assert_same loaded, loaded.fetch("recursive_reference") end def test_recursive_hash_uses_alias h = { } h["recursive_reference"] = h assert_raise(AliasesNotEnabled) do Psych.load(Psych.dump(h), aliases: false) end end def test_key_deduplication unless String.method_defined?(:-@) && (-("a" * 20)).equal?((-("a" * 20))) pend "This Ruby implementation doesn't support string deduplication" end hashes = Psych.load(<<~eoyml) --- - unique_identifier: 1 - unique_identifier: 2 eoyml assert_same hashes[0].keys.first, hashes[1].keys.first end end end psych-5.0.2/test/psych/test_json_tree.rb000066400000000000000000000027701436167465700203560ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestJSONTree < TestCase def test_string assert_match(/"foo"/, Psych.to_json("foo")) end def test_symbol assert_match(/"foo"/, Psych.to_json(:foo)) end def test_nil assert_match(/^null/, Psych.to_json(nil)) end def test_int assert_match(/^10/, Psych.to_json(10)) end def test_float assert_match(/^1.2/, Psych.to_json(1.2)) end def test_hash hash = { 'one' => 'two' } json = Psych.to_json(hash) assert_match(/}$/, json) assert_match(/^\{/, json) assert_match(/['"]one['"]/, json) assert_match(/['"]two['"]/, json) end class Bar def encode_with coder coder.represent_seq 'omg', %w{ a b c } end end def test_json_list_dump_exclude_tag json = Psych.to_json Bar.new refute_match('omg', json) end def test_list_to_json list = %w{ one two } json = Psych.to_json(list) assert_match(/\]$/, json) assert_match(/^\[/, json) assert_match(/"one"/, json) assert_match(/"two"/, json) end def test_time time = Time.utc(2010, 10, 10) assert_equal "{\"a\": \"2010-10-10 00:00:00.000000000 Z\"}\n", Psych.to_json({'a' => time }) end def test_datetime time = Time.new(2010, 10, 10).to_datetime assert_equal "{\"a\": \"#{time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z")}\"}\n", Psych.to_json({'a' => time }) end end end psych-5.0.2/test/psych/test_marshalable.rb000066400000000000000000000026161436167465700206400ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' require 'delegate' module Psych class TestMarshalable < TestCase def test_objects_defining_marshal_dump_and_marshal_load_can_be_dumped sd = SimpleDelegator.new(1) loaded = Psych.unsafe_load(Psych.dump(sd)) assert_instance_of(SimpleDelegator, loaded) assert_equal(sd, loaded) end class PsychCustomMarshalable < BasicObject attr_reader :foo def initialize(foo) @foo = foo end def marshal_dump [foo] end def mashal_load(data) @foo = data[0] end def init_with(coder) @foo = coder['foo'] end def encode_with(coder) coder['foo'] = 2 end def respond_to?(method) [:marshal_dump, :marshal_load, :init_with, :encode_with].include?(method) end def class PsychCustomMarshalable end end def test_init_with_takes_priority_over_marshal_methods obj = PsychCustomMarshalable.new(1) loaded = Psych.unsafe_load(Psych.dump(obj)) assert(PsychCustomMarshalable === loaded) assert_equal(2, loaded.foo) end def test_init_symbolize_names obj = PsychCustomMarshalable.new(1) loaded = Psych.unsafe_load(Psych.dump(obj), symbolize_names: true) assert(PsychCustomMarshalable === loaded) assert_equal(2, loaded.foo) end end end psych-5.0.2/test/psych/test_merge_keys.rb000066400000000000000000000072131436167465700205150ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestMergeKeys < TestCase class Product attr_reader :bar end def test_merge_key_with_bare_hash doc = Psych.load <<-eodoc map: <<: hello: world eodoc hash = { "map" => { "hello" => "world" } } assert_equal hash, doc end def test_merge_key_with_bare_hash_symbolized_names doc = Psych.load <<-eodoc, symbolize_names: true map: <<: hello: world eodoc hash = { map: { hello: "world" } } assert_equal hash, doc end def test_roundtrip_with_chevron_key h = {} v = { 'a' => h, '<<' => h } assert_cycle v end def test_explicit_string doc = Psych.unsafe_load <<-eoyml a: &me { hello: world } b: { !!str '<<': *me } eoyml expected = { "a" => { "hello" => "world" }, "b" => { "<<" => { "hello" => "world" } } } assert_equal expected, doc end def test_mergekey_with_object s = <<-eoyml foo: &foo bar: 10 product: !ruby/object:#{Product.name} <<: *foo eoyml hash = Psych.unsafe_load s assert_equal({"bar" => 10}, hash["foo"]) product = hash["product"] assert_equal 10, product.bar end def test_merge_nil yaml = <<-eoyml defaults: &defaults development: <<: *defaults eoyml assert_equal({'<<' => nil }, Psych.unsafe_load(yaml)['development']) end def test_merge_array yaml = <<-eoyml foo: &hello - 1 baz: <<: *hello eoyml assert_equal({'<<' => [1]}, Psych.unsafe_load(yaml)['baz']) end def test_merge_is_not_partial yaml = <<-eoyml default: &default hello: world foo: &hello - 1 baz: <<: [*hello, *default] eoyml doc = Psych.unsafe_load yaml refute doc['baz'].key? 'hello' assert_equal({'<<' => [[1], {"hello"=>"world"}]}, Psych.unsafe_load(yaml)['baz']) end def test_merge_seq_nil yaml = <<-eoyml foo: &hello baz: <<: [*hello] eoyml assert_equal({'<<' => [nil]}, Psych.unsafe_load(yaml)['baz']) end def test_bad_seq_merge yaml = <<-eoyml defaults: &defaults [1, 2, 3] development: <<: *defaults eoyml assert_equal({'<<' => [1,2,3]}, Psych.unsafe_load(yaml)['development']) end def test_missing_merge_key yaml = <<-eoyml bar: << : *foo eoyml exp = assert_raise(Psych::AnchorNotDefined) { Psych.load(yaml, aliases: true) } assert_match 'foo', exp.message end # [ruby-core:34679] def test_merge_key yaml = <<-eoyml foo: &foo hello: world bar: << : *foo baz: boo eoyml hash = { "foo" => { "hello" => "world"}, "bar" => { "hello" => "world", "baz" => "boo" } } assert_equal hash, Psych.unsafe_load(yaml) end def test_multiple_maps yaml = <<-eoyaml --- - &CENTER { x: 1, y: 2 } - &LEFT { x: 0, y: 2 } - &BIG { r: 10 } - &SMALL { r: 1 } # All the following maps are equal: - # Merge multiple maps << : [ *CENTER, *BIG ] label: center/big eoyaml hash = { 'x' => 1, 'y' => 2, 'r' => 10, 'label' => 'center/big' } assert_equal hash, Psych.unsafe_load(yaml)[4] end def test_override yaml = <<-eoyaml --- - &CENTER { x: 1, y: 2 } - &LEFT { x: 0, y: 2 } - &BIG { r: 10 } - &SMALL { r: 1 } # All the following maps are equal: - # Override << : [ *BIG, *LEFT, *SMALL ] x: 1 label: center/big eoyaml hash = { 'x' => 1, 'y' => 2, 'r' => 10, 'label' => 'center/big' } assert_equal hash, Psych.unsafe_load(yaml)[4] end end end psych-5.0.2/test/psych/test_nil.rb000066400000000000000000000006021436167465700171400ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestNil < TestCase def test_nil yml = Psych.dump nil assert_match(/---[ ]?\n(?:\.\.\.\n)?/, yml) assert_nil Psych.load(yml) end def test_array_nil yml = Psych.dump [nil] assert_match(/---\n-[ ]?\n/, yml) assert_equal [nil], Psych.load(yml) end end end psych-5.0.2/test/psych/test_null.rb000066400000000000000000000004601436167465700173320ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych ### # Test null from YAML spec: # http://yaml.org/type/null.html class TestNull < TestCase def test_null_list assert_equal [nil] * 5, Psych.load(<<-eoyml) --- - ~ - null - - Null - NULL eoyml end end end psych-5.0.2/test/psych/test_numeric.rb000066400000000000000000000025221436167465700200230ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' require 'bigdecimal' module Psych ### # Test numerics from YAML spec: # http://yaml.org/type/float.html # http://yaml.org/type/int.html class TestNumeric < TestCase def setup @old_debug = $DEBUG $DEBUG = true end def teardown $DEBUG = @old_debug end def test_load_float_with_dot assert_equal 1.0, Psych.load('--- 1.') end def test_non_float_with_0 str = Psych.load('--- 090') assert_equal '090', str end def test_big_decimal_tag decimal = BigDecimal("12.34") assert_match "!ruby/object:BigDecimal", Psych.dump(decimal) end def test_big_decimal_round_trip decimal = BigDecimal("12.34") $DEBUG = false assert_cycle decimal end def test_does_not_attempt_numeric str = Psych.load('--- 4 roses') assert_equal '4 roses', str str = Psych.load('--- 1.1.1') assert_equal '1.1.1', str end # This behavior is not to YML spec, but is kept for backwards compatibility def test_string_with_commas number = Psych.load('--- 12,34,56') assert_equal 123456, number end def test_string_with_commas_with_strict_integer str = Psych.load('--- 12,34,56', strict_integer: true) assert_equal '12,34,56', str end end end psych-5.0.2/test/psych/test_object.rb000066400000000000000000000021051436167465700176240ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class Tagged yaml_tag '!foo' attr_accessor :baz def initialize @baz = 'bar' end end class Foo attr_accessor :parent def initialize parent @parent = parent end end class TestObject < TestCase def test_dump_with_tag tag = Tagged.new assert_match('foo', Psych.dump(tag)) end def test_tag_round_trip tag = Tagged.new tag2 = Psych.unsafe_load(Psych.dump(tag)) assert_equal tag.baz, tag2.baz assert_instance_of(Tagged, tag2) end def test_cyclic_references foo = Foo.new(nil) foo.parent = foo loaded = Psych.load(Psych.dump(foo), permitted_classes: [Foo], aliases: true) assert_instance_of(Foo, loaded) assert_same loaded, loaded.parent end def test_cyclic_reference_uses_alias foo = Foo.new(nil) foo.parent = foo assert_raise(AliasesNotEnabled) do Psych.load(Psych.dump(foo), permitted_classes: [Foo], aliases: false) end end end end psych-5.0.2/test/psych/test_object_references.rb000066400000000000000000000032771436167465700220400ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestObjectReferences < TestCase def test_range_has_references assert_reference_trip 1..2 end def test_module_has_references assert_reference_trip Psych end def test_class_has_references assert_reference_trip TestObjectReferences end def test_rational_has_references assert_reference_trip Rational('1.2') end def test_complex_has_references assert_reference_trip Complex(1, 2) end def test_datetime_has_references assert_reference_trip DateTime.now end def test_struct_has_references assert_reference_trip Struct.new(:foo).new(1) end def assert_reference_trip obj yml = Psych.dump([obj, obj]) assert_match(/\*-?\d+/, yml) begin data = Psych.load yml rescue Psych::DisallowedClass data = Psych.unsafe_load yml end assert_equal data.first.object_id, data.last.object_id end def test_float_references data = Psych.unsafe_load <<-eoyml ---\s - &name 1.2 - *name eoyml assert_equal data.first, data.last assert_equal data.first.object_id, data.last.object_id end def test_binary_references data = Psych.unsafe_load <<-eoyml --- - &name !binary |- aGVsbG8gd29ybGQh - *name eoyml assert_equal data.first, data.last assert_equal data.first.object_id, data.last.object_id end def test_regexp_references data = Psych.unsafe_load <<-eoyml ---\s - &name !ruby/regexp /pattern/i - *name eoyml assert_equal data.first, data.last assert_equal data.first.object_id, data.last.object_id end end end psych-5.0.2/test/psych/test_omap.rb000066400000000000000000000032011436167465700173100ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestOmap < TestCase def test_parse_as_map o = Psych.unsafe_load "--- !!omap\na: 1\nb: 2" assert_kind_of Psych::Omap, o assert_equal 1, o['a'] assert_equal 2, o['b'] end def test_self_referential map = Psych::Omap.new map['foo'] = 'bar' map['self'] = map assert_equal(map, Psych.unsafe_load(Psych.dump(map))) end def test_keys map = Psych::Omap.new map['foo'] = 'bar' assert_equal 'bar', map['foo'] end def test_order map = Psych::Omap.new map['a'] = 'b' map['b'] = 'c' assert_equal [%w{a b}, %w{b c}], map.to_a end def test_square list = [["a", "b"], ["b", "c"]] map = Psych::Omap[*list.flatten] assert_equal list, map.to_a assert_equal 'b', map['a'] assert_equal 'c', map['b'] end def test_dump map = Psych::Omap['a', 'b', 'c', 'd'] yaml = Psych.dump(map) assert_match('!omap', yaml) assert_match('- a: b', yaml) assert_match('- c: d', yaml) end def test_round_trip list = [["a", "b"], ["b", "c"]] map = Psych::Omap[*list.flatten] assert_cycle(map) end def test_load list = [["a", "b"], ["c", "d"]] map = Psych.load(<<-eoyml) --- !omap - a: b - c: d eoyml assert_equal list, map.to_a end # NOTE: This test will not work with Syck def test_load_shorthand list = [["a", "b"], ["c", "d"]] map = Psych.load(<<-eoyml) --- !!omap - a: b - c: d eoyml assert_equal list, map.to_a end end end psych-5.0.2/test/psych/test_parser.rb000066400000000000000000000260641436167465700176640ustar00rootroot00000000000000# coding: utf-8 # frozen_string_literal: true require_relative 'helper' module Psych class TestParser < TestCase class EventCatcher < Handler attr_accessor :parser attr_reader :calls, :marks def initialize @parser = nil @calls = [] @marks = [] end (Handler.instance_methods(true) - Object.instance_methods).each do |m| class_eval <<~RUBY, __FILE__, __LINE__ + 1 def #{m} *args super @marks << @parser.mark if @parser @calls << [:#{m}, args] end RUBY end end def setup super @handler = EventCatcher.new @parser = Psych::Parser.new @handler @handler.parser = @parser end def test_ast_roundtrip parser = Psych.parser parser.parse('null') ast = parser.handler.root assert_match(/^null/, ast.yaml) end def test_exception_memory_leak yaml = <<-eoyaml %YAML 1.1 %TAG ! tag:tenderlovemaking.com,2009: --- &ponies - first element - *ponies - foo: bar ... eoyaml [:start_stream, :start_document, :end_document, :alias, :scalar, :start_sequence, :end_sequence, :start_mapping, :end_mapping, :end_stream].each do |method| klass = Class.new(Psych::Handler) do define_method(method) do |*args| raise end end parser = Psych::Parser.new klass.new 2.times { assert_raise(RuntimeError, method.to_s) do parser.parse yaml end } end end def test_multiparse 3.times do @parser.parse '--- foo' end end def test_filename ex = assert_raise(Psych::SyntaxError) do @parser.parse '--- `', 'omg!' end assert_match 'omg!', ex.message end def test_line_numbers assert_equal 0, @parser.mark.line pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ @parser.parse "---\n- hello\n- world" line_calls = @handler.marks.map(&:line).zip(@handler.calls.map(&:first)) assert_equal [ [0, :event_location], [0, :start_stream], [0, :event_location], [0, :start_document], [1, :event_location], [1, :start_sequence], [2, :event_location], [2, :scalar], [3, :event_location], [3, :scalar], [3, :event_location], [3, :end_sequence], [3, :event_location], [3, :end_document], [3, :event_location], [3, :end_stream]], line_calls assert_equal 3, @parser.mark.line end def test_column_numbers assert_equal 0, @parser.mark.column pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ @parser.parse "---\n- hello\n- world" col_calls = @handler.marks.map(&:column).zip(@handler.calls.map(&:first)) assert_equal [ [0, :event_location], [0, :start_stream], [3, :event_location], [3, :start_document], [1, :event_location], [1, :start_sequence], [0, :event_location], [0, :scalar], [0, :event_location], [0, :scalar], [0, :event_location], [0, :end_sequence], [0, :event_location], [0, :end_document], [0, :event_location], [0, :end_stream]], col_calls assert_equal 0, @parser.mark.column end def test_index_numbers assert_equal 0, @parser.mark.index pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ @parser.parse "---\n- hello\n- world" idx_calls = @handler.marks.map(&:index).zip(@handler.calls.map(&:first)) assert_equal [ [0, :event_location], [0, :start_stream], [3, :event_location], [3, :start_document], [5, :event_location], [5, :start_sequence], [12, :event_location], [12, :scalar], [19, :event_location], [19, :scalar], [19, :event_location], [19, :end_sequence], [19, :event_location], [19, :end_document], [19, :event_location], [19, :end_stream]], idx_calls assert_equal 19, @parser.mark.index end def test_bom tadpole = 'おたまじゃくし' # BOM + text yml = "\uFEFF#{tadpole}".encode('UTF-16LE') @parser.parse yml assert_equal tadpole, @parser.handler.calls.find { |method, args| method == :scalar }[1].first end def test_external_encoding tadpole = 'おたまじゃくし' @parser.external_encoding = Psych::Parser::UTF16LE @parser.parse tadpole.encode 'UTF-16LE' assert_equal tadpole, @parser.handler.calls.find { |method, args| method == :scalar }[1].first end def test_bogus_io o = Object.new def o.external_encoding; nil end def o.read len; self end assert_raise(TypeError) do @parser.parse o end end def test_parse_io @parser.parse StringIO.new("--- a") assert_called :start_stream assert_called :scalar assert_called :end_stream end def test_syntax_error assert_raise(Psych::SyntaxError) do @parser.parse("---\n\"foo\"\n\"bar\"\n") end end def test_syntax_error_twice assert_raise(Psych::SyntaxError) do @parser.parse("---\n\"foo\"\n\"bar\"\n") end assert_raise(Psych::SyntaxError) do @parser.parse("---\n\"foo\"\n\"bar\"\n") end end def test_syntax_error_has_path_for_string e = assert_raise(Psych::SyntaxError) do @parser.parse("---\n\"foo\"\n\"bar\"\n") end assert_match '():', e.message end def test_syntax_error_has_path_for_io io = StringIO.new "---\n\"foo\"\n\"bar\"\n" def io.path; "hello!"; end e = assert_raise(Psych::SyntaxError) do @parser.parse(io) end assert_match "(#{io.path}):", e.message end def test_mapping_end @parser.parse("---\n!!map { key: value }") assert_called :end_mapping end def test_mapping_tag @parser.parse("---\n!!map { key: value }") assert_called :start_mapping, ["tag:yaml.org,2002:map", false, Nodes::Mapping::FLOW] end def test_mapping_anchor @parser.parse("---\n&A { key: value }") assert_called :start_mapping, ['A', true, Nodes::Mapping::FLOW] end def test_mapping_block @parser.parse("---\n key: value") assert_called :start_mapping, [true, Nodes::Mapping::BLOCK] end def test_mapping_start @parser.parse("---\n{ key: value }") assert_called :start_mapping assert_called :start_mapping, [true, Nodes::Mapping::FLOW] end def test_sequence_end @parser.parse("---\n&A [1, 2]") assert_called :end_sequence end def test_sequence_start_anchor @parser.parse("---\n&A [1, 2]") assert_called :start_sequence, ["A", true, Nodes::Sequence::FLOW] end def test_sequence_start_tag @parser.parse("---\n!!seq [1, 2]") assert_called :start_sequence, ["tag:yaml.org,2002:seq", false, Nodes::Sequence::FLOW] end def test_sequence_start_flow @parser.parse("---\n[1, 2]") assert_called :start_sequence, [true, Nodes::Sequence::FLOW] end def test_sequence_start_block @parser.parse("---\n - 1\n - 2") assert_called :start_sequence, [true, Nodes::Sequence::BLOCK] end def test_literal_scalar @parser.parse(<<-eoyml) %YAML 1.1 --- "literal\n\ \ttext\n" eoyml assert_called :scalar, ['literal text ', false, true, Nodes::Scalar::DOUBLE_QUOTED] end def test_scalar @parser.parse("--- foo\n") assert_called :scalar, ['foo', true, false, Nodes::Scalar::PLAIN] end def test_scalar_with_tag @parser.parse("---\n!!str foo\n") assert_called :scalar, ['foo', 'tag:yaml.org,2002:str', false, false, Nodes::Scalar::PLAIN] end def test_scalar_with_anchor @parser.parse("---\n&A foo\n") assert_called :scalar, ['foo', 'A', true, false, Nodes::Scalar::PLAIN] end def test_scalar_plain_implicit @parser.parse("---\n&A foo\n") assert_called :scalar, ['foo', 'A', true, false, Nodes::Scalar::PLAIN] end def test_alias @parser.parse(<<-eoyml) %YAML 1.1 --- !!seq [ !!str "Without properties", &A !!str "Anchored", !!str "Tagged", *A, !!str "", ] eoyml assert_called :alias, ['A'] end def test_end_stream @parser.parse("--- foo\n") assert_called :end_stream end def test_start_stream @parser.parse("--- foo\n") assert_called :start_stream end def test_end_document_implicit @parser.parse("\"foo\"\n") assert_called :end_document, [true] end def test_end_document_explicit @parser.parse("\"foo\"\n...") assert_called :end_document, [false] end def test_start_document_version @parser.parse("%YAML 1.1\n---\n\"foo\"\n") assert_called :start_document, [[1,1], [], false] end def test_start_document_tag @parser.parse("%TAG !yaml! tag:yaml.org,2002\n---\n!yaml!str \"foo\"\n") assert_called :start_document, [[], [['!yaml!', 'tag:yaml.org,2002']], false] end def test_event_location pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ @parser.parse "foo:\n" \ " barbaz: [1, 2]" events = @handler.calls.each_slice(2).map do |location, event| [event[0], location[1]] end assert_equal [ [:start_stream, [0, 0, 0, 0]], [:start_document, [0, 0, 0, 0]], [:start_mapping, [0, 0, 0, 0]], [:scalar, [0, 0, 0, 3]], [:start_mapping, [1, 2, 1, 2]], [:scalar, [1, 2, 1, 8]], [:start_sequence, [1, 10, 1, 11]], [:scalar, [1, 11, 1, 12]], [:scalar, [1, 14, 1, 15]], [:end_sequence, [1, 15, 1, 16]], [:end_mapping, [2, 0, 2, 0]], [:end_mapping, [2, 0, 2, 0]], [:end_document, [2, 0, 2, 0]], [:end_stream, [2, 0, 2, 0]]], events end def assert_called call, with = nil, parser = @parser if with call = parser.handler.calls.find { |x| x.first == call && x.last.compact == with } assert(call, "#{[call,with].inspect} not in #{parser.handler.calls.inspect}" ) else assert parser.handler.calls.any? { |x| x.first == call } end end end end psych-5.0.2/test/psych/test_psych.rb000066400000000000000000000257541436167465700175230ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' require 'stringio' require 'tempfile' class TestPsych < Psych::TestCase def setup @orig_verbose, $VERBOSE = $VERBOSE, nil end def teardown Psych.domain_types.clear $VERBOSE = @orig_verbose end def test_line_width_invalid assert_raise(ArgumentError) { Psych.dump('x', { :line_width => -2 }) } end def test_line_width_no_limit data = { 'a' => 'a b' * 50} expected = "---\na: #{'a b' * 50}\n" assert_equal(expected, Psych.dump(data, { :line_width => -1 })) end def test_line_width_limit yml = Psych.dump('123456 7', { :line_width => 5 }) assert_match(/^\s*7/, yml) end def test_indent yml = Psych.dump({:a => {'b' => 'c'}}, {:indentation => 5}) assert_match(/^[ ]{5}b/, yml) end def test_canonical yml = Psych.dump({:a => {'b' => 'c'}}, {:canonical => true}) assert_match(/\? "b/, yml) end def test_header yml = Psych.dump({:a => {'b' => 'c'}}, {:header => true}) assert_match(/YAML/, yml) end def test_version_array yml = Psych.dump({:a => {'b' => 'c'}}, {:version => [1,1]}) assert_match(/1.1/, yml) end def test_version_string yml = Psych.dump({:a => {'b' => 'c'}}, {:version => '1.1'}) assert_match(/1.1/, yml) end def test_version_bool yml = Psych.dump({:a => {'b' => 'c'}}, {:version => true}) assert_match(/1.1/, yml) end def test_load_argument_error assert_raise(TypeError) do Psych.load nil end end def test_parse assert_equal %w[a b], Psych.parse("- a\n- b").to_ruby end def test_parse_default_fallback assert_equal false, Psych.parse("") end def test_parse_raises_on_bad_input assert_raise(Psych::SyntaxError) { Psych.parse("--- `") } end def test_non_existing_class_on_deserialize e = assert_raise(ArgumentError) do Psych.unsafe_load("--- !ruby/object:NonExistent\nfoo: 1") end assert_equal 'undefined class/module NonExistent', e.message end def test_dump_stream things = [22, "foo \n", {}] stream = Psych.dump_stream(*things) assert_equal things, Psych.load_stream(stream) end def test_dump_file hash = {'hello' => 'TGIF!'} Tempfile.create('fun.yml') do |io| assert_equal io, Psych.dump(hash, io) io.rewind assert_equal Psych.dump(hash), io.read end end def test_dump_io hash = {'hello' => 'TGIF!'} stringio = StringIO.new ''.dup assert_equal stringio, Psych.dump(hash, stringio) assert_equal Psych.dump(hash), stringio.string end def test_simple assert_equal 'foo', Psych.load("--- foo\n") end def test_libyaml_version assert Psych.libyaml_version assert_equal Psych.libyaml_version.join('.'), Psych::LIBYAML_VERSION end def test_load_stream docs = Psych.load_stream("--- foo\n...\n--- bar\n...") assert_equal %w{ foo bar }, docs end def test_load_stream_freeze docs = Psych.load_stream("--- foo\n...\n--- bar\n...", freeze: true) assert_equal %w{ foo bar }, docs docs.each do |string| assert_predicate string, :frozen? end end def test_load_stream_symbolize_names docs = Psych.load_stream("---\nfoo: bar", symbolize_names: true) assert_equal [{foo: 'bar'}], docs end def test_load_stream_default_fallback assert_equal [], Psych.load_stream("") end def test_load_stream_raises_on_bad_input assert_raise(Psych::SyntaxError) { Psych.load_stream("--- `") } end def test_parse_stream docs = Psych.parse_stream("--- foo\n...\n--- bar\n...") assert_equal(%w[foo bar], docs.children.map(&:transform)) end def test_parse_stream_with_block docs = [] Psych.parse_stream("--- foo\n...\n--- bar\n...") do |node| docs << node end assert_equal %w[foo bar], docs.map(&:to_ruby) end def test_parse_stream_default_fallback docs = Psych.parse_stream("") assert_equal [], docs.children.map(&:to_ruby) end def test_parse_stream_with_block_default_fallback docs = [] Psych.parse_stream("") do |node| docs << node end assert_equal [], docs.map(&:to_ruby) end def test_parse_stream_raises_on_bad_input assert_raise(Psych::SyntaxError) { Psych.parse_stream("--- `") } end def test_add_builtin_type got = nil Psych.add_builtin_type 'omap' do |type, val| got = val end Psych.load('--- !!omap hello') assert_equal 'hello', got ensure Psych.remove_type 'omap' end def test_domain_types got = nil Psych.add_domain_type 'foo.bar/2002', 'foo' do |type, val| got = val end Psych.load('--- !foo.bar/2002:foo hello') assert_equal 'hello', got Psych.load("--- !foo.bar/2002:foo\n- hello\n- world") assert_equal %w{ hello world }, got Psych.load("--- !foo.bar/2002:foo\nhello: world") assert_equal({ 'hello' => 'world' }, got) end def test_load_freeze data = Psych.load("--- {foo: ['a']}", freeze: true) assert_predicate data, :frozen? assert_predicate data['foo'], :frozen? assert_predicate data['foo'].first, :frozen? end def test_load_freeze_deduplication unless String.method_defined?(:-@) && (-("a" * 20)).equal?((-("a" * 20))) pend "This Ruby implementation doesn't support string deduplication" end data = Psych.load("--- ['a']", freeze: true) assert_same 'a', data.first end def test_load_default_fallback assert_equal false, Psych.unsafe_load("") end def test_load_with_fallback assert_equal 42, Psych.load("", filename: "file", fallback: 42) end def test_load_with_fallback_nil_or_false assert_nil Psych.load("", filename: "file", fallback: nil) assert_equal false, Psych.load("", filename: "file", fallback: false) end def test_load_with_fallback_hash assert_equal Hash.new, Psych.load("", filename: "file", fallback: Hash.new) end def test_load_with_fallback_for_nil assert_nil Psych.unsafe_load("--- null", filename: "file", fallback: 42) end def test_load_with_fallback_for_false assert_equal false, Psych.unsafe_load("--- false", filename: "file", fallback: 42) end def test_load_file Tempfile.create(['yikes', 'yml']) {|t| t.binmode t.write('--- hello world') t.close assert_equal 'hello world', Psych.load_file(t.path) } end def test_load_file_freeze Tempfile.create(['yikes', 'yml']) {|t| t.binmode t.write('--- hello world') t.close object = Psych.load_file(t.path, freeze: true) assert_predicate object, :frozen? } end def test_load_file_symbolize_names Tempfile.create(['yikes', 'yml']) {|t| t.binmode t.write("---\nfoo: bar") t.close assert_equal({foo: 'bar'}, Psych.load_file(t.path, symbolize_names: true)) } end def test_load_file_default_fallback Tempfile.create(['empty', 'yml']) {|t| assert_equal false, Psych.unsafe_load_file(t.path) } end def test_load_file_with_fallback Tempfile.create(['empty', 'yml']) {|t| assert_equal 42, Psych.load_file(t.path, fallback: 42) } end def test_load_file_with_fallback_nil_or_false Tempfile.create(['empty', 'yml']) {|t| assert_nil Psych.load_file(t.path, fallback: nil) assert_equal false, Psych.load_file(t.path, fallback: false) } end def test_load_file_with_fallback_hash Tempfile.create(['empty', 'yml']) {|t| assert_equal Hash.new, Psych.load_file(t.path, fallback: Hash.new) } end def test_load_file_with_fallback_for_nil Tempfile.create(['nil', 'yml']) {|t| t.binmode t.write('--- null') t.close assert_nil Psych.load_file(t.path, fallback: 42) } end def test_load_file_with_fallback_for_false Tempfile.create(['false', 'yml']) {|t| t.binmode t.write('--- false') t.close assert_equal false, Psych.load_file(t.path, fallback: 42) } end def test_safe_load_file_with_permitted_classe Tempfile.create(['false', 'yml']) {|t| t.binmode t.write("--- !ruby/range\nbegin: 0\nend: 42\nexcl: false\n") t.close assert_equal 0..42, Psych.safe_load_file(t.path, permitted_classes: [Range]) assert_raise(Psych::DisallowedClass) { Psych.safe_load_file(t.path) } } end def test_parse_file Tempfile.create(['yikes', 'yml']) {|t| t.binmode t.write('--- hello world') t.close assert_equal 'hello world', Psych.parse_file(t.path).transform } end def test_parse_file_default_fallback Tempfile.create(['empty', 'yml']) do |t| assert_equal false, Psych.parse_file(t.path) end end def test_degenerate_strings assert_equal false, Psych.unsafe_load(' ') assert_equal false, Psych.parse(' ') assert_equal false, Psych.unsafe_load('') assert_equal false, Psych.parse('') end def test_callbacks types = [] appender = lambda { |*args| types << args } Psych.add_domain_type('example.com:2002', 'foo', &appender) Psych.load <<-eoyml - !tag:example.com:2002:foo bar eoyml assert_equal [ ["tag:example.com:2002:foo", "bar"] ], types end def test_symbolize_names yaml = <<-eoyml foo: bar: baz 1: 2 hoge: - fuga: piyo eoyml result = Psych.load(yaml) assert_equal result, { "foo" => { "bar" => "baz", 1 => 2 }, "hoge" => [{ "fuga" => "piyo" }] } result = Psych.load(yaml, symbolize_names: true) assert_equal result, { foo: { bar: "baz", 1 => 2 }, hoge: [{ fuga: "piyo" }] } result = Psych.safe_load(yaml, symbolize_names: true) assert_equal result, { foo: { bar: "baz", 1 => 2 }, hoge: [{ fuga: "piyo" }] } end def test_safe_dump_defaults yaml = <<-eoyml --- array: - 1 float: 13.12 booleans: - true - false eoyml payload = Psych.safe_dump({ "array" => [1], "float" => 13.12, "booleans" => [true, false], }) assert_equal yaml, payload end def test_safe_dump_unpermitted_class error = assert_raise Psych::DisallowedClass do Psych.safe_dump(Object.new) end assert_equal "Tried to dump unspecified class: Object", error.message hash_subclass = Class.new(Hash) error = assert_raise Psych::DisallowedClass do Psych.safe_dump(hash_subclass.new) end assert_equal "Tried to dump unspecified class: #{hash_subclass.inspect}", error.message end def test_safe_dump_extra_permitted_classes assert_equal "--- !ruby/object {}\n", Psych.safe_dump(Object.new, permitted_classes: [Object]) end def test_safe_dump_symbols error = assert_raise Psych::DisallowedClass do Psych.safe_dump(:foo, permitted_classes: [Symbol]) end assert_equal "Tried to dump unspecified class: Symbol(:foo)", error.message assert_match(/\A--- :foo\n(?:\.\.\.\n)?\z/, Psych.safe_dump(:foo, permitted_classes: [Symbol], permitted_symbols: [:foo])) end def test_safe_dump_aliases x = [] x << x error = assert_raise Psych::BadAlias do Psych.safe_dump(x) end assert_equal "Tried to dump an aliased object", error.message assert_equal "--- &1\n" + "- *1\n", Psych.safe_dump(x, aliases: true) end end psych-5.0.2/test/psych/test_ractor.rb000066400000000000000000000030031436167465700176460ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' class TestPsychRactor < Test::Unit::TestCase def test_ractor_round_trip assert_ractor(<<~RUBY, require_relative: 'helper') obj = {foo: [42]} obj2 = Ractor.new(obj) do |obj| Psych.unsafe_load(Psych.dump(obj)) end.take assert_equal obj, obj2 RUBY end def test_not_shareable # There's no point in making these frozen / shareable # and the C-ext disregards begin frozen assert_ractor(<<~RUBY, require_relative: 'helper') parser = Psych::Parser.new emitter = Psych::Emitter.new(nil) assert_raise(Ractor::Error) { Ractor.make_shareable(parser) } assert_raise(Ractor::Error) { Ractor.make_shareable(emitter) } RUBY end def test_ractor_config # Config is ractor-local # Test is to make sure it works, even though usage is probably very low. # The methods are not documented and might be deprecated one day assert_ractor(<<~RUBY, require_relative: 'helper') r = Ractor.new do Psych.add_builtin_type 'omap' do |type, val| val * 2 end Psych.load('--- !!omap hello') end.take assert_equal 'hellohello', r assert_equal 'hello', Psych.load('--- !!omap hello') RUBY end def test_ractor_constants assert_ractor(<<~RUBY, require_relative: 'helper') r = Ractor.new do Psych.libyaml_version.join('.') == Psych::LIBYAML_VERSION end.take assert_equal true, r RUBY end end if defined?(Ractor) psych-5.0.2/test/psych/test_safe_load.rb000066400000000000000000000066241436167465700203050ustar00rootroot00000000000000# frozen_string_literal: true require 'psych/helper' module Psych class TestSafeLoad < TestCase def setup @orig_verbose, $VERBOSE = $VERBOSE, nil end def teardown $VERBOSE = @orig_verbose end class Foo; end [1, 2.2, {}, [], "foo"].each do |obj| define_method(:"test_basic_#{obj.class}") do assert_safe_cycle obj end end def test_raises_when_alias_found_if_alias_parsing_not_enabled yaml_with_aliases = <<~YAML --- a: &ABC k1: v1 k2: v2 b: *ABC YAML assert_raise(Psych::AliasesNotEnabled) do Psych.safe_load(yaml_with_aliases) end end def test_aliases_are_parsed_when_alias_parsing_is_enabled yaml_with_aliases = <<~YAML --- a: &ABC k1: v1 k2: v2 b: *ABC YAML result = Psych.safe_load(yaml_with_aliases, aliases: true) assert_same result.fetch("a"), result.fetch("b") end def test_permitted_symbol yml = Psych.dump :foo assert_raise(Psych::DisallowedClass) do Psych.safe_load yml end assert_equal( :foo, Psych.safe_load( yml, permitted_classes: [Symbol], permitted_symbols: [:foo] ) ) end def test_symbol assert_raise(Psych::DisallowedClass) do assert_safe_cycle :foo end assert_raise(Psych::DisallowedClass) do Psych.safe_load '--- !ruby/symbol foo', permitted_classes: [] end assert_safe_cycle :foo, permitted_classes: [Symbol] assert_safe_cycle :foo, permitted_classes: %w{ Symbol } assert_equal :foo, Psych.safe_load('--- !ruby/symbol foo', permitted_classes: [Symbol]) end def test_foo assert_raise(Psych::DisallowedClass) do Psych.safe_load '--- !ruby/object:Foo {}', permitted_classes: [Foo] end assert_raise(Psych::DisallowedClass) do assert_safe_cycle Foo.new end assert_kind_of(Foo, Psych.safe_load(Psych.dump(Foo.new), permitted_classes: [Foo])) end X = Struct.new(:x) def test_struct_depends_on_sym assert_safe_cycle(X.new, permitted_classes: [X, Symbol]) assert_raise(Psych::DisallowedClass) do cycle X.new, permitted_classes: [X] end end def test_anon_struct assert Psych.safe_load(<<-eoyml, permitted_classes: [Struct, Symbol]) --- !ruby/struct foo: bar eoyml assert_raise(Psych::DisallowedClass) do Psych.safe_load(<<-eoyml, permitted_classes: [Struct]) --- !ruby/struct foo: bar eoyml end assert_raise(Psych::DisallowedClass) do Psych.safe_load(<<-eoyml, permitted_classes: [Symbol]) --- !ruby/struct foo: bar eoyml end end def test_safe_load_default_fallback assert_nil Psych.safe_load("") end def test_safe_load assert_equal %w[a b], Psych.safe_load("- a\n- b") end def test_safe_load_raises_on_bad_input assert_raise(Psych::SyntaxError) { Psych.safe_load("--- `") } end private def cycle object, permitted_classes: [] Psych.safe_load(Psych.dump(object), permitted_classes: permitted_classes) end def assert_safe_cycle object, permitted_classes: [] other = cycle object, permitted_classes: permitted_classes assert_equal object, other end end end psych-5.0.2/test/psych/test_scalar.rb000066400000000000000000000004771436167465700176350ustar00rootroot00000000000000# -*- coding: utf-8 -*- # frozen_string_literal: true require_relative 'helper' module Psych class TestScalar < TestCase def test_utf_8 assert_equal "日本語", Psych.load("--- 日本語") end def test_some_bytes # Ticket #278 x = "\xEF\xBF\xBD\x1F" assert_cycle x end end end psych-5.0.2/test/psych/test_scalar_scanner.rb000066400000000000000000000143721436167465700213450ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' require 'date' module Psych class TestScalarScanner < TestCase attr_reader :ss def setup super @ss = Psych::ScalarScanner.new ClassLoader.new end def test_scan_time { '2001-12-15T02:59:43.1Z' => Time.utc(2001, 12, 15, 02, 59, 43, 100000), '2001-12-14t21:59:43.10-05:00' => Time.utc(2001, 12, 15, 02, 59, 43, 100000), '2001-12-14 21:59:43.10 -5' => Time.utc(2001, 12, 15, 02, 59, 43, 100000), '2001-12-15 2:59:43.10' => Time.utc(2001, 12, 15, 02, 59, 43, 100000), '2011-02-24 11:17:06 -0800' => Time.utc(2011, 02, 24, 19, 17, 06) }.each do |time_str, time| assert_equal time, @ss.tokenize(time_str) end end def test_scan_bad_time [ '2001-12-15T02:59:73.1Z', '2001-12-14t90:59:43.10-05:00', '2001-92-14 21:59:43.10 -5', '2001-12-15 92:59:43.10', '2011-02-24 81:17:06 -0800', ].each do |time_str| assert_equal time_str, @ss.tokenize(time_str) end end def test_scan_bad_dates x = '2000-15-01' assert_equal x, @ss.tokenize(x) x = '2000-10-51' assert_equal x, @ss.tokenize(x) x = '2000-10-32' assert_equal x, @ss.tokenize(x) end def test_scan_good_edge_date x = '2000-1-31' assert_equal Date.strptime(x, '%Y-%m-%d'), @ss.tokenize(x) end def test_scan_bad_edge_date x = '2000-11-31' assert_equal x, @ss.tokenize(x) end def test_scan_date date = '1980-12-16' token = @ss.tokenize date assert_equal 1980, token.year assert_equal 12, token.month assert_equal 16, token.day end def test_scan_inf assert_equal(1 / 0.0, ss.tokenize('.inf')) end def test_scan_plus_inf assert_equal(1 / 0.0, ss.tokenize('+.inf')) end def test_scan_minus_inf assert_equal(-1 / 0.0, ss.tokenize('-.inf')) end def test_scan_nan assert ss.tokenize('.nan').nan? end def test_scan_float_with_exponent_but_no_fraction assert_equal(0.0, ss.tokenize('0.E+0')) end def test_scan_null assert_nil ss.tokenize('null') assert_nil ss.tokenize('~') assert_nil ss.tokenize('') end def test_scan_symbol assert_equal :foo, ss.tokenize(':foo') end def test_scan_not_sexagesimal assert_equal '00:00:00:00:0f', ss.tokenize('00:00:00:00:0f') assert_equal '00:00:00:00:00', ss.tokenize('00:00:00:00:00') assert_equal '00:00:00:00:00.0', ss.tokenize('00:00:00:00:00.0') end def test_scan_sexagesimal_float assert_equal 685230.15, ss.tokenize('190:20:30.15') end def test_scan_sexagesimal_int assert_equal 685230, ss.tokenize('190:20:30') end def test_scan_float assert_equal 1.2, ss.tokenize('1.2') end def test_scan_true assert_equal true, ss.tokenize('true') end def test_scan_strings_starting_with_underscores assert_equal '_100', ss.tokenize('_100') end def test_scan_strings_starting_with_number assert_equal '450D', ss.tokenize('450D') end def test_scan_strings_ending_with_underscores assert_equal '100_', ss.tokenize('100_') end def test_scan_int_commas_and_underscores # NB: This test is to ensure backward compatibility with prior Psych versions, # not to test against any actual YAML specification. assert_equal 123_456_789, ss.tokenize('123_456_789') assert_equal 123_456_789, ss.tokenize('123,456,789') assert_equal 123_456_789, ss.tokenize('1_2,3,4_5,6_789') assert_equal 1, ss.tokenize('1') assert_equal 1, ss.tokenize('+1') assert_equal(-1, ss.tokenize('-1')) assert_equal 0b010101010, ss.tokenize('0b010101010') assert_equal 0b010101010, ss.tokenize('0b0,1_0,1_,0,1_01,0') assert_equal 01234567, ss.tokenize('01234567') assert_equal 01234567, ss.tokenize('0_,,,1_2,_34567') assert_equal 0x123456789abcdef, ss.tokenize('0x123456789abcdef') assert_equal 0x123456789abcdef, ss.tokenize('0x12_,34,_56,_789abcdef') assert_equal 0x123456789abcdef, ss.tokenize('0x_12_,34,_56,_789abcdef') assert_equal 0x123456789abcdef, ss.tokenize('0x12_,34,_56,_789abcdef__') end def test_scan_strict_int_commas_and_underscores # this test is to ensure adherance to YML spec using the 'strict_integer' option scanner = Psych::ScalarScanner.new ClassLoader.new, strict_integer: true assert_equal 123_456_789, scanner.tokenize('123_456_789') assert_equal '123,456,789', scanner.tokenize('123,456,789') assert_equal '1_2,3,4_5,6_789', scanner.tokenize('1_2,3,4_5,6_789') assert_equal 1, scanner.tokenize('1') assert_equal 1, scanner.tokenize('+1') assert_equal(-1, scanner.tokenize('-1')) assert_equal 0b010101010, scanner.tokenize('0b010101010') assert_equal 0b010101010, scanner.tokenize('0b01_01_01_010') assert_equal '0b0,1_0,1_,0,1_01,0', scanner.tokenize('0b0,1_0,1_,0,1_01,0') assert_equal 01234567, scanner.tokenize('01234567') assert_equal '0_,,,1_2,_34567', scanner.tokenize('0_,,,1_2,_34567') assert_equal 0x123456789abcdef, scanner.tokenize('0x123456789abcdef') assert_equal 0x123456789abcdef, scanner.tokenize('0x12_34_56_789abcdef') assert_equal '0x12_,34,_56,_789abcdef', scanner.tokenize('0x12_,34,_56,_789abcdef') assert_equal '0x_12_,34,_56,_789abcdef', scanner.tokenize('0x_12_,34,_56,_789abcdef') assert_equal '0x12_,34,_56,_789abcdef__', scanner.tokenize('0x12_,34,_56,_789abcdef__') end def test_scan_dot assert_equal '.', ss.tokenize('.') end def test_scan_plus_dot assert_equal '+.', ss.tokenize('+.') end class MatchCallCounter < String attr_reader :match_call_count def match?(pat) @match_call_count ||= 0 @match_call_count += 1 super end end def test_scan_ascii_matches_quickly ascii = MatchCallCounter.new('abcdefghijklmnopqrstuvwxyz') ss.tokenize(ascii) assert_equal 1, ascii.match_call_count end def test_scan_unicode_matches_quickly unicode = MatchCallCounter.new('鳥かご関連用品') ss.tokenize(unicode) assert_equal 1, unicode.match_call_count end end end psych-5.0.2/test/psych/test_serialize_subclasses.rb000066400000000000000000000015221436167465700225760ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestSerializeSubclasses < TestCase class SomeObject def initialize one, two @one = one @two = two end def == other @one == other.instance_eval { @one } && @two == other.instance_eval { @two } end end def test_some_object so = SomeObject.new('foo', [1,2,3]) assert_equal so, Psych.unsafe_load(Psych.dump(so)) end class StructSubclass < Struct.new(:foo) def initialize foo, bar super(foo) @bar = bar end def == other super(other) && @bar == other.instance_eval{ @bar } end end def test_struct_subclass so = StructSubclass.new('foo', [1,2,3]) assert_equal so, Psych.unsafe_load(Psych.dump(so)) end end end psych-5.0.2/test/psych/test_set.rb000066400000000000000000000016441436167465700171600ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestSet < TestCase def setup super @set = Psych::Set.new @set['foo'] = 'bar' @set['bar'] = 'baz' end def test_dump assert_match(/!set/, Psych.dump(@set)) end def test_roundtrip assert_cycle(@set) end ### # FIXME: Syck should also support !!set as shorthand def test_load_from_yaml loaded = Psych.unsafe_load(<<-eoyml) --- !set foo: bar bar: baz eoyml assert_equal(@set, loaded) end def test_loaded_class assert_instance_of(Psych::Set, Psych.unsafe_load(Psych.dump(@set))) end def test_set_shorthand loaded = Psych.unsafe_load(<<-eoyml) --- !!set foo: bar bar: baz eoyml assert_instance_of(Psych::Set, loaded) end def test_set_self_reference @set['self'] = @set assert_cycle(@set) end end end psych-5.0.2/test/psych/test_stream.rb000066400000000000000000000055551436167465700176650ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestStream < TestCase [ [Psych::Nodes::Alias, :alias?], [Psych::Nodes::Document, :document?], [Psych::Nodes::Mapping, :mapping?], [Psych::Nodes::Scalar, :scalar?], [Psych::Nodes::Sequence, :sequence?], [Psych::Nodes::Stream, :stream?], ].each do |klass, block| define_method :"test_predicate_#{block}" do rb = Psych.parse_stream("---\n- foo: bar\n- &a !!str Anchored\n- *a") nodes = rb.grep(klass) assert_operator nodes.length, :>, 0 assert_equal nodes, rb.find_all(&block) end end def test_parse_partial rb = Psych.parse("--- foo\n...\n--- `").to_ruby assert_equal 'foo', rb end def test_load_partial rb = Psych.load("--- foo\n...\n--- `") assert_equal 'foo', rb end def test_parse_stream_yields_documents list = [] Psych.parse_stream("--- foo\n...\n--- bar") do |doc| list << doc.to_ruby end assert_equal %w{ foo bar }, list end def test_parse_stream_break list = [] Psych.parse_stream("--- foo\n...\n--- `") do |doc| list << doc.to_ruby break end assert_equal %w{ foo }, list end def test_load_stream_yields_documents list = [] Psych.load_stream("--- foo\n...\n--- bar") do |ruby| list << ruby end assert_equal %w{ foo bar }, list end def test_load_stream_break list = [] Psych.load_stream("--- foo\n...\n--- `") do |ruby| list << ruby break end assert_equal %w{ foo }, list end def test_explicit_documents io = StringIO.new stream = Psych::Stream.new(io) stream.start stream.push({ 'foo' => 'bar' }) assert !stream.finished?, 'stream not finished' stream.finish assert stream.finished?, 'stream finished' assert_match(/^---/, io.string) assert_match(/\.\.\.$/, io.string) end def test_start_takes_block io = StringIO.new stream = Psych::Stream.new(io) stream.start do |emitter| emitter.push({ 'foo' => 'bar' }) end assert stream.finished?, 'stream finished' assert_match(/^---/, io.string) assert_match(/\.\.\.$/, io.string) end def test_no_backreferences io = StringIO.new stream = Psych::Stream.new(io) stream.start do |emitter| x = { 'foo' => 'bar' } emitter.push x emitter.push x end assert stream.finished?, 'stream finished' assert_match(/^---/, io.string) assert_match(/\.\.\.$/, io.string) assert_equal 2, io.string.scan('---').length assert_equal 2, io.string.scan('...').length assert_equal 2, io.string.scan('foo').length assert_equal 2, io.string.scan('bar').length end end end psych-5.0.2/test/psych/test_string.rb000066400000000000000000000150361436167465700176730ustar00rootroot00000000000000# encoding: UTF-8 # frozen_string_literal: true require_relative 'helper' module Psych class TestString < TestCase class X < String end class Y < String attr_accessor :val end class Z < String def initialize force_encoding Encoding::US_ASCII end end # 'y' and 'n' are kind of ambiguous. Syck treated y and n literals in # YAML documents as strings. But this is not what the YAML 1.1 spec says. # YAML 1.1 says they should be treated as booleans. When we're dumping # documents, we know it's a string, so adding quotes will eliminate the # "ambiguity" in the emitted document def test_y_is_quoted assert_match(/"y"/, Psych.dump("y")) end def test_n_is_quoted assert_match(/"n"/, Psych.dump("n")) end def test_string_with_newline assert_equal "1\n2", Psych.load("--- ! '1\n\n 2'\n") end def test_no_doublequotes_with_special_characters assert_equal 2, Psych.dump(%Q{<%= ENV["PATH"] %>}).count('"') end def test_no_quotes_when_start_with_non_ascii_character yaml = Psych.dump 'Český non-ASCII'.encode(Encoding::UTF_8) assert_match(/---\s*[^"'!]+$/, yaml) end def test_doublequotes_when_there_is_a_single str = "@123'abc" yaml = Psych.dump str assert_match(/---\s*"/, yaml) assert_equal str, Psych.load(yaml) end def test_plain_when_shorten_than_line_width_and_no_final_line_break str = "Lorem ipsum" yaml = Psych.dump str, line_width: 12 assert_match(/---\s*[^>|]+\n/, yaml) assert_equal str, Psych.load(yaml) end def test_plain_when_shorten_than_line_width_and_with_final_line_break str = "Lorem ipsum\n" yaml = Psych.dump str, line_width: 12 assert_match(/---\s*[^>|]+\n/, yaml) assert_equal str, Psych.load(yaml) end def test_folded_when_longer_than_line_width_and_with_final_line_break str = "Lorem ipsum dolor sit\n" yaml = Psych.dump str, line_width: 12 assert_match(/---\s*>\n(.*\n){2}\Z/, yaml) assert_equal str, Psych.load(yaml) end # http://yaml.org/spec/1.2/2009-07-21/spec.html#id2593651 def test_folded_strip_when_longer_than_line_width_and_no_newlines str = "Lorem ipsum dolor sit amet, consectetur" yaml = Psych.dump str, line_width: 12 assert_match(/---\s*>-\n(.*\n){3}\Z/, yaml) assert_equal str, Psych.load(yaml) end def test_literal_when_inner_and_final_line_break [ "Lorem ipsum\ndolor\n", "Lorem ipsum\nZolor\n", ].each do |str| yaml = Psych.dump str, line_width: 12 assert_match(/---\s*\|\n(.*\n){2}\Z/, yaml) assert_equal str, Psych.load(yaml) end end # http://yaml.org/spec/1.2/2009-07-21/spec.html#id2593651 def test_literal_strip_when_inner_line_break_and_no_final_line_break [ "Lorem ipsum\ndolor", "Lorem ipsum\nZolor", ].each do |str| yaml = Psych.dump str, line_width: 12 assert_match(/---\s*\|-\n(.*\n){2}\Z/, yaml) assert_equal str, Psych.load(yaml) end end def test_cycle_x str = X.new 'abc' assert_cycle str end def test_dash_dot assert_cycle '-.' assert_cycle '+.' end def test_float_with_no_fractional_before_exponent assert_cycle '0.E+0' end def test_string_subclass_with_anchor y = Psych.unsafe_load <<-eoyml --- body: string: &70121654388580 !ruby/string str: ! 'foo' x: body: *70121654388580 eoyml assert_equal({"body"=>{"string"=>"foo", "x"=>{"body"=>"foo"}}}, y) end def test_self_referential_string y = Psych.unsafe_load <<-eoyml --- string: &70121654388580 !ruby/string str: ! 'foo' body: *70121654388580 eoyml assert_equal({"string"=>"foo"}, y) value = y['string'] assert_equal value, value.instance_variable_get(:@body) end def test_another_subclass_with_attributes y = Psych.unsafe_load Psych.dump Y.new("foo").tap {|o| o.val = 1} assert_equal "foo", y assert_equal Y, y.class assert_equal 1, y.val end def test_backwards_with_syck x = Psych.unsafe_load "--- !str:#{X.name} foo\n\n" assert_equal X, x.class assert_equal 'foo', x end def test_empty_subclass assert_match "!ruby/string:#{X}", Psych.dump(X.new) x = Psych.unsafe_load Psych.dump X.new assert_equal X, x.class end def test_empty_character_subclass assert_match "!ruby/string:#{Z}", Psych.dump(Z.new) x = Psych.unsafe_load Psych.dump Z.new assert_equal Z, x.class end def test_subclass_with_attributes y = Psych.unsafe_load Psych.dump Y.new.tap {|o| o.val = 1} assert_equal Y, y.class assert_equal 1, y.val end def test_string_with_base_60 yaml = Psych.dump '01:03:05' assert_match "'01:03:05'", yaml assert_equal '01:03:05', Psych.load(yaml) end def test_nonascii_string_as_binary string = "hello \x80 world!".dup string.force_encoding 'ascii-8bit' yml = Psych.dump string assert_match(/binary/, yml) assert_equal string, Psych.load(yml) end def test_binary_string_null string = "\x00\x92".b yml = Psych.dump string assert_match(/binary/, yml) assert_equal string, Psych.load(yml) end def test_binary_string string = binary_string yml = Psych.dump string assert_match(/binary/, yml) assert_equal string, Psych.load(yml) end def test_ascii_only_binary_string string = "non bnry string".b yml = Psych.dump string refute_match(/binary/, yml) assert_equal string, Psych.load(yml) end def test_ascii_only_8bit_string string = "abc".encode(Encoding::ASCII_8BIT) yml = Psych.dump string refute_match(/binary/, yml) assert_equal string, Psych.load(yml) end def test_string_with_ivars food = "is delicious".dup ivar = "on rock and roll" food.instance_variable_set(:@we_built_this_city, ivar) Psych.load Psych.dump food assert_equal ivar, food.instance_variable_get(:@we_built_this_city) end def test_binary string = [0, 123,22, 44, 9, 32, 34, 39].pack('C*') assert_cycle string end def test_float_confusion assert_cycle '1.' end def binary_string percentage = 0.31, length = 100 string = ''.b (percentage * length).to_i.times do |i| string << "\x92".b end string << 'a' * (length - string.length) string end end end psych-5.0.2/test/psych/test_struct.rb000066400000000000000000000020001436167465700176740ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' class PsychStructWithIvar < Struct.new(:foo) attr_reader :bar def initialize *args super @bar = 'hello' end end module Psych class TestStruct < TestCase class StructSubclass < Struct.new(:foo) def initialize foo, bar super(foo) @bar = bar end end def test_self_referential_struct ss = StructSubclass.new(nil, 'foo') ss.foo = ss loaded = Psych.unsafe_load(Psych.dump(ss)) assert_instance_of(StructSubclass, loaded.foo) assert_equal(ss, loaded) end def test_roundtrip thing = PsychStructWithIvar.new('bar') struct = Psych.unsafe_load(Psych.dump(thing)) assert_equal 'hello', struct.bar assert_equal 'bar', struct.foo end def test_load obj = Psych.unsafe_load(<<-eoyml) --- !ruby/struct:PsychStructWithIvar :foo: bar :@bar: hello eoyml assert_equal 'hello', obj.bar assert_equal 'bar', obj.foo end end end psych-5.0.2/test/psych/test_symbol.rb000066400000000000000000000006341436167465700176700ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestSymbol < TestCase def test_cycle_empty assert_cycle :'' end def test_cycle_colon assert_cycle :':' end def test_cycle assert_cycle :a end def test_stringy assert_cycle :"1" end def test_load_quoted assert_equal :"1", Psych.load("--- :'1'\n") end end end psych-5.0.2/test/psych/test_tree_builder.rb000066400000000000000000000045471436167465700210370ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' module Psych class TestTreeBuilder < TestCase def setup super @parser = Psych::Parser.new TreeBuilder.new @parser.parse(<<-eoyml) %YAML 1.1 --- - foo - { bar : &A !!str baz, boo : *A } - *A eoyml @tree = @parser.handler.root end def test_stream assert_instance_of Nodes::Stream, @tree assert_location 0, 0, 8, 0, @tree end def test_documents assert_equal 1, @tree.children.length assert_instance_of Nodes::Document, @tree.children.first doc = @tree.children.first assert_equal [1,1], doc.version assert_equal [], doc.tag_directives assert_equal false, doc.implicit assert_location 0, 0, 8, 0, doc end def test_sequence doc = @tree.children.first assert_equal 1, doc.children.length seq = doc.children.first assert_instance_of Nodes::Sequence, seq assert_nil seq.anchor assert_nil seq.tag assert_equal true, seq.implicit assert_equal Nodes::Sequence::BLOCK, seq.style assert_location 2, 0, 8, 0, seq end def test_scalar doc = @tree.children.first seq = doc.children.first assert_equal 3, seq.children.length scalar = seq.children.first assert_instance_of Nodes::Scalar, scalar assert_equal 'foo', scalar.value assert_nil scalar.anchor assert_nil scalar.tag assert_equal true, scalar.plain assert_equal false, scalar.quoted assert_equal Nodes::Scalar::PLAIN, scalar.style assert_location 2, 2, 2, 5, scalar end def test_mapping doc = @tree.children.first seq = doc.children.first map = seq.children[1] assert_instance_of Nodes::Mapping, map assert_location 3, 2, 6, 1, map end def test_alias doc = @tree.children.first seq = doc.children.first assert_equal 3, seq.children.length al = seq.children[2] assert_instance_of Nodes::Alias, al assert_equal 'A', al.anchor assert_location 7, 2, 7, 4, al end private def assert_location(start_line, start_column, end_line, end_column, node) assert_equal start_line, node.start_line assert_equal start_column, node.start_column assert_equal end_line, node.end_line assert_equal end_column, node.end_column end end end psych-5.0.2/test/psych/test_yaml.rb000066400000000000000000001010751436167465700173260ustar00rootroot00000000000000# -*- coding: us-ascii; mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- # frozen_string_literal: true # vim:sw=4:ts=4 # $Id$ # require_relative 'helper' require 'ostruct' # [ruby-core:01946] module Psych_Tests StructTest = Struct::new( :c ) end class Psych_Unit_Tests < Psych::TestCase def teardown Psych.domain_types.clear end def test_y_method assert_raise(NoMethodError) do OpenStruct.new.y 1 end end def test_syck_compat time = Time.utc(2010, 10, 10) yaml = Psych.dump time assert_match "2010-10-10 00:00:00.000000000 Z", yaml end def test_multiline_regexp assert_cycle(Regexp.new("foo\nbar")) end # [ruby-core:34969] def test_regexp_with_n assert_cycle(Regexp.new('',Regexp::NOENCODING)) end # # Tests modified from 00basic.t in Psych.pm # def test_basic_map # Simple map assert_parse_only( { 'one' => 'foo', 'three' => 'baz', 'two' => 'bar' }, < 'simple string', 2 => 42, 3 => '1 Single Quoted String', 4 => 'Psych\'s Double "Quoted" String', 5 => "A block\n with several\n lines.\n", 6 => "A \"chomped\" block", 7 => "A folded\n string\n", 8 => ": started string" }, < A folded string 8: ": started string" EOY ) end # # Test the specification examples # - Many examples have been changes because of whitespace problems that # caused the two to be inequivalent, or keys to be sorted wrong # def test_spec_simple_implicit_sequence # Simple implicit sequence assert_to_yaml( [ 'Mark McGwire', 'Sammy Sosa', 'Ken Griffey' ], < 65, 'avg' => 0.278, 'rbi' => 147 }, < [ 'Boston Red Sox', 'Detroit Tigers', 'New York Yankees' ], 'national' => [ 'New York Mets', 'Chicago Cubs', 'Atlanta Braves' ] }, < 'Mark McGwire', 'hr' => 65, 'avg' => 0.278}, {'name' => 'Sammy Sosa', 'hr' => 63, 'avg' => 0.288} ], < { 'hr' => 65, 'avg' => 0.278 }, 'Sammy Sosa' => { 'hr' => 63, 'avg' => 0.288 } }, < [ 'Mark McGwire', 'Sammy Sosa' ], 'rbi' => [ 'Sammy Sosa', 'Ken Griffey' ] }, < [ 'Mark McGwire', 'Sammy Sosa' ], 'rbi' => [ 'Sammy Sosa', 'Ken Griffey' ] }, <"EDI", "departure"=>"LAX", "fareref"=>"DOGMA", "currency"=>"GBP"}, {"arrival"=>"MEL", "departure"=>"SYD", "fareref"=>"MADF", "currency"=>"AUD"}, {"arrival"=>"MCO", "departure"=>"JFK", "fareref"=>"DFSF", "currency"=>"USD"}], <["fareref", "currency", "departure", "arrival"], "FARES"=>[{"arrival"=>"EDI", "departure"=>"LAX", "fareref"=>"DOGMA", "currency"=>"GBP"}, {"arrival"=>"MEL", "departure"=>"SYD", "fareref"=>"MADF", "currency"=>"AUD"}, {"arrival"=>"MCO", "departure"=>"JFK", "fareref"=>"DFSF", "currency"=>"USD"}]}, < [ Date.new( 2001, 7, 23 ) ], [ 'New York Yankees', 'Atlanta Braves' ] => [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ), Date.new( 2001, 8, 14 ) ] }, < [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ), Date.new( 2001, 8, 14 ) ], [ 'Detroit Tigers', 'Chicago Cubs' ] => [ Date.new( 2001, 7, 23 ) ] }, < 34843, 'date' => Date.new( 2001, 1, 23 ), 'bill-to' => 'Chris Dumars', 'product' => [ { 'item' => 'Super Hoop', 'quantity' => 1 }, { 'item' => 'Basketball', 'quantity' => 4 }, { 'item' => 'Big Shoes', 'quantity' => 1 } ] }, < nil }, [ { 'five' => [ 'six' ] } ], [ 'seven' ] ], [ 'eight', 'nine' ] ], < Mark McGwire\'s year was crippled by a knee injury. EOY ) end def test_spec_preserve_indent # Preserve indented spaces assert_parse_only( "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n", < Sammy Sosa completed another fine season with great stats. 63 Home Runs 0.288 Batting Average What a year! EOY ) end def test_spec_indentation_determines_scope assert_parse_only( { 'name' => 'Mark McGwire', 'accomplishment' => "Mark set a major league home run record in 1998.\n", 'stats' => "65 Home Runs\n0.278 Batting Average\n" }, < Mark set a major league home run record in 1998. stats: | 65 Home Runs 0.278 Batting Average EOY ) end def test_spec_multiline_scalars # Multiline flow scalars assert_parse_only( { 'plain' => 'This unquoted scalar spans many lines.', 'quoted' => "So does this quoted scalar.\n" }, < 12345, 'decimal' => 12345, 'octal' => '014'.oct, 'hexadecimal' => '0xC'.hex }, < 685230, 'decimal' => 685230, 'octal' => 02472256, 'hexadecimal' => 0x0A74AE, 'sexagesimal' => 685230 }, < 1230.15, 'exponential' => 1230.15, 'fixed' => 1230.15, 'negative infinity' => -1.0/0.0 }, < nil, true => true, false => false, 'string' => '12345' }, < 'Chris', 'family' => 'Dumars', 'address' => { 'lines' => "458 Walkman Dr.\nSuite #292\n", 'city' => 'Royal Oak', 'state' => 'MI', 'postal' => 48046 } } assert_parse_only( { 'invoice' => 34843, 'date' => Date.new( 2001, 1, 23 ), 'bill-to' => id001, 'ship-to' => id001, 'product' => [ { 'sku' => 'BL394D', 'quantity' => 4, 'description' => 'Basketball', 'price' => 450.00 }, { 'sku' => 'BL4438H', 'quantity' => 1, 'description' => 'Super Hoop', 'price' => 2392.00 } ], 'tax' => 251.42, 'total' => 4443.52, 'comments' => "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.\n" }, < Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338. EOY ) end def test_spec_log_file doc_ct = 0 Psych::load_stream( < This is an error message for the log file --- Time: 2001-11-23 15:02:31 -05:00 User: ed Warning: > A slightly different error message. --- Date: 2001-11-23 15:03:17 -05:00 User: ed Fatal: > Unknown variable "bar" Stack: - file: TopClass.py line: 23 code: | x = MoreObject("345\\n") - file: MoreClass.py line: 58 code: |- foo = bar EOY ) { |doc| case doc_ct when 0 assert_equal( doc, { 'Time' => mktime( 2001, 11, 23, 15, 01, 42, 00, "-05:00" ), 'User' => 'ed', 'Warning' => "This is an error message for the log file\n" } ) when 1 assert_equal( doc, { 'Time' => mktime( 2001, 11, 23, 15, 02, 31, 00, "-05:00" ), 'User' => 'ed', 'Warning' => "A slightly different error message.\n" } ) when 2 assert_equal( doc, { 'Date' => mktime( 2001, 11, 23, 15, 03, 17, 00, "-05:00" ), 'User' => 'ed', 'Fatal' => "Unknown variable \"bar\"\n", 'Stack' => [ { 'file' => 'TopClass.py', 'line' => 23, 'code' => "x = MoreObject(\"345\\n\")\n" }, { 'file' => 'MoreClass.py', 'line' => 58, 'code' => "foo = bar" } ] } ) end doc_ct += 1 } assert_equal( doc_ct, 3 ) end def test_spec_root_fold y = Psych::load( < 34843, 'date' => Date.new( 2001, 1, 23 ), 'total' => 4443.52 } ) end def test_spec_oneline_docs doc_ct = 0 Psych::load_stream( < { "customers"=> [ { "given"=>"Chris", "type"=>"domain customer", "family"=>"Dumars" } ], "type"=>"domain invoice" } }, <"contains three lines of text.\nThe third one starts with a\n# character. This isn't a comment.\n"}, < 12, 'also int' => 12, 'string' => '12' }, < [ 'ONE: value', 'ONE: value' ], 'different' => [ 'TWO: value' ] }, < 'This scalar has an anchor.', 'override' => a001, 'alias' => a001 }, < The alias node below is a repeated use of this value. alias : *A001 EOY ) end def test_spec_explicit_families Psych.add_domain_type( "somewhere.com/2002", 'type' ) { |type, val| "SOMEWHERE: #{val}" } assert_parse_only( { 'not-date' => '2002-04-28', 'picture' => "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236i^\020' \202\n\001\000;", 'hmm' => "SOMEWHERE: family above is short for\nhttp://somewhere.com/type\n" }, <7, "center"=>{"x"=>73, "y"=>129}, "TYPE"=>"Shape: graph/circle"}, {"finish"=>{"x"=>89, "y"=>102}, "TYPE"=>"Shape: graph/line", "start"=>{"x"=>73, "y"=>129}}, {"TYPE"=>"Shape: graph/text", "value"=>"Pretty vector drawing.", "start"=>{"x"=>73, "y"=>129}, "color"=>16772795}, "Shape Container"]], < [], 'in-line' => [ 'one', 'two', 'three', 'four', 'five' ], 'nested' => [ 'First item in top sequence', [ 'Subordinate sequence entry' ], "A multi-line sequence entry\n", 'Sixth item in top sequence' ] }, < A multi-line sequence entry - Sixth item in top sequence EOY ) end def test_spec_builtin_map # Assortment of mappings assert_parse_only( { 'empty' => {}, 'in-line' => { 'one' => 1, 'two' => 2 }, 'spanning' => { 'one' => 1, 'two' => 2 }, 'nested' => { 'first' => 'First entry', 'second' => { 'key' => 'Subordinate mapping' }, 'third' => [ 'Subordinate sequence', {}, 'Previous mapping is empty.', { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' }, 'The previous entry is equal to the following one.', { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' } ], 12.0 => 'This key is a float.', "?\n" => 'This key had to be protected.', "\a" => 'This key had to be escaped.', "This is a multi-line folded key\n" => "Whose value is also multi-line.\n", [ 'This key', 'is a sequence' ] => [ 'With a sequence value.' ] } }, < ? : This key had to be protected. "\\a" : This key had to be escaped. ? > This is a multi-line folded key : > Whose value is also multi-line. ? - This key - is a sequence : - With a sequence value. # The following parses correctly, # but Ruby 1.6.* fails the comparison! # ? # This: key # is a: mapping # : # with a: mapping value. EOY ) end def test_spec_builtin_literal_blocks # Assortment of literal scalar blocks assert_parse_only( {"both are equal to"=>" This has no newline.", "is equal to"=>"The \\ ' \" characters may be\nfreely used. Leading white\n space is significant.\n\nLine breaks are significant.\nThus this value contains one\nempty line and ends with a\nsingle line break, but does\nnot start with one.\n", "also written as"=>" This has no newline.", "indented and chomped"=>" This has no newline.", "empty"=>"", "literal"=>"The \\ ' \" characters may be\nfreely used. Leading white\n space is significant.\n\nLine breaks are significant.\nThus this value contains one\nempty line and ends with a\nsingle line break, but does\nnot start with one.\n"}, < str1, 'same as "clipped" above' => str1, 'stripped' => str2, 'same as "stripped" above' => str2, 'kept' => str3, 'same as "kept" above' => str3 }, <"a single quote ' must be escaped.", "second"=>"! : \\ etc. can be used freely.", "is same as"=>"this contains six spaces\nand one line break", "empty"=>"", "span"=>"this contains six spaces\nand one line break"}, <"this contains four spaces", "third"=>"a \" or a \\ must be escaped.", "second"=>"! : etc. can be used freely.", "empty"=>"", "fourth"=>"this value ends with an LF.\n", "span"=>"this contains four spaces"}, < mktime( 2001, 12, 14, 21, 59, 43, ".10", "-05:00" ), "canonical" => mktime( 2001, 12, 15, 2, 59, 43, ".10" ), "date (noon UTC)" => Date.new( 2002, 12, 14), "valid iso8601" => mktime( 2001, 12, 14, 21, 59, 43, ".10", "-05:00" ) }, < arrow_gif, 'base64' => arrow_gif, 'description' => "The binary value above is a tiny arrow encoded as a gif image.\n" }, < /George McFly/i }, < 2, :UseVersion => 0 ) # y.add( # { 'hi' => 'hello', 'map' => # { 'good' => 'two' }, # 'time' => Time.now, # 'try' => /^po(.*)$/, # 'bye' => 'goodbye' # } # ) # y.add( { 'po' => 'nil', 'oper' => 90 } ) # y.add( { 'hi' => 'wow!', 'bye' => 'wow!' } ) # y.add( { [ 'Red Socks', 'Boston' ] => [ 'One', 'Two', 'Three' ] } ) # y.add( [ true, false, false ] ) #end # # Test YPath choices parsing # #def test_ypath_parsing # assert_path_segments( "/*/((one|three)/name|place)|//place", # [ ["*", "one", "name"], # ["*", "three", "name"], # ["*", "place"], # ["/", "place"] ] # ) #end # # Tests from Tanaka Akira on [ruby-core] # def test_akira # Commas in plain scalars [ruby-core:1066] assert_to_yaml( {"A"=>"A,","B"=>"B"}, <2, "2"=>3}, <"b"}] * 2, <"b", "c"=>"d"} } # Psych::load( a.to_yaml ) end # # Test Time.now cycle # def test_time_now_cycle # # From Minero Aoki [ruby-core:2305] # #require 'yaml' t = Time.now t = Time.at(t.tv_sec, t.tv_usec) 5.times do assert_cycle(t) end end # # Test Range cycle # def test_range_cycle # # From Minero Aoki [ruby-core:02306] # assert_cycle("a".."z") # # From Nobu Nakada [ruby-core:02311] # assert_cycle(0..1) assert_cycle(1.0e20 .. 2.0e20) assert_cycle("0".."1") assert_cycle(".."..."...") assert_cycle(".rb"..".pl") assert_cycle(".rb"...".pl") assert_cycle('"'...".") assert_cycle("'"...".") end # # Circular references # def test_circular_references a = []; a[0] = a; a[1] = a inspect_str = "[[...], [...]]" assert_equal( inspect_str, Psych::unsafe_load(Psych.dump(a)).inspect ) end # # Test Symbol cycle # def test_symbol_cycle # # From Aaron Schrab [ruby-Bugs:2535] # assert_cycle(:"^foo") end # # Test Numeric cycle # class NumericTest < Numeric def initialize(value) @value = value end def ==(other) @value == other.instance_eval{ @value } end end def test_numeric_cycle assert_cycle(1) # Fixnum assert_cycle(111111111111111111111111111111111) # Bignum assert_cycle(NumericTest.new(3)) # Subclass of Numeric end # # Test empty map/seq in map cycle # def test_empty_map_key # # empty seq as key # assert_cycle({[]=>""}) # # empty map as key # assert_cycle({{}=>""}) end # # contributed by riley lynch [ruby-Bugs-8548] # def test_object_id_collision omap = Psych::Omap.new 1000.times { |i| omap["key_#{i}"] = { "value" => i } } raise "id collision in ordered map" if Psych.dump(omap) =~ /id\d+/ end def test_date_out_of_range Psych::unsafe_load('1900-01-01T00:00:00+00:00') end def test_normal_exit Psych.unsafe_load("2000-01-01 00:00:00.#{"0"*1000} +00:00\n") # '[ruby-core:13735]' end def test_multiline_string_uses_literal_style yaml = Psych.dump("multi\nline\nstring") assert_match("|", yaml) end def test_string_starting_with_non_word_character_uses_double_quotes_without_exclamation_mark yaml = Psych.dump("@123'abc") refute_match("!", yaml) end def test_string_dump_with_colon yaml = Psych.dump 'x: foo' refute_match '!', yaml end def test_string_dump_starting_with_star yaml = Psych.dump '*foo' refute_match '!', yaml end end psych-5.0.2/test/psych/test_yaml_special_cases.rb000066400000000000000000000075611436167465700222110ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' require 'stringio' require 'tempfile' module Psych class TestYamlSpecialCases < TestCase def setup super end def test_empty_string s = "" assert_equal false, Psych.unsafe_load(s) assert_equal [], Psych.load_stream(s) assert_equal false, Psych.parse(s) assert_equal [], Psych.parse_stream(s).transform assert_nil Psych.safe_load(s) end def test_false s = "false" assert_equal false, Psych.load(s) assert_equal [false], Psych.load_stream(s) assert_equal false, Psych.parse(s).transform assert_equal [false], Psych.parse_stream(s).transform assert_equal false, Psych.safe_load(s) end def test_n s = "n" assert_equal "n", Psych.load(s) assert_equal ["n"], Psych.load_stream(s) assert_equal "n", Psych.parse(s).transform assert_equal ["n"], Psych.parse_stream(s).transform assert_equal "n", Psych.safe_load(s) end def test_off s = "off" assert_equal false, Psych.load(s) assert_equal [false], Psych.load_stream(s) assert_equal false, Psych.parse(s).transform assert_equal [false], Psych.parse_stream(s).transform assert_equal false, Psych.safe_load(s) end def test_inf s = "-.inf" assert_equal(-Float::INFINITY, Psych.load(s)) assert_equal([-Float::INFINITY], Psych.load_stream(s)) assert_equal(-Float::INFINITY, Psych.parse(s).transform) assert_equal([-Float::INFINITY], Psych.parse_stream(s).transform) assert_equal(-Float::INFINITY, Psych.safe_load(s)) end def test_NaN s = ".NaN" assert Psych.load(s).nan? assert Psych.load_stream(s).first.nan? assert Psych.parse(s).transform.nan? assert Psych.parse_stream(s).transform.first.nan? assert Psych.safe_load(s).nan? end def test_0xC s = "0xC" assert_equal 12, Psych.load(s) assert_equal [12], Psych.load_stream(s) assert_equal 12, Psych.parse(s).transform assert_equal [12], Psych.parse_stream(s).transform assert_equal 12, Psych.safe_load(s) end def test_arrows s = "<<" assert_equal "<<", Psych.load(s) assert_equal ["<<"], Psych.load_stream(s) assert_equal "<<", Psych.parse(s).transform assert_equal ["<<"], Psych.parse_stream(s).transform assert_equal "<<", Psych.safe_load(s) end def test_arrows_hash s = "<<: {}" assert_equal({}, Psych.load(s)) assert_equal [{}], Psych.load_stream(s) assert_equal({}, Psych.parse(s).transform) assert_equal [{}], Psych.parse_stream(s).transform assert_equal({}, Psych.safe_load(s)) end def test_thousand s = "- 1000\n- +1000\n- 1_000" assert_equal [1000, 1000, 1000], Psych.load(s) assert_equal [[1000, 1000, 1000]], Psych.load_stream(s) assert_equal [1000, 1000, 1000], Psych.parse(s).transform assert_equal [[1000, 1000, 1000]], Psych.parse_stream(s).transform assert_equal [1000, 1000, 1000], Psych.safe_load(s) end def test_8 s = "[8, 08, 0o10, 010]" assert_equal [8, "08", "0o10", 8], Psych.load(s) assert_equal [[8, "08", "0o10", 8]], Psych.load_stream(s) assert_equal [8, "08", "0o10", 8], Psych.parse(s).transform assert_equal [[8, "08", "0o10", 8]], Psych.parse_stream(s).transform assert_equal [8, "08", "0o10", 8], Psych.safe_load(s) end def test_null s = "null" assert_nil Psych.load(s) assert_equal [nil], Psych.load_stream(s) assert_nil Psych.parse(s).transform assert_equal [nil], Psych.parse_stream(s).transform assert_nil Psych.safe_load(s) end private def special_case_cycle(object) %w[load load_stream parse parse_stream safe_load].map do |m| Psych.public_send(m, object) end end end end psych-5.0.2/test/psych/test_yamldbm.rb000066400000000000000000000117721436167465700200150ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' require 'tmpdir' begin require 'yaml/dbm' rescue LoadError end module Psych ::Psych::DBM = ::YAML::DBM unless defined?(::Psych::DBM) class YAMLDBMTest < TestCase def setup @dir = Dir.mktmpdir("rubytest-file") File.chown(-1, Process.gid, @dir) @yamldbm_file = make_tmp_filename("yamldbm") @yamldbm = YAML::DBM.new(@yamldbm_file) end def teardown @yamldbm.clear @yamldbm.close FileUtils.remove_entry_secure @dir end def make_tmp_filename(prefix) @dir + "/" + prefix + File.basename(__FILE__) + ".#{$$}.test" end def test_store @yamldbm.store('a','b') @yamldbm.store('c','d') assert_equal 'b', @yamldbm['a'] assert_equal 'd', @yamldbm['c'] assert_nil @yamldbm['e'] end def test_store_using_carret @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' assert_equal 'b', @yamldbm['a'] assert_equal 'd', @yamldbm['c'] assert_nil @yamldbm['e'] end def test_to_a @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' assert_equal([['a','b'],['c','d']], @yamldbm.to_a.sort) end def test_to_hash @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' assert_equal({'a'=>'b','c'=>'d'}, @yamldbm.to_hash) end def test_has_value? @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' assert_equal true, @yamldbm.has_value?('b') assert_equal true, @yamldbm.has_value?('d') assert_equal false, @yamldbm.has_value?('f') end # Note: # YAML::DBM#index makes warning from internal of ::DBM#index. # It says 'DBM#index is deprecated; use DBM#key', but DBM#key # behaves not same as DBM#index. # # def test_index # @yamldbm['a'] = 'b' # @yamldbm['c'] = 'd' # assert_equal 'a', @yamldbm.index('b') # assert_equal 'c', @yamldbm.index('d') # assert_nil @yamldbm.index('f') # end def test_key @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' assert_equal 'a', @yamldbm.key('b') assert_equal 'c', @yamldbm.key('d') assert_nil @yamldbm.key('f') end def test_fetch assert_equal('bar', @yamldbm['foo']='bar') assert_equal('bar', @yamldbm.fetch('foo')) assert_nil @yamldbm.fetch('bar') assert_equal('baz', @yamldbm.fetch('bar', 'baz')) assert_equal('foobar', @yamldbm.fetch('bar') {|key| 'foo' + key }) end def test_shift @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' assert_equal([['a','b'], ['c','d']], [@yamldbm.shift, @yamldbm.shift].sort) assert_nil @yamldbm.shift end def test_invert @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' assert_equal({'b'=>'a','d'=>'c'}, @yamldbm.invert) end def test_update @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' @yamldbm.update({'c'=>'d','e'=>'f'}) assert_equal 'b', @yamldbm['a'] assert_equal 'd', @yamldbm['c'] assert_equal 'f', @yamldbm['e'] end def test_replace @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' @yamldbm.replace({'c'=>'d','e'=>'f'}) assert_nil @yamldbm['a'] assert_equal 'd', @yamldbm['c'] assert_equal 'f', @yamldbm['e'] end def test_delete @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' assert_equal 'b', @yamldbm.delete('a') assert_nil @yamldbm['a'] assert_equal 'd', @yamldbm['c'] assert_nil @yamldbm.delete('e') end def test_delete_if @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' @yamldbm['e'] = 'f' @yamldbm.delete_if {|k,v| k == 'a'} assert_nil @yamldbm['a'] assert_equal 'd', @yamldbm['c'] assert_equal 'f', @yamldbm['e'] @yamldbm.delete_if {|k,v| v == 'd'} assert_nil @yamldbm['c'] assert_equal 'f', @yamldbm['e'] @yamldbm.delete_if {|k,v| false } assert_equal 'f', @yamldbm['e'] end def test_reject @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' @yamldbm['e'] = 'f' assert_equal({'c'=>'d','e'=>'f'}, @yamldbm.reject {|k,v| k == 'a'}) assert_equal({'a'=>'b','e'=>'f'}, @yamldbm.reject {|k,v| v == 'd'}) assert_equal({'a'=>'b','c'=>'d','e'=>'f'}, @yamldbm.reject {false}) end def test_values assert_equal [], @yamldbm.values @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' assert_equal ['b','d'], @yamldbm.values.sort end def test_values_at @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' assert_equal ['b','d'], @yamldbm.values_at('a','c') end def test_selsct @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' @yamldbm['e'] = 'f' assert_equal(['b','d'], @yamldbm.select('a','c')) end def test_selsct_with_block @yamldbm['a'] = 'b' @yamldbm['c'] = 'd' @yamldbm['e'] = 'f' assert_equal([['a','b']], @yamldbm.select {|k,v| k == 'a'}) assert_equal([['c','d']], @yamldbm.select {|k,v| v == 'd'}) assert_equal([], @yamldbm.select {false}) end end end if defined?(YAML::DBM) && defined?(Psych) psych-5.0.2/test/psych/test_yamlstore.rb000066400000000000000000000046421436167465700204050ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'helper' require 'yaml/store' require 'tmpdir' module Psych class YAML::Store alias :old_load :load def load(content) table = YAML.load(content, fallback: false) if table == false {} else table end end end unless defined?(Psych::Store) Psych::Store = YAML::Store end class YAMLStoreTest < TestCase def setup @dir = Dir.mktmpdir("rubytest-file") File.chown(-1, Process.gid, @dir) @yamlstore_file = make_tmp_filename("yamlstore") @yamlstore = YAML::Store.new(@yamlstore_file) end def teardown FileUtils.remove_entry_secure @dir end def make_tmp_filename(prefix) @dir + "/" + prefix + File.basename(__FILE__) + ".#{$$}.test" end def test_opening_new_file_in_readonly_mode_should_result_in_empty_values @yamlstore.transaction(true) do assert_nil @yamlstore["foo"] assert_nil @yamlstore["bar"] end end def test_opening_new_file_in_readwrite_mode_should_result_in_empty_values @yamlstore.transaction do assert_nil @yamlstore["foo"] assert_nil @yamlstore["bar"] end end def test_data_should_be_loaded_correctly_when_in_readonly_mode @yamlstore.transaction do @yamlstore["foo"] = "bar" end @yamlstore.transaction(true) do assert_equal "bar", @yamlstore["foo"] end end def test_data_should_be_loaded_correctly_when_in_readwrite_mode @yamlstore.transaction do @yamlstore["foo"] = "bar" end @yamlstore.transaction do assert_equal "bar", @yamlstore["foo"] end end def test_changes_after_commit_are_discarded @yamlstore.transaction do @yamlstore["foo"] = "bar" @yamlstore.commit @yamlstore["foo"] = "baz" end @yamlstore.transaction(true) do assert_equal "bar", @yamlstore["foo"] end end def test_changes_are_not_written_on_abort @yamlstore.transaction do @yamlstore["foo"] = "bar" @yamlstore.abort end @yamlstore.transaction(true) do assert_nil @yamlstore["foo"] end end def test_writing_inside_readonly_transaction_raises_error assert_raise(PStore::Error) do @yamlstore.transaction(true) do @yamlstore["foo"] = "bar" end end end end end if defined?(Psych) psych-5.0.2/test/psych/visitors/000077500000000000000000000000001436167465700166565ustar00rootroot00000000000000psych-5.0.2/test/psych/visitors/test_depth_first.rb000066400000000000000000000023251436167465700225570ustar00rootroot00000000000000# frozen_string_literal: true require 'psych/helper' module Psych module Visitors class TestDepthFirst < TestCase class Collector < Struct.new(:calls) def initialize(calls = []) super end def call obj calls << obj end end def test_scalar collector = Collector.new visitor = Visitors::DepthFirst.new collector visitor.accept Psych.parse_stream '--- hello' assert_equal 3, collector.calls.length end def test_sequence collector = Collector.new visitor = Visitors::DepthFirst.new collector visitor.accept Psych.parse_stream "---\n- hello" assert_equal 4, collector.calls.length end def test_mapping collector = Collector.new visitor = Visitors::DepthFirst.new collector visitor.accept Psych.parse_stream "---\nhello: world" assert_equal 5, collector.calls.length end def test_alias collector = Collector.new visitor = Visitors::DepthFirst.new collector visitor.accept Psych.parse_stream "--- &yay\n- foo\n- *yay\n" assert_equal 5, collector.calls.length end end end end psych-5.0.2/test/psych/visitors/test_emitter.rb000066400000000000000000000072221436167465700217160ustar00rootroot00000000000000# frozen_string_literal: true require 'psych/helper' module Psych module Visitors class TestEmitter < TestCase def setup super @io = StringIO.new @visitor = Visitors::Emitter.new @io end def test_options io = StringIO.new visitor = Visitors::Emitter.new io, :indentation => 3 s = Nodes::Stream.new doc = Nodes::Document.new mapping = Nodes::Mapping.new m2 = Nodes::Mapping.new m2.children << Nodes::Scalar.new('a') m2.children << Nodes::Scalar.new('b') mapping.children << Nodes::Scalar.new('key') mapping.children << m2 doc.children << mapping s.children << doc visitor.accept s assert_match(/^[ ]{3}a/, io.string) end def test_stream s = Nodes::Stream.new @visitor.accept s assert_equal '', @io.string end def test_document s = Nodes::Stream.new doc = Nodes::Document.new [1,1] scalar = Nodes::Scalar.new 'hello world' doc.children << scalar s.children << doc @visitor.accept s assert_match(/1.1/, @io.string) assert_equal @io.string, s.yaml end def test_document_implicit_end s = Nodes::Stream.new doc = Nodes::Document.new mapping = Nodes::Mapping.new mapping.children << Nodes::Scalar.new('key') mapping.children << Nodes::Scalar.new('value') doc.children << mapping s.children << doc @visitor.accept s assert_match(/key: value/, @io.string) assert_equal @io.string, s.yaml assert(/\.\.\./ !~ s.yaml) end def test_scalar s = Nodes::Stream.new doc = Nodes::Document.new scalar = Nodes::Scalar.new 'hello world' doc.children << scalar s.children << doc @visitor.accept s assert_match(/hello/, @io.string) assert_equal @io.string, s.yaml end def test_scalar_with_tag s = Nodes::Stream.new doc = Nodes::Document.new scalar = Nodes::Scalar.new 'hello world', nil, '!str', false, false, 5 doc.children << scalar s.children << doc @visitor.accept s assert_match(/str/, @io.string) assert_match(/hello/, @io.string) assert_equal @io.string, s.yaml end def test_sequence s = Nodes::Stream.new doc = Nodes::Document.new scalar = Nodes::Scalar.new 'hello world' seq = Nodes::Sequence.new seq.children << scalar doc.children << seq s.children << doc @visitor.accept s assert_match(/- hello/, @io.string) assert_equal @io.string, s.yaml end def test_mapping s = Nodes::Stream.new doc = Nodes::Document.new mapping = Nodes::Mapping.new mapping.children << Nodes::Scalar.new('key') mapping.children << Nodes::Scalar.new('value') doc.children << mapping s.children << doc @visitor.accept s assert_match(/key: value/, @io.string) assert_equal @io.string, s.yaml end def test_alias s = Nodes::Stream.new doc = Nodes::Document.new mapping = Nodes::Mapping.new mapping.children << Nodes::Scalar.new('key', 'A') mapping.children << Nodes::Alias.new('A') doc.children << mapping s.children << doc @visitor.accept s assert_match(/&A key: \*A/, @io.string) assert_equal @io.string, s.yaml end end end end psych-5.0.2/test/psych/visitors/test_to_ruby.rb000066400000000000000000000250461436167465700217340ustar00rootroot00000000000000# coding: US-ASCII # frozen_string_literal: true require 'psych/helper' module Psych module Visitors class TestToRuby < TestCase def setup super @visitor = ToRuby.create end def test_object mapping = Nodes::Mapping.new nil, "!ruby/object" mapping.children << Nodes::Scalar.new('foo') mapping.children << Nodes::Scalar.new('bar') o = mapping.to_ruby assert_equal 'bar', o.instance_variable_get(:@foo) end def test_tz_00_00_loads_without_error assert Psych.unsafe_load('1900-01-01T00:00:00+00:00') end def test_legacy_struct Struct.send(:remove_const, :AWESOME) if Struct.const_defined?(:AWESOME) foo = Struct.new('AWESOME', :bar) assert_equal foo.new('baz'), Psych.unsafe_load(<<-eoyml) !ruby/struct:AWESOME bar: baz eoyml end def test_binary gif = "GIF89a\f\x00\f\x00\x84\x00\x00\xFF\xFF\xF7\xF5\xF5\xEE\xE9\xE9\xE5fff\x00\x00\x00\xE7\xE7\xE7^^^\xF3\xF3\xED\x8E\x8E\x8E\xE0\xE0\xE0\x9F\x9F\x9F\x93\x93\x93\xA7\xA7\xA7\x9E\x9E\x9Eiiiccc\xA3\xA3\xA3\x84\x84\x84\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9!\xFE\x0EMade with GIMP\x00,\x00\x00\x00\x00\f\x00\f\x00\x00\x05, \x8E\x810\x9E\xE3@\x14\xE8i\x10\xC4\xD1\x8A\b\x1C\xCF\x80M$z\xEF\xFF0\x85p\xB8\xB01f\r\e\xCE\x01\xC3\x01\x1E\x10' \x82\n\x01\x00;" hash = Psych.load(<<-'eoyaml') canonical: !!binary "\ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5\ OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+\ +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC\ AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=" generic: !binary | R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5 OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+ +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs= description: The binary value above is a tiny arrow encoded as a gif image. eoyaml assert_equal gif, hash['canonical'] assert_equal gif, hash['generic'] end A = Struct.new(:foo) def test_struct s = A.new('bar') mapping = Nodes::Mapping.new nil, "!ruby/struct:#{s.class}" mapping.children << Nodes::Scalar.new('foo') mapping.children << Nodes::Scalar.new('bar') ruby = mapping.to_ruby assert_equal s.class, ruby.class assert_equal s.foo, ruby.foo assert_equal s, ruby end def test_anon_struct_legacy s = Struct.new(:foo).new('bar') mapping = Nodes::Mapping.new nil, '!ruby/struct:' mapping.children << Nodes::Scalar.new('foo') mapping.children << Nodes::Scalar.new('bar') assert_equal s.foo, mapping.to_ruby.foo end def test_anon_struct s = Struct.new(:foo).new('bar') mapping = Nodes::Mapping.new nil, '!ruby/struct' mapping.children << Nodes::Scalar.new('foo') mapping.children << Nodes::Scalar.new('bar') assert_equal s.foo, mapping.to_ruby.foo end def test_exception exc = ::Exception.new 'hello' mapping = Nodes::Mapping.new nil, '!ruby/exception' mapping.children << Nodes::Scalar.new('message') mapping.children << Nodes::Scalar.new('hello') ruby = mapping.to_ruby assert_equal exc.class, ruby.class assert_equal exc.message, ruby.message end def test_regexp node = Nodes::Scalar.new('/foo/', nil, '!ruby/regexp') assert_equal(/foo/, node.to_ruby) node = Nodes::Scalar.new('/foo/m', nil, '!ruby/regexp') assert_equal(/foo/m, node.to_ruby) node = Nodes::Scalar.new('/foo/ix', nil, '!ruby/regexp') assert_equal(/foo/ix, node.to_ruby) end def test_time now = Time.now zone = now.strftime('%z') zone = " #{zone[0,3]}:#{zone[3,5]}" formatted = now.strftime("%Y-%m-%d %H:%M:%S.%9N") + zone assert_equal now, Nodes::Scalar.new(formatted).to_ruby end def test_time_utc now = Time.now.utc formatted = now.strftime("%Y-%m-%d %H:%M:%S") + ".%09dZ" % [now.nsec] assert_equal now, Nodes::Scalar.new(formatted).to_ruby end def test_time_utc_no_z now = Time.now.utc formatted = now.strftime("%Y-%m-%d %H:%M:%S") + ".%09d" % [now.nsec] assert_equal now, Nodes::Scalar.new(formatted).to_ruby end def test_date d = '1980-12-16' actual = Date.strptime(d, '%Y-%m-%d') date = Nodes::Scalar.new(d, nil, 'tag:yaml.org,2002:timestamp', false) assert_equal actual, date.to_ruby end def test_rational mapping = Nodes::Mapping.new nil, '!ruby/object:Rational' mapping.children << Nodes::Scalar.new('denominator') mapping.children << Nodes::Scalar.new('2') mapping.children << Nodes::Scalar.new('numerator') mapping.children << Nodes::Scalar.new('1') assert_equal Rational(1,2), mapping.to_ruby end def test_complex mapping = Nodes::Mapping.new nil, '!ruby/object:Complex' mapping.children << Nodes::Scalar.new('image') mapping.children << Nodes::Scalar.new('2') mapping.children << Nodes::Scalar.new('real') mapping.children << Nodes::Scalar.new('1') assert_equal Complex(1,2), mapping.to_ruby end def test_complex_string node = Nodes::Scalar.new '3+4i', nil, "!ruby/object:Complex" assert_equal Complex(3, 4), node.to_ruby end def test_rational_string node = Nodes::Scalar.new '1/2', nil, "!ruby/object:Rational" assert_equal Rational(1, 2), node.to_ruby end def test_range_string node = Nodes::Scalar.new '1..2', nil, "!ruby/range" assert_equal 1..2, node.to_ruby end def test_range_string_triple node = Nodes::Scalar.new '1...3', nil, "!ruby/range" assert_equal 1...3, node.to_ruby end def test_integer i = Nodes::Scalar.new('1', nil, 'tag:yaml.org,2002:int') assert_equal 1, i.to_ruby assert_equal 1, Nodes::Scalar.new('1').to_ruby i = Nodes::Scalar.new('-1', nil, 'tag:yaml.org,2002:int') assert_equal(-1, i.to_ruby) assert_equal(-1, Nodes::Scalar.new('-1').to_ruby) assert_equal 1, Nodes::Scalar.new('+1').to_ruby end def test_int_ignore ['1,000', '1_000'].each do |num| i = Nodes::Scalar.new(num, nil, 'tag:yaml.org,2002:int') assert_equal 1000, i.to_ruby assert_equal 1000, Nodes::Scalar.new(num).to_ruby end end def test_float_ignore ['1,000.3', '1_000.3'].each do |num| i = Nodes::Scalar.new(num, nil, 'tag:yaml.org,2002:float') assert_equal 1000.3, i.to_ruby i = Nodes::Scalar.new(num, nil, '!float') assert_equal 1000.3, i.to_ruby assert_equal 1000.3, Nodes::Scalar.new(num).to_ruby end end # http://yaml.org/type/bool.html def test_boolean_true %w{ yes Yes YES true True TRUE on On ON }.each do |t| i = Nodes::Scalar.new(t, nil, 'tag:yaml.org,2002:bool') assert_equal true, i.to_ruby assert_equal true, Nodes::Scalar.new(t).to_ruby end end # http://yaml.org/type/bool.html def test_boolean_false %w{ no No NO false False FALSE off Off OFF }.each do |t| i = Nodes::Scalar.new(t, nil, 'tag:yaml.org,2002:bool') assert_equal false, i.to_ruby assert_equal false, Nodes::Scalar.new(t).to_ruby end end def test_float i = Nodes::Scalar.new('12', nil, 'tag:yaml.org,2002:float') assert_equal 12.0, i.to_ruby i = Nodes::Scalar.new('1.2', nil, 'tag:yaml.org,2002:float') assert_equal 1.2, i.to_ruby i = Nodes::Scalar.new('1.2') assert_equal 1.2, i.to_ruby assert_equal 1, Nodes::Scalar.new('.Inf').to_ruby.infinite? assert_equal 1, Nodes::Scalar.new('.inf').to_ruby.infinite? assert_equal 1, Nodes::Scalar.new('.Inf', nil, 'tag:yaml.org,2002:float').to_ruby.infinite? assert_equal(-1, Nodes::Scalar.new('-.inf').to_ruby.infinite?) assert_equal(-1, Nodes::Scalar.new('-.Inf').to_ruby.infinite?) assert_equal(-1, Nodes::Scalar.new('-.Inf', nil, 'tag:yaml.org,2002:float').to_ruby.infinite?) assert Nodes::Scalar.new('.NaN').to_ruby.nan? assert Nodes::Scalar.new('.NaN', nil, 'tag:yaml.org,2002:float').to_ruby.nan? end def test_exp_float exp = 1.2e+30 i = Nodes::Scalar.new(exp.to_s, nil, 'tag:yaml.org,2002:float') assert_equal exp, i.to_ruby assert_equal exp, Nodes::Scalar.new(exp.to_s).to_ruby end def test_scalar scalar = Nodes::Scalar.new('foo') assert_equal 'foo', @visitor.accept(scalar) assert_equal 'foo', scalar.to_ruby end def test_sequence seq = Nodes::Sequence.new seq.children << Nodes::Scalar.new('foo') seq.children << Nodes::Scalar.new('bar') assert_equal %w{ foo bar }, seq.to_ruby end def test_mapping mapping = Nodes::Mapping.new mapping.children << Nodes::Scalar.new('foo') mapping.children << Nodes::Scalar.new('bar') assert_equal({'foo' => 'bar'}, mapping.to_ruby) end def test_document doc = Nodes::Document.new doc.children << Nodes::Scalar.new('foo') assert_equal 'foo', doc.to_ruby end def test_stream a = Nodes::Document.new a.children << Nodes::Scalar.new('foo') b = Nodes::Document.new b.children << Nodes::Scalar.new('bar') stream = Nodes::Stream.new stream.children << a stream.children << b assert_equal %w{ foo bar }, stream.to_ruby end def test_alias seq = Nodes::Sequence.new seq.children << Nodes::Scalar.new('foo', 'A') seq.children << Nodes::Alias.new('A') list = seq.to_ruby assert_equal %w{ foo foo }, list assert_equal list[0].object_id, list[1].object_id end def test_mapping_with_str_tag mapping = Nodes::Mapping.new(nil, '!strawberry') mapping.children << Nodes::Scalar.new('foo') mapping.children << Nodes::Scalar.new('bar') assert_equal({'foo' => 'bar'}, mapping.to_ruby) end end end end psych-5.0.2/test/psych/visitors/test_yaml_tree.rb000066400000000000000000000120371436167465700222260ustar00rootroot00000000000000# frozen_string_literal: true require 'psych/helper' require 'delegate' module Psych module Visitors class TestYAMLTree < TestCase class TestDelegatorClass < Delegator def initialize(obj); super; @obj = obj; end def __setobj__(obj); @obj = obj; end def __getobj__; @obj if defined?(@obj); end end class TestSimpleDelegatorClass < SimpleDelegator end def setup super @v = Visitors::YAMLTree.create end def test_tree_can_be_called_twice @v.start @v << Object.new t = @v.tree assert_equal t, @v.tree end def test_yaml_tree_can_take_an_emitter io = StringIO.new e = Psych::Emitter.new io v = Visitors::YAMLTree.create({}, e) v.start v << "hello world" v.finish assert_match "hello world", io.string end def test_binary_formatting gif = "GIF89a\f\x00\f\x00\x84\x00\x00\xFF\xFF\xF7\xF5\xF5\xEE\xE9\xE9\xE5fff\x00\x00\x00\xE7\xE7\xE7^^^\xF3\xF3\xED\x8E\x8E\x8E\xE0\xE0\xE0\x9F\x9F\x9F\x93\x93\x93\xA7\xA7\xA7\x9E\x9E\x9Eiiiccc\xA3\xA3\xA3\x84\x84\x84\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9!\xFE\x0EMade with GIMP\x00,\x00\x00\x00\x00\f\x00\f\x00\x00\x05, \x8E\x810\x9E\xE3@\x14\xE8i\x10\xC4\xD1\x8A\b\x1C\xCF\x80M$z\xEF\xFF0\x85p\xB8\xB01f\r\e\xCE\x01\xC3\x01\x1E\x10' \x82\n\x01\x00;".b @v << gif scalar = @v.tree.children.first.children.first assert_equal Psych::Nodes::Scalar::LITERAL, scalar.style end def test_object_has_no_class yaml = Psych.dump(Object.new) assert(Psych.dump(Object.new) !~ /Object/, yaml) end def test_struct_const foo = Struct.new("Foo", :bar) assert_cycle foo.new('bar') Struct.instance_eval { remove_const(:Foo) } end A = Struct.new(:foo) def test_struct assert_cycle A.new('bar') end def test_struct_anon s = Struct.new(:foo).new('bar') obj = Psych.unsafe_load(Psych.dump(s)) assert_equal s.foo, obj.foo end def test_override_method s = Struct.new(:method).new('override') obj = Psych.unsafe_load(Psych.dump(s)) assert_equal s.method, obj.method end def test_exception ex = Exception.new 'foo' loaded = Psych.unsafe_load(Psych.dump(ex)) assert_equal ex.message, loaded.message assert_equal ex.class, loaded.class end def test_regexp assert_cycle(/foo/) assert_cycle(/foo/i) assert_cycle(/foo/mx) end def test_time t = Time.now assert_equal t, Psych.unsafe_load(Psych.dump(t)) end def test_date date = Date.strptime('2002-12-14', '%Y-%m-%d') assert_cycle date end def test_rational assert_cycle Rational(1,2) end def test_complex assert_cycle Complex(1,2) end def test_scalar assert_cycle 'foo' assert_cycle ':foo' assert_cycle '' assert_cycle ':' end def test_boolean assert_cycle true assert_cycle 'true' assert_cycle false assert_cycle 'false' end def test_range_inclusive assert_cycle 1..2 end def test_range_exclusive assert_cycle 1...2 end def test_anon_class assert_raise(TypeError) do @v.accept Class.new end assert_raise(TypeError) do Psych.dump(Class.new) end end def test_hash assert_cycle('a' => 'b') end def test_list assert_cycle(%w{ a b }) assert_cycle([1, 2.2]) end def test_symbol assert_cycle :foo end def test_int assert_cycle 1 assert_cycle(-1) assert_cycle '1' assert_cycle '-1' end def test_float assert_cycle 1.2 assert_cycle '1.2' assert Psych.load(Psych.dump(0.0 / 0.0)).nan? assert_equal 1, Psych.load(Psych.dump(1 / 0.0)).infinite? assert_equal(-1, Psych.load(Psych.dump(-1 / 0.0)).infinite?) end def test_string assert_match(/'017'/, Psych.dump({'a' => '017'})) assert_match(/'019'/, Psych.dump({'a' => '019'})) assert_match(/'01818'/, Psych.dump({'a' => '01818'})) end # http://yaml.org/type/null.html def test_nil assert_cycle nil assert_nil Psych.load('null') assert_nil Psych.load('Null') assert_nil Psych.load('NULL') assert_nil Psych.load('~') assert_equal({'foo' => nil}, Psych.load('foo: ')) assert_cycle 'null' assert_cycle 'nUll' assert_cycle '~' end def test_delegator assert_cycle(TestDelegatorClass.new([1, 2, 3])) end def test_simple_delegator assert_cycle(TestSimpleDelegatorClass.new([1, 2, 3])) end end end end