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_missingmethod_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
が利用されています。