модуль Psych
Обзор
Psych — это парсер и генератор YAML. Psych использует libyaml [Главная страница: pyyaml.org/wiki/LibYAML] или [Репозиторий HG: bitbucket.org/xi/libyaml] для своих возможностей парсинга и генерации YAML. Помимо обёртки над libyaml, Psych также умеет сериализовать и десериализовать большинство объектов Ruby в и из формата YAML.
Мне нужно разобрать или сгенерировать YAML ПРЯМО СЕЙЧАС!
# Parse some YAML
Psych.load("--- foo") # => "foo"
# Emit some YAML
Psych.dump("foo") # => "--- foo\n...\n"
{ :a => 'b'}.to_yaml # => "---\n:a: b\n"
У вас больше времени? Продолжайте читать!
Парсинг YAML
Psych предоставляет различные интерфейсы для парсинга документа YAML, от низкого до высокого уровня, в зависимости от ваших потребностей. На самом низком уровне — это парсер на основе событий. На среднем уровне — доступ к исходному AST YAML, а на самом высоком — возможность распаковки YAML в объекты Ruby.
Генерация YAML
Psych предоставляет различные интерфейсы, начиная от низкого и заканчивая высоким уровнем, для создания документов YAML. Очень похожие на интерфейсы парсинга YAML, Psych предоставляет на самом низком уровне систему на основе событий, на среднем — создание AST YAML, а на самом высоком — преобразование объекта Ruby непосредственно в документ YAML.
API высокого уровня
Парсинг
Парсер YAML высокого уровня, предоставляемый Psych, просто принимает YAML в качестве входных данных и возвращает структуру данных Ruby. Для получения информации об использовании парсера высокого уровня см. Psych.load
Чтение из строки
Psych.load("--- a") # => 'a'
Psych.load("---\n - a\n - b") # => ['a', 'b']
Чтение из файла
Psych.load_file("database.yml")
Обработка исключений
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
Генерация
Интерфейс генератора высокого уровня самый простой. Psych просто принимает структуру данных Ruby и преобразует её в документ YAML. Подробнее о выгрузке структуры данных Ruby см. Psych.dump.
Запись в строку
# 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) # => #<StringIO:0x000001009d0890> # 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)
Запись в файл
В настоящее время нет прямого API для выгрузки структуры Ruby в файл:
File.open('database.yml', 'w') do |file|
file.write(Psych.dump(['a', 'b']))
end
API среднего уровня
Парсинг
Psych предоставляет доступ к AST, созданной при парсинге документа YAML. Это дерево построено с помощью Psych::Parser и Psych::TreeBuilder. AST можно свободно просматривать и изменять. Для получения дополнительной информации о работе с синтаксическими деревьями YAML см. Psych::parse_stream, Psych::Nodes и Psych::Nodes::Node.
Чтение из строки
# Returns Psych::Nodes::Stream
Psych.parse_stream("---\n - a\n - b")
# Returns Psych::Nodes::Document
Psych.parse("---\n - a\n - b")
Чтение из файла
# Returns Psych::Nodes::Stream
Psych.parse_stream(File.read('database.yml'))
# Returns Psych::Nodes::Document
Psych.parse_file('database.yml')
Обработка исключений
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
Генерация
На среднем уровне происходит построение AST. Эта AST идентична AST, используемой при парсинге документа YAML. Пользователи могут самостоятельно создавать AST, и AST знает, как преобразовать себя в документ YAML. Для получения дополнительной информации о создании AST YAML см. Psych::Nodes, Psych::Nodes::Node и Psych::TreeBuilder.
Запись в строку
# 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"
Запись в файл
# 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
API низкого уровня
Парсинг
Парсер самого низкого уровня следует использовать, когда входные данные YAML уже известны, и разработчик не хочет платить за создание AST или автоматическое определение и преобразование в объекты Ruby. Для получения дополнительной информации об использовании парсера на основе событий см. Psych::Parser.
Чтение в структуру
parser = Psych::Parser.new(TreeBuilder.new) # => #<Psych::Parser>
parser = Psych.parser # it's an alias for the above
parser.parse("---\n - a\n - b") # => #<Psych::Parser>
parser.handler # => #<Psych::TreeBuilder>
parser.handler.root # => #<Psych::Nodes::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
Генерация
Генератор самого низкого уровня — это система на основе событий. События отправляются объекту Psych::Emitter. Этот объект знает, как преобразовать события в документ YAML. Этот интерфейс следует использовать, когда формат документа известен заранее или скорость является важным фактором. Для получения дополнительной информации см. Psych::Emitter.
Запись в структуру Ruby
Psych.parser.parse("--- a") # => #<Psych::Parser>
parser.handler.first # => #<Psych::Nodes::Stream>
parser.handler.first.to_ruby # => ["a"]
parser.handler.root.first # => #<Psych::Nodes::Document>
parser.handler.root.first.to_ruby # => "a"
# You can instantiate an Emitter manually
Psych::Visitors::ToRuby.new.accept(parser.handler.root.first)
# => "a"
Константы
Публичные методы класса
# File ext/psych/lib/psych.rb, line 506
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 Преобразует объект Ruby o в строку YAML. Необязательные параметры options могут быть переданы для управления форматом вывода. Если передан объект IO, YAML будет выгружен в этот объект IO.
В настоящее время поддерживаются следующие параметры:
-
:indentation -
Количество пробелов, используемых для отступа. Допустимое значение должно находиться в диапазоне
0..9, в противном случае параметр игнорируется.Значение по умолчанию:
2. -
:line_width -
Максимальное количество символов для переноса строки.
Значение по умолчанию:
0(означает «перенос на 81 символе»). -
:canonical -
Записать «каноническую» форму YAML (очень подробную, но строго формальную).
Значение по умолчанию:
false. -
:header -
Записать
%YAML [version]в начале документа.Значение по умолчанию:
false.
Пример:
# 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) # => #<StringIO:0x000001009d0890> # 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)
# File ext/psych/lib/psych.rb, line 523
def self.dump_stream *objects
visitor = Psych::Visitors::YAMLTree.create({})
objects.each do |o|
visitor << o
end
visitor.tree.yaml
end Выгружает список объектов как отдельные документы в поток документов.
Пример:
Psych.dump_stream("foo\n ", {}) # => "--- ! \"foo\\n \"\n--- {}\n"
static VALUE libyaml_version(VALUE module)
{
int major, minor, patch;
VALUE list[3];
yaml_get_version(&major, &minor, &patch);
list[0] = INT2NUM((long)major);
list[1] = INT2NUM((long)minor);
list[2] = INT2NUM((long)patch);
return rb_ary_new4((long)3, list);
} Возвращает версию используемой библиотеки libyaml
# File ext/psych/lib/psych.rb, line 271
def self.load yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: false, symbolize_names: false
if legacy_filename != NOT_GIVEN
warn_with_uplevel 'Passing filename with the 2nd argument of Psych.load is deprecated. Use keyword argument like Psych.load(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
filename = legacy_filename
end
result = parse(yaml, filename: filename)
return fallback unless result
result = result.to_ruby if result
symbolize_names!(result) if symbolize_names
result
end Загружает yaml в структуру данных Ruby. Если предоставлено несколько документов, будет возвращен объект, содержащийся в первом документе. filename будет использоваться в сообщении об исключении, если при разборе возникнет какое-либо исключение. Если yaml пуст, он возвращает указанное значение возврата fallback, которое по умолчанию равно false.
Вызывает исключение Psych::SyntaxError, когда обнаруживается синтаксическая ошибка YAML.
Пример:
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
Если необязательный именованный параметр symbolize_names имеет значение true, возвращает символы для ключей в объектах Hash (по умолчанию: строки).
Psych.load("---\n foo: bar") # => {"foo"=>"bar"}
Psych.load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
Вызывает исключение TypeError, когда параметр `yaml` равен NilClass
ПРИМЕЧАНИЕ: этот метод *не должен* использоваться для разбора недоверенных документов, таких как документы YAML, предоставляемые через пользовательский ввод. Вместо этого используйте метод safe_load.
# File ext/psych/lib/psych.rb, line 576
def self.load_file filename, fallback: false
File.open(filename, 'r:bom|utf-8') { |f|
self.load f, filename: filename, fallback: fallback
}
end Загружает документ, содержащийся в filename. Возвращает YAML, содержащийся в filename, как объект Ruby, или, если файл пуст, возвращает указанное значение возврата fallback, которое по умолчанию равно false.
# File ext/psych/lib/psych.rb, line 554
def self.load_stream yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: []
if legacy_filename != NOT_GIVEN
warn_with_uplevel 'Passing filename with the 2nd argument of Psych.load_stream is deprecated. Use keyword argument like Psych.load_stream(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
filename = legacy_filename
end
result = if block_given?
parse_stream(yaml, filename: filename) do |node|
yield node.to_ruby
end
else
parse_stream(yaml, filename: filename).children.map(&:to_ruby)
end
return fallback if result.is_a?(Array) && result.empty?
result
end Загружает несколько документов, указанных в yaml. Возвращает разобранные документы в виде списка. Если задан блок, каждый документ будет преобразован в Ruby и передан в блок во время разбора
Пример:
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']
# File ext/psych/lib/psych.rb, line 384
def self.parse yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: NOT_GIVEN
if legacy_filename != NOT_GIVEN
warn_with_uplevel 'Passing filename with the 2nd argument of Psych.parse is deprecated. Use keyword argument like Psych.parse(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
filename = legacy_filename
end
parse_stream(yaml, filename: filename) do |node|
return node
end
if fallback != NOT_GIVEN
warn_with_uplevel 'Passing the `fallback` keyword argument of Psych.parse is deprecated.', uplevel: 1 if $VERBOSE
fallback
else
false
end
end Разбирает строку YAML в yaml. Возвращает Psych::Nodes::Document. filename используется в сообщении об исключении, если возникает Psych::SyntaxError.
Вызывает исключение Psych::SyntaxError, когда обнаруживается синтаксическая ошибка YAML.
Пример:
Psych.parse("---\n - a\n - b") # => #<Psych::Nodes::Document:0x00>
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
См. Psych::Nodes для получения дополнительной информации о YAML AST.
# File ext/psych/lib/psych.rb, line 406
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 Разбирает файл по адресу filename. Возвращает Psych::Nodes::Document.
Вызывает исключение Psych::SyntaxError, когда обнаруживается синтаксическая ошибка YAML.
# File ext/psych/lib/psych.rb, line 448
def self.parse_stream yaml, legacy_filename = NOT_GIVEN, filename: nil, &block
if legacy_filename != NOT_GIVEN
warn_with_uplevel 'Passing filename with the 2nd argument of Psych.parse_stream is deprecated. Use keyword argument like Psych.parse_stream(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
filename = legacy_filename
end
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 Разбирает строку YAML в yaml. Возвращает Psych::Nodes::Stream. Этот метод может обрабатывать несколько документов YAML, содержащихся в yaml. filename используется в сообщении об исключении, если возникает Psych::SyntaxError.
Если задан блок, узел Psych::Nodes::Document будет передан в блок по мере его анализа.
Вызывает исключение Psych::SyntaxError, когда обнаруживается синтаксическая ошибка YAML.
Пример:
Psych.parse_stream("---\n - a\n - b") # => #<Psych::Nodes::Stream:0x00>
Psych.parse_stream("--- a\n--- b") do |node|
node # => #<Psych::Nodes::Document:0x00>
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
Вызывает исключение TypeError, когда передается NilClass.
См. Psych::Nodes для получения дополнительной информации о YAML AST.
# File ext/psych/lib/psych.rb, line 415 def self.parser Psych::Parser.new(TreeBuilder.new) end
Возвращает парсер по умолчанию
# File ext/psych/lib/psych.rb, line 328
def self.safe_load yaml, legacy_permitted_classes = NOT_GIVEN, legacy_permitted_symbols = NOT_GIVEN, legacy_aliases = NOT_GIVEN, legacy_filename = NOT_GIVEN, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false
if legacy_permitted_classes != NOT_GIVEN
warn_with_uplevel 'Passing permitted_classes with the 2nd argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, permitted_classes: ...) instead.', uplevel: 1 if $VERBOSE
permitted_classes = legacy_permitted_classes
end
if legacy_permitted_symbols != NOT_GIVEN
warn_with_uplevel 'Passing permitted_symbols with the 3rd argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, permitted_symbols: ...) instead.', uplevel: 1 if $VERBOSE
permitted_symbols = legacy_permitted_symbols
end
if legacy_aliases != NOT_GIVEN
warn_with_uplevel 'Passing aliases with the 4th argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, aliases: ...) instead.', uplevel: 1 if $VERBOSE
aliases = legacy_aliases
end
if legacy_filename != NOT_GIVEN
warn_with_uplevel 'Passing filename with the 5th argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
filename = legacy_filename
end
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
visitor = if aliases
Visitors::ToRuby.new scanner, class_loader
else
Visitors::NoAliasRuby.new scanner, class_loader
end
result = visitor.accept result
symbolize_names!(result) if symbolize_names
result
end Безопасно загружает строку yaml в yaml. По умолчанию разрешена десериализация только следующих классов:
Рекурсивные структуры данных по умолчанию не допускаются. Произвольные классы могут быть разрешены путем добавления этих классов к именованному параметру permitted_classes. Они являются аддитивными. Например, для разрешения десериализации Date:
Psych.safe_load(yaml, permitted_classes: [Date])
Теперь класс Date может быть загружен в дополнение к классам, перечисленным выше.
Псевдонимы могут быть явно разрешены, изменив именованный параметр aliases. Например:
x = [] x << x yaml = Psych.dump x Psych.safe_load yaml # => raises an exception Psych.safe_load yaml, aliases: true # => loads the aliases
Исключение Psych::DisallowedClass будет выброшено, если yaml содержит класс, которого нет в списке permitted_classes.
Исключение Psych::BadAlias будет выброшено, если yaml содержит псевдонимы, но именованный параметр aliases имеет значение false.
filename будет использоваться в сообщении об исключении, если при разборе возникнет какое-либо исключение.
Если необязательный именованный параметр symbolize_names имеет значение true, возвращает символы для ключей в объектах Hash (по умолчанию: строки).
Psych.safe_load("---\n foo: bar") # => {"foo"=>"bar"}
Psych.safe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
# File ext/psych/lib/psych.rb, line 533 def self.to_json object visitor = Psych::Visitors::JSONTree.create visitor << object visitor.tree.yaml end
Преобразует объект Ruby object в строку JSON.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.