модуль 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. Для получения информации об использовании парсера высокого уровня см. ::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. См. ::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 можно свободно исследовать и изменять. Для получения дополнительной информации об обращении с деревьями синтаксического анализа YAML см. ::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>
Получение потока событий
parser = Psych::Parser.new(Psych::Handlers::Recorder.new)
parser.parse("---\n - a\n - b")
parser.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/deprecated.rb, line 49
def self.add_private_type type_tag, &block
warn "#{caller[0]}: add_private_type is deprecated, use add_domain_type" if $VERBOSE
domain = 'x-private'
key = [domain, type_tag].join ':'
@domain_types[key] = [key, block]
end # File ext/psych/lib/psych/deprecated.rb, line 42
def self.add_ruby_type type_tag, &block
warn "#{caller[0]}: add_ruby_type is deprecated, use add_domain_type" if $VERBOSE
domain = 'ruby.yaml.org,2002'
key = ['tag', domain, type_tag].join ':'
@domain_types[key] = [key, block]
end # File ext/psych/lib/psych/deprecated.rb, line 34
def self.detect_implicit thing
warn "#{caller[0]}: detect_implicit is deprecated" if $VERBOSE
return '' unless String === thing
return 'null' if '' == thing
ss = ScalarScanner.new(ClassLoader.new)
ss.tokenize(thing).class.name.downcase
end # File ext/psych/lib/psych.rb, line 402
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.
Пример:
# 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 419
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 244 def self.load yaml, filename = nil result = parse(yaml, filename) result ? result.to_ruby : result end
Загружает yaml в структуру данных Ruby. Если предоставлено несколько документов, будет возвращен объект, содержащийся в первом документе. filename будет использовано в сообщении об ошибке, если при разборе возникнет какая-либо ошибка.
Вызывает исключение Psych::SyntaxError, если обнаружена синтаксическая ошибка YAML.
Пример:
Psych.load("--- a") # => 'a'
Psych.load("---\n - a\n - b") # => ['a', 'b']
begin
Psych.load("--- `", "file.txt")
rescue Psych::SyntaxError => ex
ex.file # => 'file.txt'
ex.message # => "(file.txt): found character that cannot start any token"
end
# File ext/psych/lib/psych/deprecated.rb, line 25
def self.load_documents yaml, &block
if $VERBOSE
warn "#{caller[0]}: load_documents is deprecated, use load_stream"
end
list = load_stream yaml
return list unless block_given?
list.each(&block)
end Этот метод устарел, используйте ::load_stream вместо него.
# File ext/psych/lib/psych.rb, line 463
def self.load_file filename
File.open(filename, 'r:bom|utf-8') { |f| self.load f, filename }
end Загружает документ, содержащийся в filename. Возвращает содержащийся в filename YAML как объект Ruby
# File ext/psych/lib/psych.rb, line 450
def self.load_stream yaml, filename = nil
if block_given?
parse_stream(yaml, filename) do |node|
yield node.to_ruby
end
else
parse_stream(yaml, filename).children.map { |child| child.to_ruby }
end
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/deprecated.rb, line 72
def self.object_maker klass, hash
warn "#{caller[0]}: object_maker is deprecated" if $VERBOSE
klass.allocate.tap do |obj|
hash.each { |k,v| obj.instance_variable_set(:"@#{k}", v) }
end
end # File ext/psych/lib/psych.rb, line 317
def self.parse yaml, filename = nil
parse_stream(yaml, filename) do |node|
return node
end
false
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("--- `", "file.txt")
rescue Psych::SyntaxError => ex
ex.file # => 'file.txt'
ex.message # => "(file.txt): found character that cannot start any token"
end
См. Psych::Nodes для получения дополнительной информации об AST YAML.
# File ext/psych/lib/psych.rb, line 328
def self.parse_file filename
File.open filename, 'r:bom|utf-8' do |f|
parse f, filename
end
end Разбирает файл по адресу filename. Возвращает Psych::Nodes::Document.
Вызывает исключение Psych::SyntaxError, если обнаружена синтаксическая ошибка YAML.
# File ext/psych/lib/psych.rb, line 367
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 Разбирает строку 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("--- `", "file.txt")
rescue Psych::SyntaxError => ex
ex.file # => 'file.txt'
ex.message # => "(file.txt): found character that cannot start any token"
end
См. Psych::Nodes для получения дополнительной информации об AST YAML.
# File ext/psych/lib/psych.rb, line 336 def self.parser Psych::Parser.new(TreeBuilder.new) end
Возвращает стандартный парсер
# File ext/psych/lib/psych/deprecated.rb, line 62
def self.read_type_class type, reference
warn "#{caller[0]}: read_type_class is deprecated" if $VERBOSE
_, _, type, name = type.split ':', 4
reference = name.split('::').inject(reference) do |k,n|
k.const_get(n.to_sym)
end if name
[type, reference]
end # File ext/psych/lib/psych.rb, line 283
def self.safe_load yaml, whitelist_classes = [], whitelist_symbols = [], aliases = false, filename = nil
result = parse(yaml, filename)
return unless result
class_loader = ClassLoader::Restricted.new(whitelist_classes.map(&:to_s),
whitelist_symbols.map(&:to_s))
scanner = ScalarScanner.new class_loader
if aliases
visitor = Visitors::ToRuby.new scanner, class_loader
else
visitor = Visitors::NoAliasRuby.new scanner, class_loader
end
visitor.accept result
end Безопасно загружает строку yaml в yaml. По умолчанию разрешены только следующие классы для десериализации:
-
String
-
Array
Рекурсивные структуры данных по умолчанию запрещены. Произвольные классы могут быть разрешены путем добавления этих классов в whitelist. Они являются аддитивными. Например, для разрешения десериализации Date:
Psych.safe_load(yaml, [Date])
Теперь класс Date может загружаться в дополнение к перечисленным выше классам.
Псевдонимы могут быть явно разрешены, изменив параметр aliases. Например:
x = [] x << x yaml = Psych.dump x Psych.safe_load yaml # => raises an exception Psych.safe_load yaml, [], [], true # => loads the aliases
Исключение Psych::DisallowedClass будет выброшено, если yaml содержит класс, который не находится в белом списке.
Исключение Psych::BadAlias будет выброшено, если yaml содержит псевдонимы, но параметр aliases установлен в false.
# File ext/psych/lib/psych/deprecated.rb, line 56
def self.tagurize thing
warn "#{caller[0]}: add_private_type is deprecated, use add_domain_type" if $VERBOSE
return thing unless String === thing
"tag:yaml.org,2002:#{thing}"
end # File ext/psych/lib/psych.rb, line 429 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.