модуль 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. См. Psych.dump для получения дополнительной информации о выводе структуры данных Ruby.
Запись в строку
# 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 можно свободно исследовать и изменять. См. Psych::parse_stream, Psych::Nodes и Psych::Nodes::Node для получения дополнительной информации о работе со синтаксическими деревьями YAML.
Чтение из строки
# 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. См. Psych::Nodes, Psych::Nodes::Node и Psych::TreeBuilder для получения дополнительной информации о построении AST YAML.
Запись в строку
# 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
# 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.
# 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.
# 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.