こんなymlがあったとして
#settings.yml twitter: consumer_key: 'aaaaaaaaaaaaa' consumer_secret: 'bbbbbbbbbbbbb' oauth_token: 'ccccccccccccc' oauth_token_secret: 'ddddddddddddd'
settings[:twitter][:consumer_key]みたいに扱いたく
きっとActiveSuportにあるだろうと調べてみると
rails4.0.2から入った deep_symbolize_keys というのがありました。
(つい最近入ったのは意外でした)
以前からある symbolize_keys というメソッドでは、再帰的に変換してくれません。
上記例だと、settings[:twitter] は okでsettings[:twitter][:consumer_key] はng。
ソース
#activesupport-4.1.1/lib/active_support/core_ext/hash/keys.rb class Hash def deep_symbolize_keys deep_transform_keys{ |key| key.to_sym rescue key } end def deep_transform_keys(&block) result = {} each do |key, value| result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value end result end end
やってることは単純で、Hashだったら再帰的にkeyをto_symしているだけです。
rescueは何じゃろなと思って触ってみると
keyがnilの場合を考慮したロジックでした。
まとめ
deep_symbolize_keys 便利!
ActiveSupportのソースコードリーディングは、読みやすくてよさそうです。
以下おまけ
deep_transform_keys の書き方をみていると、どうもinject化したくなってしまいました。
benchmark取ってみると、、、
class Hash def deep_symbolize_keys deep_transform_keys{ |key| key.to_sym rescue key } end def new_deep_symbolize_keys new_deep_transform_keys{ |key| key.to_sym rescue key } end def deep_transform_keys(&block) result = {} each do |key, value| result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value end result end def new_deep_transform_keys(&block) inject({}) do |result, (key, value)| result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value result end end end require 'benchmark' h = {"a"=> "a1", "b"=> "b1"} n = 1000000 Benchmark.bm do |x| x.report { (1..n).each{ h.deep_symbolize_keys } } x.report { (1..n).each{ h.new_deep_symbolize_keys } } end
user system total real 3.900000 0.050000 3.950000 ( 3.977981) 5.120000 0.030000 5.150000 ( 5.173796)
あ、だめだすごく遅い。injectは遅いのか。
ということでココに書いて忘れることにします。