kiyokaのブログアーカイブ

Archive of old blog posts

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版を用意すれば、それなりに喜ばれるだろう。