Ruby 3.1 で非互換になる YAML.load

93.2K Views

January 06, 22

スライド概要

https://omotesandorb.connpass.com/event/235092/

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

Ruby 3.1 で非互換になる YAML.load Omotesando.rb #70

2.

自己紹介 名前:osyo Twitter : @pink_bangbi github : osyo-manga ブログ : Secret Garden(Instrumental) Rails エンジニア 好きな Ruby の機能は Refinements Ruby 3.1 で気になる機能は Hash のショートハンドと debug.gem RubyKaigi Takeout 2021 Use Macro all the time ~ マクロを使いまくろ ~ Advent Calendar やってるよ! Ruby の TracePoint を使ってメソッド呼び出しをトレースしよう 一人 bugs.ruby Advent Calendar

3.

Ruby 3.1 で非互換になる YAML.load

4.

概要

5.

概要 YAML は psych というライブラリで実装されている `YAML # => Psych` `YAML.load` は実質 `Psych.load` にあたる

6.

概要 YAML は psych というライブラリで実装されている `YAML # => Psych` `YAML.load` は実質 `Psych.load` にあたる この psych が 4.0.0 で非互換になった `Psych.load` 周りが非互換になった

7.

概要 YAML は psych というライブラリで実装されている `YAML # => Psych` `YAML.load` は実質 `Psych.load` にあたる この psych が 4.0.0 で非互換になった `Psych.load` 周りが非互換になった Ruby 3.1 ではこの psych 4.0.0 がバンドルされている

8.

psych 4.0.0 の非互換な変更

9.

その前に

10.

Psych.safe_load について

11.

Psych.safe_load について `Psych.load` ソッドがある とは別に安全に YAML ファイルを読み込む `Psych.safe_load` というメ

12.

Psych.safe_load について `Psych.load` ソッドがある とは別に安全に YAML ファイルを読み込む `Psych.safe_load` というメ と比較して再帰的な特定のクラスのオブジェクトしか変換しなかったり、 データ構造はデフォルトでは許可されていなかったりする `Psych.load`

13.

Psych.safe_load について `Psych.load` ソッドがある とは別に安全に YAML ファイルを読み込む `Psych.safe_load` というメ と比較して再帰的な特定のクラスのオブジェクトしか変換しなかったり、 データ構造はデフォルトでは許可されていなかったりする `Psych.load_file` と対になる `Psych.safe_load_file` メソッドも存在している `Psych.load`

14.

デフォルトでは以下のクラスのオブジェクトしか変換されない `TrueClass` `FalseClass` `NilClass` `Numeric` `String` `Array` `Hash`

15.
[beta]
デフォルトでは以下のクラスのオブジェクトしか変換されない
`TrueClass` `FalseClass` `NilClass` `Numeric` `String` `Array` `Hash`

`Psych.safe_load`

だと `Date` オブジェクトは変換されない

1
2
3
4

require "psych"
require "date"

5
6
7
8

pp Psych.safe_load("name: homu")
# => {"name"=>"homu"}

9
10

# OK: String

# NG: Date

は問題ない

オブジェクトは変換できずにエラーになる

# error: `find': Tried to load unspecified class: Date (Psych::DisallowedClass)
pp Psych.safe_load("date: 2020-01-01")

16.

デフォルトでは再帰的なデータ構造はデフォルトで許可されてない

17.

デフォルトでは再帰的なデータ構造はデフォルトで許可されてない `&default` のようなエイリアスがあるとエラーになる 1 2 3 require "psych" 4 5 6 default: &default aaa: aaa bbb: bbb data = <<~EOS 7 8 9 10 11 development: <<: *default test: <<: *default production: 12 13 14 15 16 17 <<: *default EOS 読み込めない # NG: # error: Unknown alias: default (Psych::BadAlias) pp Psych.safe_load(data)

18.

これらは引数で制御する事ができる `Date` などを許可する場合は

19.
[beta]
これらは引数で制御する事ができる
`Date` などを許可する場合は
`permitted_classes:` に許可するクラスのオブジェクトの配列を渡す
1

require "psych"

2
3
4
5
6

require "date"
# OK
pp Psych.safe_load("date: 2020-01-01", permitted_classes: [Date])
# => {"date"=>#<Date: 2020-01-01 ((2458850j,0s,0n),+0s,2299161j)>}

20.

再帰的なデータ構造を許可する場合は

21.
[beta]
再帰的なデータ構造を許可する場合は
`aliases: true` を渡す
1
2
3

require "psych"

4
5
6

default: &default
aaa: aaa
development:

data = <<~EOS

7
8
9
10
11

<<: *default
EOS

12

# => {"default"=>{"aaa"=>"aaa"}, "development"=>{"aaa"=>"aaa"}}

# OK
pp Psych.load(data, aliases: true)

22.

psych 4.0.0 だと `Psych.load` -> `Psych.safe_load` `Psych.load_file` -> `Psych.safe_load_file` に置き換わった

23.

Ruby 3.1 では psych 4.0.0 がバンドルされる

24.

なので今まで読み込めていた YAML ファイルが読み込めなくなる 可能性がある

25.

もう1つの非互換な変更

