Psych 모듈

Psych 모듈

YAML 파서이자 emitter인 Psych는 libyaml(홈: pyyaml.org/wiki/LibYAML, git: github.com/yaml/libyaml)을 활용해서 YAML을 파싱·발행해요. libyaml을 감싸는 것 외에도, Psych는 대부분의 Ruby 객체를 YAML 형식으로 직렬화·역직렬화하는 법도 알고 있어요.

출처: Ruby 3.3 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 파싱 (YAML Parsing)

Psych는 파싱 요구에 따라 낮은 수준부터 높은 수준까지 다양한 YAML 문서 파싱 인터페이스를 제공해요. 가장 낮은 수준은 이벤트 기반 파서이고, 중간 수준은 raw YAML AST에 접근하는 것이며, 가장 높은 수준은 YAML을 Ruby 객체로 언마샬(unmarshal)하는 기능이에요.

YAML 발행 (YAML Emitting)

Psych는 YAML 문서를 만들기 위한 낮은 수준부터 높은 수준까지의 인터페이스를 제공해요. YAML 파싱 인터페이스와 매우 비슷하게, 가장 낮은 수준은 이벤트 기반 시스템, 중간 수준은 YAML AST 구축, 가장 높은 수준은 Ruby 객체를 곧바로 YAML 문서로 변환하는 것이에요.

고수준 API (High-level API)

파싱 (Parsing)

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

발행 (Emitting)

고수준 emitter는 가장 쉬운 인터페이스를 가져요. 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)

파일에 쓰기

현재 Ruby 구조를 파일로 덤프하는 직접 API는 없어요.

File.open('database.yml', 'w') do |file|
  file.write(Psych.dump(['a', 'b']))
end

중간 수준 API (Mid-level API)

파싱 (Parsing)

Psych는 YAML 문서를 파싱해 만든 AST에 접근할 수 있게 해 줘요. 이 트리는 Psych::ParserPsych::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

발행 (Emitting)

중간 수준은 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 (Low-level API)

파싱 (Parsing)

가장 낮은 수준의 파서는 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

발행 (Emitting)

가장 낮은 수준의 emitter는 이벤트 기반 시스템이에요. 이벤트는 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

Public Class Methods

dump(o) → string of yaml

Ruby 객체 o를 YAML 문자열로 덤프해요. 선택 options를 넘겨 출력 형식을 제어할 수 있어요. IO 객체가 넘어오면 YAML이 그 IO 객체로 덤프돼요.

현재 지원되는 옵션:

:indentation

예시:

# 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_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)

yaml을 Ruby 데이터 구조로 불러와요. 여러 문서가 제공되면 첫 문서에 담긴 객체가 반환돼요. 파싱 중 예외가 발생하면 filename이 예외 메시지에 사용돼요. yaml이 비어 있으면 지정된 fallback 반환값을 반환하는데, 기본값은 false예요.

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 값으로 설정되면 Hash 객체의 키에 대해 symbol을 반환해요(기본: 문자열).

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 반환값을 반환해요(기본 false). 옵션은 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를 반환해요. Psych::SyntaxError가 발생하면 예외 메시지에 filename이 사용돼요.

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 문서를 처리할 수 있어요. Psych::SyntaxError가 발생하면 예외 메시지에 filename이 사용돼요.

블록이 주어지면 파싱되는 중 Psych::Nodes::Document 노드가 블록에 넘겨져요.

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

Ruby 객체 o를 안전하게 YAML 문자열로 덤프해요. 선택 options를 넘겨 출력 형식을 제어할 수 있어요. IO 객체가 넘어오면 YAML이 그 IO로 덤프돼요. 기본적으로 다음 클래스만 직렬화가 허용돼요:

TrueClass, FalseClass, NilClass, Integer, Float, String, Array, Hash

permitted_classes 키워드 인자에 클래스를 추가하면 임의의 클래스를 허용할 수 있어요. 추가 방식이에요. 예를 들어 Date 직렬화를 허용하려면:

Psych.safe_dump(yaml, permitted_classes: [Date])

이제 위에 나열된 클래스 외에 Date 클래스도 덤프할 수 있어요.

객체에 permitted_classes 목록에 없는 클래스가 들어 있으면 Psych::DisallowedClass 예외가 던져져요.

현재 지원되는 옵션:

:indentation

예시:

# 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)  # => #<StringIO:0x000001009d0890>

# 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)

safe_load (yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false)

yaml의 yaml 문자열을 안전하게 불러와요. 기본적으로 다음 클래스만 역직렬화가 허용돼요:

TrueClass, FalseClass, NilClass, Integer, Float, String, Array, Hash

재귀적 데이터 구조는 기본적으로 허용되지 않아요. 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

yaml에 permitted_classes 목록에 없는 클래스가 들어 있으면 Psych::DisallowedClass 예외가 던져져요.

yaml에 별칭이 들어 있고 aliases 키워드 인자가 false로 설정돼 있으면 Psych::AliasesNotEnabled 예외가 던져져요.

파싱 중 예외가 발생하면 예외 메시지에 filename이 사용돼요.

선택 symbolize_names 키워드 인자가 true 값으로 설정되면 Hash 객체의 키에 대해 symbol을 반환해요(기본: 문자열).

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 반환값을 반환해요(기본 false). 옵션은 safe_load 참고.

to_json (object)

Ruby object를 JSON 문자열로 덤프해요.

unsafe_load (yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false)

yaml을 Ruby 데이터 구조로 불러와요. 여러 문서가 제공되면 첫 문서에 담긴 객체가 반환돼요. 파싱 중 예외가 발생하면 예외 메시지에 filename이 사용돼요. yaml이 비어 있으면 지정된 fallback 반환값을 반환해요(기본 false).

YAML 문법 에러가 감지되면 Psych::SyntaxError를 던져요.

예시:

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

선택 symbolize_names 키워드 인자가 true 값으로 설정되면 Hash 객체의 키에 대해 symbol을 반환해요(기본: 문자열).

Psych.unsafe_load("---\n foo: bar")                         # => {"foo"=>"bar"}
Psych.unsafe_load("---\n foo: bar", symbolize_names: true)  # => {:foo=>"bar"}

yaml 파라미터가 NilClass이면 TypeError를 던져요.

주의: 이 메서드는 사용자 입력으로 제공되는 YAML 문서 같은 신뢰할 수 없는 문서를 파싱하는 데 쓰면 안 돼요. 대신 loadsafe_load 메서드를 쓰세요.

unsafe_load_file (filename, **kwargs)

filename에 담긴 문서를 불러와요. filename에 담긴 yaml을 Ruby 객체로 반환하고, 파일이 비어 있으면 지정된 fallback 반환값을 반환해요(기본 false).

주의: 이 메서드는 사용자 입력으로 제공되는 YAML 문서 같은 신뢰할 수 없는 문서를 파싱하는 데 쓰면 안 돼요. 대신 safe_load_file 메서드를 쓰세요.