はじめに
# 定数を定義した例
ARASHI_MEMBERS = ['大野', '松本', '二宮', '桜井', '相葉'].freeze
上記のように Rails で定義した定数は基本的に freeze しておくのがベストです。
不用意に変更されることを防げます。
freeze しておくと、もし ARASHI_MEMBERS
に破壊的な変更が行われようとした時に FrozenError を起こしてくれます。
irb(main):001:0> ARASHI_MEMBERS << '木村'
Traceback (most recent call last):
4: from /Users/dan/.rbenv/versions/2.7.1/bin/irb:23:in `<main>'
3: from /Users/dan/.rbenv/versions/2.7.1/bin/irb:23:in `load'
2: from /Users/dan/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/irb-1.2.3/exe/irb:11:in `<top (required)>'
1: from (irb):2
FrozenError (can't modify frozen Array: ["大野", "松本", "二宮", "桜井", "相葉"]
なので ARASHI_MEMBERS
を呼び出した時はいつでもこの5人であることが保証されます。
応用① freeze した定数を変更して使いたい時
もし ARASHI_MEMBERS を変更して使いたい時は dup
メソッドを使いましょう。 freeze されていない コピーを作れます。
irb(main):001:0> tmp_arashi_members = ARASHI_MEMBERS.dup
irb(main):002:0> tmp_arashi_members << '木村'
=> ["大野", "松本", "二宮", "桜井", "相葉", "木村"]
※ ちなみに dup せずに変数に定義して変更を行うとやはり FrozenError が発生します。
応用② ネストされた Array や Hash を freeze したい時
ちなみに上記の freeze メソッドはネストされていると深くまで完全に機能しません。
irb(main):001:0> JOHNNYS_MEMBERS = {arashi: ["大野", "松本", "二宮", "桜井", "相葉"] , kat_tun:['亀梨', '上田', '中丸']}.freeze
irb(main):002:0> JOHNNYS_MEMBERS[:arashi] << ' 木村'
=> ["大野", "松本", "二宮", "桜井", "相葉", " 木村"]
irb(main):003:0> JOHNNYS_MEMBERS
=> {:arashi=>["大野", "松本", "二宮", "桜井", "相葉", " 木村"], :kat_tun=>["亀梨", "上田", "中丸"]}
定数のネストした配列に変更を加えましたがエラーが出ませんね。
ネストの深くまで freeze させるためには、 gem 'ice_nine'
の deep_freeze
メソッド を使います。(導入方法はこちら)
※ 他にも同じような gem はあると思いますので何を使っても大丈夫です。
変更を加えようとするとちゃんとエラーが出てますね。
irb(main):001:0> JOHNNYS_MEMBERS = {arashi: ["大野", "松本", "二宮", "桜井", "相葉"] , kat_tun:['亀梨', '上田', '中丸']}.freeze
irb(main):002:0> JOHNNYS_MEMBERS[:arashi] << ' 木村'
Traceback (most recent call last):
4: from /Users/dan/.rbenv/versions/2.7.1/bin/irb:23:in `<main>'
3: from /Users/dan/.rbenv/versions/2.7.1/bin/irb:23:in `load'
2: from /Users/dan/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/irb-1.2.3/exe/irb:11:in `<top (required)>'
1: from (irb):8
FrozenError (can't modify frozen Array: ["大野", "松本", "二宮", "桜井", "相葉"])
応用③ ネストされた Array や Hash の定数を変更して使いたい時
deep_dup
メソッドを使いましょう。 ※ こちらは Rails に標準で備わっています。
irb(main):001:0> JOHNNYS_MEMBERS = {arashi: ["大野", "松本", "二宮", "桜井", "相葉"] , kat_tun:['亀梨', '上田', '中丸']}.deep_freeze
irb(main):002:0> tmp_johnnys_members = JOHNNYS_MEMBERS.deep_dup
irb(main):002:0> Jtmp_johnnys_members[:arashi] << ' 木村'
=> ["大野", "松本", "二宮", "桜井", "相葉", " 木村"]
irb(main):003:0> tmp_johnnys_members
=> {:arashi=>["大野", "松本", "二宮", "桜井", "相葉", " 木村"], :kat_tun=>["亀梨", "上田", "中丸"]}