26.

引数のシグネチャが変わった psych 4.0.0 以前はオプション引数とキーワード引数の2つ受け取るようになっていた 1 2 3 def self.safe_load( yaml, legacy_permitted_classes = NOT_GIVEN, 4 5 6 7 legacy_permitted_symbols = NOT_GIVEN, legacy_aliases = NOT_GIVEN, legacy_filename = NOT_GIVEN, permitted_classes: [], 8 9 10 11 permitted_symbols: [], aliases: false, filename: nil, fallback: nil, 12 13 14 15 symbolize_names: false, freeze: false) # ... end

27.
[beta]
オプション引数とキーワード引数を両方渡せる
1
2
3

require "psych"
require "date"

4
5
6
7
8

pp Psych::VERSION

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# => "3.2.0"

data = <<~EOS
default: &default
aaa: 2020-01-1
development:
<<: *default
EOS

オプション引数を受け取る

#
pp Psych.safe_load(data, [Date], [], true)

キーワード引数でも受け取れる

#
pp Psych.safe_load(data, permitted_classes: [Date], aliases: true)

両方渡した場合はオプション引数が優先される

#
# no error
pp Psych.safe_load("2020-01-1", [Date], permitted_classes: [])
# error: Tried to load unspecified class: Date (Psych::DisallowedClass)
pp Psych.safe_load("2020-01-1", [], permitted_classes: [Date])

28.

psych 4.0.0 以降はキーワード引数のみを受け取る 1 2 3 4 5 6 7 8 9 10 def self.load( yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false) # ... end

29.
[beta]
キーワード引数だけ渡せる
1
2
3

require "psych"
require "date"

4
5
6
7
8

pp Psych::VERSION

9
10
11
12
13
14
15
16
17
18

# => "3.2.0"

data = <<~EOS
default: &default
aaa: 2020-01-1
development:
<<: *default
EOS

キーワード引数でも受け取れる

#
pp Psych.safe_load(data, permitted_classes: [Date], aliases: true)

エラー

#
pp Psych.safe_load(data, [Date], [], true)

31.

psych 4.0.0 への対応策

32.
[beta]
引数の値で制御する

必要に応じてキーワード引数で制御を行う

1
2
3

require "psych"
require "date"

4
5
6
7

data = <<~EOS
default: &default
aaa: 2020-01-1
development:

8
9
10
11

<<: *default
EOS

12
13
14

pp Psych.load(data, permitted_classes: [Date], aliases: true)
# => {"default"=>{"aaa"=>#<Date: 2020-01-01 ((2458850j,0s,0n),+0s,2299161j)>},
#
"development"=>{"aaa"=>#<Date: 2020-01-01 ((2458850j,0s,0n),+0s,2299161j)>}}

# OK

33.

バージョンを固定する Gemfile にバージョンを指定する gem 内で `Psych.load` が使われている場合などはこの対処になりそう 1 2 gem "psych", "< 4.0.0" 3 4 # 4.0.0 # gem "psych", ">= 4.0.0" 逆に 以上を使用したい場合はこんな感じ

34.
[beta]
Psych.unsafe_load を使う

の変わりに `Psych.unsafe_load` を使う
`Psych.unsafe_load` は以前の `Psych.load` と同じ挙動になる
`Psych.load`

1
2
3
4

require "psych"
require "date"

5
6
7
8

default: &default
aaa: 2020-01-1
development:
<<: *default

9
10
11
12
13
14

data = <<~EOS

EOS
# OK
pp Psych.unsafe_load(data)
# => {"default"=>{"aaa"=>#<Date: 2020-01-01 ((2458850j,0s,0n),+0s,2299161j)>},
#
"development"=>{"aaa"=>#<Date: 2020-01-01 ((2458850j,0s,0n),+0s,2299161j)>}}

35.

まとめ

36.

まとめ

37.

まとめ YAML は psych というライブラリで実装されている `YAML == Psych``

38.

まとめ YAML は psych というライブラリで実装されている `YAML == Psych`` psych 4.0.0 から `Psych.safe_load` が `Psych.load` に置き換わるので結果的に非互換 になる 更にキーワード引数のみ受け付けるようになる

39.

まとめ YAML は psych というライブラリで実装されている `YAML == Psych`` psych 4.0.0 から `Psych.safe_load` が `Psych.load` に置き換わるので結果的に非互換 になる 更にキーワード引数のみ受け付けるようになる Ruby 3.1 では psych 4.0.0 がデフォルトでバンドルされるので注意

40.

まとめ YAML は psych というライブラリで実装されている `YAML == Psych`` psych 4.0.0 から `Psych.safe_load` が `Psych.load` に置き換わるので結果的に非互換 になる 更にキーワード引数のみ受け付けるようになる Ruby 3.1 では psych 4.0.0 がデフォルトでバンドルされるので注意 psych 4.0.0 を使う場合は Ruby 3.1 未満でも影響がある

41.

参照 Ruby の Psych.safe_load(YAML.safe_load)の引数が Psych v4.0.0 から非互換になる Secret Garden(Instrumental) Ruby の YAML.load が非互換になる(かもしれない) - Secret Garden(Instrumental)