Psych
Psych
Psych는 YAML 파서이자 이미터(emitter)예요. YAML 파싱·발행 기능을 위해 libyaml을 활용해요. libyaml을 감싸는 것에 더해, 대부분의 Ruby 객체를 YAML 형식으로/에서 직렬화·역직렬화하는 방법도 알고 있어요.
출처: Ruby 4.0 API
본문
바로 지금 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 문서를 파싱하는 다양한 인터페이스를 제공해요. 가장 낮은 수준은 이벤트 기반 파서예요. 중간 수준은 원시 YAML AST에 접근하는 것, 그리고 가장 높은 수준은 YAML을 Ruby 객체로 언마샬(unmarshal)하는 기능이에요.
YAML 발행
Psych는 YAML 문서를 만드는 데 저수준에서 고수준까지 다양한 인터페이스를 제공해요. 파싱 인터페이스와 아주 비슷하게, 가장 낮은 수준은 이벤트 기반 시스템, 중간 수준은 YAML AST 빌드, 가장 높은 수준은 Ruby 객체를 바로 YAML 문서로 변환하는 것이에요.
고수준 API
파싱
Psych가 제공하는 고수준 YAML 파서는 YAML을 입력으로 받아 Ruby 데이터 구조를 돌려줘요. 사용법은 Psych.load를 참고하세요.
문자열에서 읽기:
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
파일에서 읽기:
Psych.safe_load_file("data.yml", permitted_classes: [Date])
Psych.load_file("trusted_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를 참고하세요.
문자열로 쓰기:
# 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.open('database.yml', 'w') do |file|
file.write(Psych.dump(['a', 'b']))
end
중간 수준 API
파싱
Psych는 YAML 문서를 파싱해서 만들어진 AST에 접근을 제공해요. 이 트리는 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')
발행
중간 수준은 AST를 빌드하는 거예요. 이 AST는 YAML 문서를 파싱할 때 쓰이는 AST와 정확히 같아요. 사용자는 AST를 손으로 빌드할 수 있고, AST는 YAML 문서로 자기 자신을 발행하는 방법을 알고 있어요. YAML AST 빌드에 대한 자세한 내용은 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를 참고하세요.
Psych::Nodes::Stream 구조로 읽기:
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"
Constants
DEFAULT_SNAKEYAML_VERSION
LIBYAML_VERSION
Psych가 사용하는 libyaml의 버전
VERSION
사용 중인 Psych의 버전
Public Class Methods
dump(o) → string of yaml / dump(o, options) → string of yaml / dump(o, io) → io object passed in / dump(o, io, options) → io object passed in
Ruby 객체 o를 YAML 문자열로 덤프해요. 선택적 options로 출력 형식을 제어할 수 있어요. IO 객체가 전달되면 YAML이 그 IO 객체로 덤프돼요.
현재 지원되는 옵션은 다음과 같아요.
:indentation
들여쓰기에 사용되는 공백 문자 수. 허용 값은 0..9 범위이고, 그 외에는 옵션이 무시돼요. 기본: 2.
:line_width
줄을 감싸는 최대 문자 수. 무제한 줄 너비에는 -1을 사용해요. 기본: 0("81에서 줄바꿈"을 의미).
:canonical
"canonical" YAML 형식을 작성해요(매우 장황하지만 엄격하게 형식을 지킴). 기본: false.
:header
문서의 시작에 %YAML [version]을 작성해요. 기본: false.
:stringify_names
해시 객체의 심볼 키를 문자열로 덤프해요. 기본: 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)
# Dump hash with symbol keys as string
Psych.dump({a: "b"}, stringify_names: true) # => "---\na: b\n"
dump_stream(*objects)
객체 목록을 별개의 문서들로 문서 스트림에 덤프해요.
Psych.dump_stream("foo\n ", {}) # => "--- ! \"foo\\n \"\n--- {}\n"
libyaml_version
사용 중인 libyaml의 버전을 돌려줘요.
load(yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true)
yaml을 Ruby 데이터 구조로 로드해요. 여러 문서가 제공되면 첫 번째 문서에 담긴 객체를 돌려줘요. filename은 파싱 중 예외가 발생하면 그 예외 메시지에 사용돼요. yaml이 비어 있으면 지정된 fallback 반환 값(기본 nil)을 돌려줘요.
YAML 구문 오류가 감지되면 Psych::SyntaxError를 발생시켜요.
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로 설정되면 해시 객체의 키에 심볼을 돌려줘요(기본: 문자열).
Psych.load("---\n foo: bar") # => {"foo"=>"bar"}
Psych.load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
yaml 파라미터가 NilClass이면 TypeError를 발생시켜요. 이 메서드는 기본적으로 Symbol 객체가 허용된다는 점을 제외하면 safe_load와 비슷해요.
load_file(filename, **kwargs)
filename에 담긴 문서를 로드해요. filename에 담긴 yaml을 Ruby 객체로 돌려주거나, 파일이 비어 있으면 지정된 fallback 값(기본 nil)을 돌려줘요. 옵션은 load를 참고하세요.
load_stream(yaml, filename: nil, fallback: [], **kwargs) { |to_ruby(**kwargs)| ... }
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']
parse(yaml, filename: nil)
yaml의 YAML 문자열을 파싱해요. Psych::Nodes::Document를 돌려줘요. filename은 Psych::SyntaxError가 발생할 때 그 예외 메시지에 사용돼요.
YAML 구문 오류가 감지되면 Psych::SyntaxError를 발생시켜요.
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
YAML AST에 대한 자세한 내용은 Psych::Nodes를 참고하세요.
parse_file(filename, fallback: false)
filename의 파일을 파싱해요. Psych::Nodes::Document를 돌려줘요. YAML 구문 오류가 감지되면 Psych::SyntaxError를 발생시켜요.
parse_stream(yaml, filename: nil, &block)
yaml의 YAML 문자열을 파싱해요. Psych::Nodes::Stream을 돌려줘요. 이 메서드는 yaml에 담긴 여러 YAML 문서를 처리할 수 있어요. filename은 Psych::SyntaxError가 발생할 때 그 예외 메시지에 사용돼요.
블록이 주어지면 파싱되는 동안 Psych::Nodes::Document 노드가 블록으로 yield 돼요.
YAML 구문 오류가 감지되면 Psych::SyntaxError를 발생시켜요.
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
NilClass가 전달되면 TypeError를 발생시켜요. YAML AST에 대한 자세한 내용은 Psych::Nodes를 참고하세요.
parser()
기본 파서를 돌려줘요.
safe_dump(o) → string of yaml / safe_dump(o, options) → string of yaml / safe_dump(o, io) → io object passed in / safe_dump(o, io, options) → io object passed in
Ruby 객체 o를 YAML 문자열로 안전하게 덤프해요. 선택적 options로 출력 형식을 제어할 수 있어요. IO 객체가 전달되면 YAML이 그 IO 객체로 덤프돼요. 기본적으로 permitted_classes 키워드 인자에 추가해주면 임의의 클래스가 허용돼요. 추가(additive) 방식이에요. 예를 들어 Date 직렬화를 허용하려면:
Psych.safe_dump(yaml, permitted_classes: [Date])
이제 위에 나열된 클래스들에 더해 Date 클래스도 덤프할 수 있어요. 객체가 permitted_classes 목록에 없는 클래스를 포함하면 Psych::DisallowedClass 예외가 발생해요. 지원 옵션들은 dump의 옵션과 동일해요(:indentation, :line_width, :canonical, :header, :stringify_names). 기본 직렬화 허용 클래스는 TrueClass, FalseClass, NilClass, Integer, Float, String, Array, Hash예요.
safe_load(yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true)
yaml 문자열을 안전하게 로드해요. 기본적으로 역직렬화가 허용되는 클래스는 TrueClass, FalseClass, NilClass, Integer, Float, String, Array, Hash뿐이에요. 재귀적 데이터 구조는 기본적으로 허용되지 않아요. permitted_classes 키워드 인자에 클래스를 추가하면 임의의 클래스를 허용할 수 있어요. 추가 방식이에요.
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
yaml이 permitted_classes 목록에 없는 클래스를 포함하면 Psych::DisallowedClass 예외가 발생해요. yaml이 앨리어스를 포함하는데 aliases 키워드 인자가 false로 설정돼 있으면 Psych::AliasesNotEnabled 예외가 발생해요. filename은 파싱 중 예외가 발생하면 그 예외 메시지에 사용돼요.
선택적 symbolize_names 키워드 인자가 true로 설정되면 해시 객체의 키에 심볼을 돌려줘요(기본: 문자열).
Psych.safe_load("---\n foo: bar") # => {"foo"=>"bar"}
Psych.safe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
safe_load_file(filename, **kwargs)
filename에 담긴 문서를 안전하게 로드해요. filename에 담긴 yaml을 Ruby 객체로 돌려주거나, 파일이 비어 있으면 지정된 fallback 값(기본 nil)을 돌려줘요. 옵션은 safe_load를 참고하세요.
safe_load_stream(yaml, filename: nil, permitted_classes: [], aliases: false) { |doc| ... }
yaml에 주어진 여러 문서를 로드해요. 파싱된 문서들을 목록으로 돌려줘요.
Psych.safe_load_stream("--- foo\n...\n--- bar\n...") # => ['foo', 'bar']
list = []
Psych.safe_load_stream("--- foo\n...\n--- bar\n...") do |ruby|
list << ruby
end
list # => ['foo', 'bar']
to_json(object)
Ruby 객체를 JSON 문자열로 덤프해요.
unsafe_load(yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true)
yaml을 Ruby 데이터 구조로 로드해요. 여러 문서가 제공되면 첫 번째 문서의 객체를 돌려줘요. filename은 파싱 중 예외가 발생하면 그 예외 메시지에 사용돼요. yaml이 비어 있으면 지정된 fallback 값(기본 false)을 돌려줘요. YAML 구문 오류가 감지되면 Psych::SyntaxError를 발생시켜요.
Psych.unsafe_load("--- a") # => 'a'
Psych.unsafe_load("---\n - a\n - b") # => ['a', 'b']
선택적 symbolize_names 키워드 인자가 true로 설정되면 해시 키에 심볼을 돌려줘요(기본: 문자열).
Psych.unsafe_load("---\n foo: bar") # => {"foo"=>"bar"}
Psych.unsafe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
yaml 파라미터가 NilClass이면 TypeError를 발생시켜요.
참고: 이 메서드는 신뢰할 수 없는 문서, 예를 들어 사용자 입력으로 제공되는 YAML 문서를 파싱하는 데 쓰면 안 돼요. 대신
load메서드나safe_load메서드를 사용하세요.
unsafe_load_file(filename, **kwargs)
filename에 담긴 문서를 로드해요. filename에 담긴 yaml을 Ruby 객체로 돌려주거나, 파일이 비어 있으면 지정된 fallback 값(기본 false)을 돌려줘요.
참고: 이 메서드는 신뢰할 수 없는 문서를 파싱하는 데 쓰면 안 돼요. 대신
safe_load_file메서드를 사용하세요.