Rubyの拡張によるアプローチの違い
Extension部分のコーディング
連携用のコードや拡張用のコードをCやC++でRuby向けに作成することは難しくありません。しかし拡張用コードの中から利用するRuby C APIの機能を詳細に解説したものを見つけることは難しいことでしょう。その中でも「Programming Ruby3」は推奨したい1冊です。この本ではCによるRuby拡張のコーディングの一般的な方法に加えて、拡張用のAPI機能についての記述が数多く掲 載されています。もし独自の拡張用コードを書くのであれば、必読ともいえるでしょう。
例えば、拡張用のコードでC++やCのミドルウェア用ライブラリへ直接アクセスするとします。そのときRubyのパッケージにあるモジュールの多く はRubyではなく、Cで書かれてOSの機能にアクセスすることもあります。そのためミドルウェア用に行う拡張では、ミドルウェアの機能をラッピングする 新しいRubyのモジュールを作ることになります。
これは、前述のRubyモジュールがOSの機能をラッピングするようなものです。Rubyアプリケーションは、作成したミドルウェアモジュールを Rubyモジュールと同様に利用できます。それらはRubyもしくはC、その他の言語で書かれていても関係ありません。
もし「Programming Ruby3」に必要なRuby APIの記述がなくても、インストール済みのRubyモジュールの機能で使えるものが数多くあります。またRubyのソースコードを調べることで、どのよ うな機能でも動作を理解することができるでしょう。
Rubyのmkmfモジュールは、拡張コードを簡単に作成します。次のような内容で、名前がexconf.rbというRubyファイルを作るだけです。
require 'mkmf'
create_makefile('myextension')
ここで、myextensionという文字列は作成するRubyモジュールの名前です。そして次のコマンドを実行します。
ruby extconf.rb
これでMakefileを生成することができます。その後「make」を起動することで拡張機能をビルドできます。なお、ちょっとしたオプションを extconf.rbファイルに追加することでビルドのカスタマイズが可能です。例えばincludeパスやライブラリや関数の存在を検証することもでき ます。
extconf.rbは現在のディレクトリにあるすべてのC++とCのソースファイルをMakefileに設定します。「make」によって共有ラ イブラリやダイナミックリンクライブラリ(DLL)を作成し、必要となったモジュールをRubyがアプリケーションの中へロードします。
myextensionという名前のモジュールの場合は、共有ライブラリの中にInit_myextensionという名前のC関数が存在すること が前提なので、共有ライブラリをロードする際にRubyはこの関数を呼び出して拡張機能の初期設定を行います。この関数の中で、拡張機能のRubyモ ジュールやクラス/関数をセットアップするのに必要なRuby API関数を呼び出します。
難しいところは、Rubyのレイヤの機能を公開するために設計し、コードを書く実装の部分です。先述したように、インピーダンスのミスマッチの問題 のために、機械的に生成したコードに頼るよりも、何をRubyのレイヤで公開したいかということを設計することが必要です。
ダイナミック言語が作るレイヤ
話をMacのIOKitフレームワークの例に戻すと、ノートPCの電源やバッテリ情報にアクセスするCの関数を闇雲に公開するのではなく、必要とな る様々なデータ型をサポートするためだけに、それらをラップするC関数を書くことにしました。この機能だけRuby下のレイヤで実現することで、不必要な 細かいことをRubyコード外に置くことができるのです。
このRuby/C++ミドルウェア統合では、Rubyで書かれたモジュールをRubyのアプリケーションが利用する多重のレイヤ構造を採用しまし た。このモジュールは、順次Cで書いたモジュールをラッピングし、ミドルウェアシステムに直接アクセスしています。このレイヤ構造のアプローチは、いくつ かの利点があります。
例えばアプリケーションレイヤはRubyとCの境界ではなく、RubyとRubyの間にあります。これによって、多くをRubyで実装することによってアプリケーションインターフェースの開発を単純化でき、C言語のみの場合よりも容易に開発が行えます。
またRubyモジュールがRubyとCの境界を包含することで、アプリケーションの直接のアクセスから隠蔽することもできます。アプリケーションイ ンターフェースや既存のアプリケーションに影響を与えることなしに、境界を跨いで機能の配置を変えることができます。
これによりRubyからCへの境界が隠蔽されるので、Cのコードだけでインピーダンスミスマッチの問題を解決する必要はなくなります。インピーダン スミスマッチの一部をCのレイヤで解決し、一部をRubyモジュールで解消します。必要があれば、Rubyアプリケーションに影響を与えないようなCのレ イヤを作るためにRubyモジュールには、慣用的なプログラミング方法をしないようなコードを作成します。
このようにSOAの開発において、SOAネットワーク上のアプリケーションが利用できる特定のサービス機能の開発要件は、設計のプロセスと正確に一 致する必要があることが必須です。あるテクノロジーの上でオブジェクトを直接公開するようにサービスを作る方法は、実装のための多くの細かい情報がサービ ス利用者に見えたり影響を与えてしまうために、一般的に良い方法とはいえません。
SOA開発担当者がサービス設計に注力すべきことは明らかであり、そのためにもエンキャプスレーション(包含関係)をはじめとして、テクノロジー同士の境界線やカップリング(組み合わせ)、結合の方法についての課題を検討する必要があるのです。
これまで説明してきたように、ミドルウェア統合のためにRubyを拡張することは比較的簡単に行えます。もしかしたら最初は大変だと感じるかもしれ ませんが、書籍やオンラインドキュメント、サンプルコード、「comp.lang.list」のようなRubyコミュニティ、そしてUsenetグループ とRubyのソースコードそのものなどを利用することで、開発のスピードを上げることは容易でしょう。
Rubyを利用することにより生産性が向上し、結果的に統合プロジェクトを実施する価値も高いものとなるのです。
- M. Schmidt, Enterprise Integration with Ruby, Pragmatic Bookshelf, 2006.
- S. Vinoski, "RPC Under Fire," IEEE Internet Computing, vol. 9, no. 5, 2005, pp. 93-95.
- D. Thomas, Programming Ruby, second ed., Pragmatic Bookshelf, 2005.
- S. Vinoski, "Old Measures for New Services," IEEE Internet Computing, vol. 9, no. 6, 2005, pp. 72-74.