Measure の改良について考えた

やりたい事は色々あるけど、まずは単位系をオブジェクト化しよう。改良後は次のようなコードが動くようにしたい。

require 'measure'

measure = Measure::Context.new
# 複数の単位を一度に定義
measure.def_unit :length => [:km, :m, :cm, :mm, :um, :nm]
# 複数の変換を一度に定義
measure.def_conversion { :km => { :m => 1_000 },
                         :m  => { :cm => 100, :mm => 1_000, :um => 1_000_000, :nm => 1_000_000_000 },
                         :cm => { :mm => 10 },
                         :mm => { :um => 1_000, :nm => 1_000_000 },
                         :um => { :nm => 1_000 } }
# ひとつの単位を定義 (これまでの方法)
measure.def_unit :aa, :length
# ひとつの単位に対する変換の定義 (これまでの方法)
measure.def_conversion :m, :aa => 10_000_000_000

measure.def_unit :length => [:in, :ft, :yd]
measure.def_conv { :in => { :cm => 2.54, :mm => 25.4 },
                   :ft => { :in => 12 },
                   :yd => { :ft  => 3, :in => 36 } }

さらに、これまでのコードとの互換性を保つため*1 Measure.default_context というスレッドローカルな属性を導入し、Measure.def_unit、Measure.def_conversion などを Measure.default_context に対するプロキシとして定義する。

現在 measure/length.rb でやってるように、よく使う単位をライブラリとして提供したい。今までは、ライブラリが require されたときに単位を定義すればよかった。単位系がオブジェクトになった場合、こんどはユーザが望んだ単位系オブジェクトに対してライブラリが提供する単位を定義できる必要がある。それを実現するために、Object#extend を使ってみようかと思っている。

require 'measure/weight'
measure.extend Measure::Weight

ここで Measure::Weight は measure/weight.rb で定義されるモジュールで、Module#extended をオーバーライドして、extend された単位系オブジェクトに対して単位と変換を定義する。

さいごに、現在は measure/support というライブラリでショートフォーム (Measure.form { 1.cm + 2.in } など) を導入できるわけだが、これと同様の目的で次のような記法を導入してみようかと思っている。

Measure.with(measure) do
  # この中では Measure.default_context == measure
end

*1:保たなきゃならない程の資産なんて無いわけがwwwwww