nativeとpureの両方に対応したgemを作る方法
[Sekka]の曖昧文字列マッチングを担っているkiyoka/fuzzy-string-matchというgemのpure ruby版を作った話を書く。 JSONのgemがnativeとpureの両方をサポートしていたので参考したのだが、結果的にJSONよりもコンパクトに実現できたのでメモしておこう。
[Sekka]がJRubyでは動かないのでkiyoka/fuzzy-string-matchのJRuby対応は自分は必要ないのだけど、GitHub上でJRuby用のforkが多かったので対応した。 自分はDebianパッケージ化の準備がメインの作業なのでgemの依存関係を直すだけでいいのだが、気分がノッている間に作業した。こういう勢いは重要だ。 DebianにはJRubyも入っているので、うまくいけばDebianのJRubyでも使えるのかな?
JRuby対応といってもJavaで高速な編集距離ライブラリをコーディングするわけではなく、pure Ruby版も用意しただけ。 GitHubでforkしてくれた方々はC言語に依存した部分を外してpure rubyのコードのみ残し、コメントに「JRubyで動かすため」と書いておられた。 処理速度もpure rubyで十分と書いておられる方もいた。
要望
JRubyでは、gccを必要とするRubyInlineをインストールしてほしくない。
- fuzzy-string-match_pure パッケージはRubyInlineに依存せず、pure Ruby版のみがインストールされてほしい。
- fuzzy-string-match と fuzzy-string-match_pure の二つのパッケージを両方同時にインストールしても良いようにしたい。
- fuzzy-string-match は nativeとpureの両方を同梱してあり、テスト用に動的に切り替えれるようにしたい。
動的に切り替えるAPI
– requireで切り替えれる native版を使いたい場合
require 'fuzzystringmatch'
@jarow = FuzzyStringMatch::JaroWinkler.create()
pure版を使いたい場合
require 'fuzzystringmatch/pure'
@jarow = FuzzyStringMatch::JaroWinkler.create()
– コンストラクタで切り替えれる native版を使いたい場合
require 'fuzzystringmatch'
@jarow = FuzzyStringMatch::JaroWinkler.create( :native )
pure版を使いたい場合
require 'fuzzystringmatch'
@jarow = FuzzyStringMatch::JaroWinkler.create( :pure )
実装方法(Rubyコード)
native版にのみRubyInlineで実装したRubyクラスを含める。 require ‘fuzzystringmatch’ でrequireした時、native版のクラスが存在しなければ、自動的にpure版にfall backするようにした。
以下がLoadError例外処理を入れたrequire。JSONと同じ方法。
require 'fuzzystringmatch/pure'
begin
require 'fuzzystringmatch/inline'
rescue LoadError
end
factoryメソッドで、実際にpure版にfall back しているコードがこちら。 native版のクラスのNameError例外を補足して、pure版のインスタンスを返している。
def self.create( type = :pure ) # factory method
case type
when :pure
FuzzyStringMatch::JaroWinklerPure.new
when :native
begin
FuzzyStringMatch::JaroWinklerInline.new
rescue NameError
STDERR.puts "fuzzy-string-match Warning: native version is disabled. falled back to pure ruby version..."
FuzzyStringMatch::JaroWinklerPure.new
end
end
end
実装方法(gemspec)
fuzzy-string-match_pure gemパッケージは以下のようにしている。
- RubyInlineへの依存を外している。
- native版のRSpecテストケースを含めていない。
- 以下のファイルを含めない。
lib/fuzzystringmatch/inline.rb lib/fuzzystringmatch/inline/*
それによって、factoryメソッドで :native が指定された場合でも、前述のRubyのLoadError例外によりpure版にfall backする。
このような方法で、純粋な計算しかしないライブラリではpure ruby版を用意すれば、それなりに喜ばれるだろう。