[Rails便利機能]ActiveSupport::OrderedOptions

Hashの要素をメソッドっぽく取得できるやつです。 ググってソースを見ていたら、改めて発見があったのでメモしておきます。

ActiveSupport::OrderedOptions

実装も非常にsimpleです。

#[]=#[]

rails/ordered_options.rb

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  class OrderedOptions < Hash
    alias_method :_get, :[] # preserve the original #[] method
    protected :_get # make it protected

    def []=(key, value)
      super(key.to_sym, value)
    end

    def [](key)
      super(key.to_sym)
    end

Hashを継承し、#[]=#[] でsymbolを使って値のセットと取得を行うようにしています。 ちなみに、_get はパフォーマンス面で優位な元のHashの方を残しているようです。

method_missing

method_missingを利用して、.でアクセスできるようにしています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    def method_missing(name, *args)
      name_string = +name.to_s
      if name_string.chomp!("=")
        self[name_string] = args.first
      else
        bangs = name_string.chomp!("!")

        if bangs
          self[name_string].presence || raise(KeyError.new(":#{name_string} is blank"))
        else
          self[name_string]
        end
      end
    end

=のロジックはsimpleですが、!のロジックが一瞬分からなかったので、履歴を見て見ると !付きでアクセスした場合、KeyError を発生させたい(そうすることで短くかけるケースがある) ための実装のようでした。 Add bang version to OrderedOptions · rails/rails@e768c51

まぁ、分からんでもないが、この存在に気づいている人は少ないかもしれません。

String#+

もうひとつ、よく分からない実装がありました。

1
name_string = +name.to_s

この+は一体なんだろうと調べてみると、以下に記載がありました。
String#+@ (Ruby 3.0.0 リファレンスマニュアル)

frozen textを考慮した実装のようで、frozen(immutable)な文字列が渡ってきても、chomp! するためにunfrozenな文字列として処理しているようです。

ActiveSupport::InheritableOptions

もう一つ便利な機能としては、同じファイルに記載されている、既存のhashをOrderedOptionsに変換するクラス。

1
h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' })

という感じで利用できます。便利。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  class InheritableOptions < OrderedOptions
    def initialize(parent = nil)
      if parent.kind_of?(OrderedOptions)
        # use the faster _get when dealing with OrderedOptions
        super() { |h, k| parent._get(k) }
      elsif parent
        super() { |h, k| parent[k] }
      else
        super()
      end
    end

ここで先述の、_getが利用されています。

updatedupdated2021-12-072021-12-07
コメントを読み込みますか?