MOSA Multi-OS Software Artists

MOSAはソフトウェア開発者を支援します

  • iPhone/iPod touch アプリ紹介
  • MOSA掲示板
  • 活動履歴
  • About MOSA(English)

MOSA Developer News[MOSADeN=モサ伝]第136号

2004-11-09

目次

  • “関西オープンソース2004”レポート(後編)村上幸雄★MOSA会員専用★
  • SqueakではじめるSmalltalk入門  第18回  鷲見 正人
  • 小池邦人の「Carbon API 徒然草」
  • 「Behind the WebObjects」    第31回  田畑 英和
  • ニュース・解説
MOSA会員専用記事

“関西オープンソース2004”レポート(後編) ビッツ有限会社 村上幸雄

 開催場所:大阪産業創造館
 日  時:2004年10月22日(金)〜23日(土)
http://k-of.jp/

 前回に引き続き、関西オープンソース2004のレポートの後編をお届けします。

Cocoa勉強会in関西

 Cocoa勉強会in関西は、昨年10月から横浜を中心に開催されていたCocoa勉強会を大森智史氏が関西での代表となって開催することになった、関西版の第一回こけら落とし勉強会です。勉強会は22日と23日の両日開催され、22日は関西版代表の大森氏が「AppleEventの送信」を23日はモサ伝ではお馴染みの新居氏が「Cocoa Bindings について」というタイトルで講演されました。
 前者はTechnical Note TN2045の情報を参考にして、gdbでAESend()が送信する電文を確認し、確認した電文をAEBuildAppleEvent()で送信するという内容でした。後者では、実際にCocoa Bindingsを使用したプログラムを作成してCocoa Bindingsの仕組みを確認するといった内容でした。

PHP-BOF:フレームワークを使ったWebアプリケーション開発

 MapleというオープンソースのPHPフレームワークの紹介です。
 Mapleは、出演者の高橋邦彦氏が、社内向けに開発したPHPのフレームワークをベースに、高橋氏のプライベートな時間と環境で開発されたようで、社内向けのソフトウェアから発展してきたというお話は、オープンソースと企業活動の関係の上手くいった事例として興味深いものでした。
 Maple ProjectのホームページのURLは以下のとおりです
http://kunit.jp/maple/

Appleはもう一度ブレークするか? -Macコミュニティの現在/過去/未来-

 新居雅行氏と大谷和利氏、MACお宝鑑定団のDANBO氏、MUGNETの山村和久氏の四名がパネラーとして、AppleとMacコミュニティーの歴史を語るという内容でした。HyperCardやKnowledge Navigator、OpenDoc、Cyberdogなどの懐かしいお話が聴けて、期待通り楽しめました。

コミュニティブース:WideStudio

 WideStudioは統合開発環境で、開発者の平林俊一氏がブースを担当されていましたので、開発の裏話や、WideStudioの内部について、詳しく聞くことができました。WideStudioは現在では平林氏の勤務先の事業の一環として扱われるようになりましたが、当初はプライベートな時間と環境を利用して開発されていましたので、例えば、仕事が夜の12時に終わるのに、それから帰宅して開発するといった苦労もあったようです。
 WideStudioの内部のお話をいたしますと、GUI部品は独自に実装しています。つまり、プラットフォームに依存しないGUIアプリケーションが開発できます。具体的には、四角を描画する描画ルーチンや、C++のnew演算子等のプラットフォームに依存する部分を移植すれば、そのプラットフォームでWideStudioのアプリケーションが動作するようになるそうです。
 WideStudioのアプリケーションが動作する環境にはT-Engineのような組み込みシステムもあります。また、WideStudioの開発環境はMac OS Xにも対応しています。したがいまして、今まで、Mac OS Xで組み込みシステムのアプリケーションを開発する事例は少なかったと思いますが、WideStudioを利用すれば、Mac OS Xで組み込みシステムを開発することが可能となります。

コミュニティブース:オープンメール環境(OME)

 OMEは、Mac OS Xで利用できるオープンソースのメールソフトで、コミュニティの代表は新居さんです。私もこのコミュニティに参加しています。
 OMEのコミュニティのメンバは、全国に散らばっていますので、このようなイベントのときでないと、実際に逢うことはありません。そういう訳で、関西オープンソースは関西在住のメンバと逢える、良い機会となっています。

ステージ

 ステージは3F展示会場と4Fの関西コミュニティ大決戦の会場の二カ所に設けられていました。内容は多種多様なのですが、その中で私の面白いと思ったのは以下のステージです。

・突撃インタビュー1号

 Microsoftの楠正憲氏への突撃インタビューです。
 Microsoftのエンジニア全員がVisual Studioを使用している訳ではなく、あるエンジニアの開発環境を覗いたところ、~(チルダ)がついているソースファイルもあったとか、Virtual PCを使用して、仮想マシン環境で動作しているWindowsをデバッグする実演等、裏話から技術的なお話まで聞くことができました。

・Samba日本語版開発裏話

 日本Sambaユーザ会の三浦広志氏と白井隆氏が、Samba日本語版開発の裏話や技術的な問題の説明、外国の技術者との共同作業で発生した苦労話を発表するという内容で、特に、日本人でも複雑なために敬遠される日本語処理の問題を外国の技術者に理解してもらうという大変な問題に、いかに対処したかという裏話は、Sambaの恩恵を受けている私としては頭が下がりました。

SqueakではじめるSmalltalk入門   第18回  鷲見正人

 本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。今回はノーティファイアで表示される情報の読み方を取り上げます。

メソッド#deposit:が定義された今、a BankAccountは、「deposit: 100」というような預金額を模した数値をパラメータに持つメッセージを受け取って期待される動きをすることができるようになりました。これは前回、インスペクタで束縛しているa BankAccountで確認しています。ただ、このチェックには、ちょっとした落とし穴があります。たとえば、あらためて、どこか適当な場所で、

BankAccount new deposit: 100

をdo it (cmd-D)してみましょう。別のBankAccountのインスタンスを作り、それに「deposit: 100」を送信しているだけです。結果、俗に“エラー”と呼ばれるノーティファイア(ピンクの横長のウインドウ)が現われて処理は中断してしまうはずです。

[fig.A]ノーティファイア
http://squab.no-ip.com:8080/mosaren/uploads/18a.png

 なぜ、このようなことになるのかをノーティファイアに表示される情報をもとに、解明してみましょう。

まず、BankAccountに送信されるnew、その結果返されるa BankAccountに改めて送られる「deposit: 100」も、いずれも不正なメッセージでないことについてはよろしいかと思います。では、なにゆえエラーが起こったのか。そのヒントとなる情報がノーティファイアのタイトルバーに表示されています。

MessageNotUnderstood: UndefinedObject>>+

 これは、UndefinedObject(のインスタンス)は、#+(というセレクタを含むメッセージ)を理解できません…という意味です。UndefinedObjectのインスタンスは、nilです。nilは、UndefinedObjectの唯一のインスタンスです。つまり、BankAccountに対する「new」か、その返値に対する「deposit: 100」というメッセージ送信の結果、派生的に生じるメッセージ送信カスケードのどこかで「nil + …」という式が評価されたことが分かります。もちろん、あやしいのは我々が定義した後者ということになりますが、ノーティファイアにより提示された情報からも、それを確認することができます。

ノーティファイアのウインドウ内部、デバッガ起動などのための3つのボタンを用意したペインの下のリストに目を向けてみましょう。各行は、このノーティファイアを表示するまでに送信されたメッセージの履歴を表わしています。Squeakを含めたSmalltalkシステムは、一般に、バイトコードを実行することで動作し、そのバイトコードインタープリタにはスタックマシンが使われています。このリストは、その各スタックに積まれたコンテキスト(インタプリタの内部状態)の一覧、というふうに解釈することもできます。いずれにせよ、今のところは、どういう経緯でノーティファイアの表示に至ったのかを、このリストで簡単に把握できるというふうに考えておけばよいと思います。

UndefinedObject(Object)>>doesNotUnderstand: #+
BankAccount>>deposit:
UndefinedObject>>DoIt
Compiler>>evaluate:in:to:notifying:ifFail:


 余談ですが、同様の情報を含んだものがノーティファイアの起動と同時に、仮想イメージと同じフォルダにSqueakDebug.logとして出力されています。Squeakシステムを維持できない致命的な障害を生じさせてしまったときは、いったん環境を抜けて、こちらを参考にするとよいでしょう。

1行目は、a UndefinedObjectが#doesNotUnderstand:メソッドを起動したことを表わします。括弧内のObjectは、実際にこのメソッドが定義されているクラス(メソッドホルダ)がUndefinedObjectではなく、Objectであることを表わしています。これは、ノーティファイアを起動するためのメッセージ送信です。一般にオブジェクトは自らが理解できないメッセージを受け取ると自身に改めて「doesNotUnderstand: …」というメッセージを送信する決まりになっています。なお、これに似た機構はCocoaでも採用されています。

本題の2行目はいったん飛ばして、参考のため、4行目と3行目についても簡単にコメントしておきましょう。do it (cmd-D)という操作によって選択文字列は、エディタなどのGUI関連オブジェクトを介して、コンパイラに渡されます。4行目のa Compilerに対するメッセージ送信がこれに当たります。このコンパイルの結果、選択文字列はバイトコードの関数(つまりメソッド)になるのですが、残念(?)ながらSmalltalkの世界ではクラスに属さない関数(つまりメソッド)の存在を許していません。そこでコンパイラはとりあえず、その文脈で擬変数selfに束縛されているオブジェクト(たいていはnil)のクラス(nilならUndefinedObject)に一時的に#DoItという名前のメソッドとして登録することで、その場をしのぎます。

3行目は、#DoItメソッドを登録したUndefinedObjectのインスタンスで擬変数selfに束縛されているnilに、改めて「DoIt」というメッセージを送信したことを表わします。この評価の後、#DoItメソッドは自動的に削除されます。do itやprint itなどのGUIを介した式の評価はあまりに手軽なので、一見、コードを直接インタープレットしているように思えるのですが、実際には、メソッドの定義とその起動のときと、まったく同じ手順(コンパイル、クラスへの登録、クラスに属するインスタンスへのメッセージ送信)を踏んでいることは、とても興味深いことですね。

さて、問題の2行目に戻ります。2行目は字面通り、そして当初の予想通り、a BankAccountに「deposit: 100」が送信されたことを表わしています。このことから、nilが自身に「doesNotUnderstand: …」というメッセージを送るはめになったのは、このコンテキストが原因であることを改めて確認できます。BankAccount >> #deposit:の定義は、

deposit: aNumber
self balance: self balance + aNumber

でしたね。今回の“騒ぎ”は、ここで送られる「+ aNumber」がnilに送られたためであることが総合的に判断できると思います。ではなぜ、nilに送られてしまったのか。答えは簡単で、self balanceがnilを返したからです。アクセッサ「#balance」の定義は、

balance
^ balance

ですから、同名のインスタンス変数「balance」に束縛されているオブジェクトを返すだけで、他にはなにもしていません。つまり、balanceにnilが束縛されているのが“騒ぎ”の真相ということになります。もう、すでに忘れておられる方もあるかもしれませんが第15回で触れたとおり、Smalltalkでは、未定義の変数には未定義値(undefined object)、すなわちクラスUndefinedObjectの唯一のインスタンスであるnilが束縛される決まりになっています。そして、nilは「+ 100」というメッセージを知らないので、自らに「doesNotUnderstand: …」を送信し、ノーティファイアを起動した、というわけです。

バックナンバー:
http://squab.no-ip.com:8080/mosaren/

小池邦人の「Carbon API 徒然草」(2004/11/05)

カタログウィンドウのイベントハンドラ

今回は、newCatalogWindow()で使われているsetupCatalogWindow()と、その関連ルーチンを調べてみます。メインウィンドウ(CatalogWindow)に実装するイベントハンドラの解説が中心となります。
まずは、再度setupCatalogWindow()を提示してみましょう。

void setupCatalogWindow( WindowRef window )
{
    setupCatalogWindowEvent( window ); // イベントハンドラルーチンの実装
    setupDataBrowser( window );        // DataBrowserコントロールの初期化
}


このシンプルなルーチンの役割は、CatalogWindowの初期化を行うことです。
実際の処理内容は「ウィンドウへのイベントハンドラルーチンの実装」と「DataBrowserコントロールの初期化」で、setupCatalogWindowEvent()が前者を、setupDataBrowser()が後者を担当しています。今回は、この2つのルーチンのうちsetupCatalogWindowEvent()についてのみ解説を行います。

void setupCatalogWindowEvent( WindowRef window )
{
    EventTypeSpec    list[]={
                              { kEventClassCommand, kEventCommandProcess },
                              { kEventClassCommand, kEventCommandUpdateStatus },
                              { kEventClassWindow, kEventWindowClose },
                              { kEventClassKeyboard, kEventRawKeyDown }
                            };

    InstallWindowEventHandler( window,myCatalogWindowEventHandler,
                                     GetEventTypeCount(list),list,window,NULL );
}

ここでは、4種類のイベントに対応できるようにEventTypeSpecリストが作成されています。kEventCommandProcessは、メニューアイテムやウィンドウに配置されているボタン(Control)のコマンドIDを受け取るためです。また、kEventCommandUpdateStatusは、メンテナンスが必要なメニュー(状況によりアイテムの利用可、不可を切り替える)のMenuRefを受け取り、それをメンテナンスルーチンへ渡すために使います。この2つのイベントはkEventClassCommandに属しています。ここまでの2つは、以前解説したアバウトダイアログのイベントハンドラとまったく同じです。

続くkEventWindowCloseは、ウィンドウへの「閉じなさい!」という指令を受け取るために用いられます。ファイルメニューから「閉じる」が選択された時や、ウィンドウのクローズドボックスがクリックされた時に送られて来ます。最後のkEventRawKeyDownは、キーボードのキーが押されたタイミングで何かを実行したい時に使います。イベントからはキーボード・キーのASCIIコード、物理コード、特殊キーON/OFF状況などの情報を得ることが可能です。kEventWindowCloseはkEventClassWindowに属し、kEventRawKeyDownはkEventClassKeyboardに属しています。詳しいイベントの挙動についてはCarbonEvent.hを参照してください。

イベントハンドラをウィンドウへ実装するのには、InstallWindowEventHandler()を利用します。また、この時にuserDataとして対応するウィンドウのWindowRefを渡しておきます。これにより、処理対象となるウィンドウをハンドラルーチン側で簡単に認識することが可能となります。以下が、メインウィンドウに実装されたイベントハンドラルーチンのmyCatalogWindowEventHandler()です。

pascal OSStatus myCatalogWindowEventHandler( EventHandlerCallRef myHandler,
                                                EventRef event,void *userData )
{
    OSStatus        ret=eventNotHandledErr;
    unsigned long   ekind;
    WindowRef       wptr;
    long            cls;
    HICommand       cmd;
    char            asc;

    cls=GetEventClass( event );   // イベントのクラス名を得る
    ekind=GetEventKind( event );  // イベントの種類を得る
    wptr=(WindowRef)userData;     // 処理対象ウィンドウのWindowRefを得る
    if( cls==kEventClassCommand ) // Carbon Eventのクラスはコマンド
    {
        switch( ekind )  // イベントの種類で分岐
        {
            case kEventCommandProcess: // コマンドIDが届く

                GetEventParameter( event,kEventParamDirectObject,typeHICommand,
                                             NULL,sizeof(HICommand),NULL,&cmd );

                switch( cmd.commandID ) // コマンドの種類で分岐
                {
                    case 'fold': // 「フォルダから...」ボタン

                        importImageFolder( wptr ); // フォルダ内ファイル登録処理
                        ret=noErr;
                        break;

                    case 'file': // 「ファイルから...」ボタン

                        importImageFile( wptr ); // ファイル登録処理
                        ret=noErr;
                        break;

                    case 'save': // ファイルメニューの「保存...」アイテム

                        saveCatalogFile( wptr ); // ドキュメント保存処理
                        ret=noErr;
                        break;

                    case 'laun': // 「起動」ボタン

                        openSelectedImageFile( wptr,1 ); // ファイル起動処理
                        ret=noErr;
                        break;

                    case 'view': // 「表示」ボタン

                        openSelectedImageFile( wptr,0 ); // ファイル表示処理
                        ret=noErr;
                        break;

                    case 'dele': // 「削除...」ボタンと
                                 // 編集メニューの「削除...」アイテム

                        deleteSelectedImageFile( wptr ); // ファイル削除処理
                        ret=noErr;
                        break;

                    case 'sall': // 編集メニューの「すべて選択」アイテム

                        selectAllDataBrowser( wptr,kDataBrowserItemsAdd );
                        break;   // ファイル一覧すべてを選択する

                    case 'call': // 編集メニューの「選択を解除」アイテム

                        selectAllDataBrowser( wptr,kDataBrowserItemsRemove );
                        break;   // ファイル一覧の選択を解除する
                }
                break;

            case kEventCommandUpdateStatus: // メニューのメンテナンス要求が発生

                GetEventParameter( event,kEventParamDirectObject,typeHICommand,
                                            NULL,sizeof(HICommand),NULL,&cmd );
                                // 選択されたメニューのMenuRefを得る
                ret=mainteCatalogWindowMenu( wptr,cmd.menu.menuRef );
                break; // メインウィンドウ表示でのメニューのメンテナンス実行
        }
    }
    else if( cls==kEventClassWindow ) // Carbon Eventのクラスはウィンドウ
    {
        GetEventParameter( event,kEventParamDirectObject,typeWindowRef,NULL,
                                                 sizeof(WindowRef),NULL,&wptr );
                          // イベントが発生したウィンドウのWindowRfwを得る

        SetPortWindowPort( wptr ); // カレントポートに設定
        switch( ekind ) // イベントの種類で分岐
        {
            case kEventWindowClose: // ファイルメニューの「閉じる」アイテムか
                                    // クローズドボックスがクリックされた場合

                closeCatalogWindow( wptr,0 ); // メインウィンドウを閉じる処理
                ret=noErr;
                break;
        }
    }
    else if( cls==kEventClassKeyboard )// Carbon Eventのクラスはキーボード
    {
        switch( ekind ) // イベントの種類で分岐
        {
            case kEventRawKeyDown: // キーボードのキーが押された場合

                GetEventParameter( event,kEventParamKeyMacCharCodes,typeChar,
                                                 NULL,sizeof(char),NULL,&asc );
                                  // イベントからキーコードを得る
                if( asc==0x8 )    // Deleteキーが押された(キーコード=0x8)
                {
                    deleteSelectedImageFile( wptr ); //  選択ファイルを削除
                    ret=noErr;
                }
                break;
        }
    }
    return( ret );
}


注目すべき点は、メニューアイテムとボタンに同じコマンドID(4バイト識別子)を割り振っておけば、どちらのイベントも同じ分岐位置へ到達すると言うことです。たとえば、コマンドID=’dele’は、編集メニューの「削除…」とウィンドウの「削除…」ボタンの両方に設定されており、どちらを操作しても同じ処理が実行されます。また、コマンドID=’clos’のボタンやメニューアイテムが操作された場合には、ウィンドウのクローズドボックスがクリックされた時と同じように、kEventClassWindowのkEventWindowCloseへとイベントが送られて来ます。こうした仕組みは、ウィンドウのStandard Event Handler側でコントロールされています。

注意する点は、ウィンドウに配置されたデータブラウザ(DataBrowser Control)にもStandard Event Handlerが実装されており、データブラウザがフォーカス(マウスクリック後に青枠で囲まれている状態)されている時には、編集メニューの特定のコマンドID(取り消し、カット、コピー、ペースト、削除、すべて選択など)を受けて、内部処理を実行するようになっていることです。こうした仕組みは、データブラウザだけではなくテキスト入力カラムなどにも備わっています。例えば、’sall’(すべて選択)は、メインウィンドウのハンドラ内で処理をしていますが、本当はアプリ側で何も行う必要はありません。ただし、データブラウザがフォーカスされていない時でも処理を実行したい場合があるわけで(今回がそう)、そうした時にはウィンドウ側のハンドラで、このコマンドIDに対応しておく必要があります。

もうひとつの注意点は、コントロール側の内部処理が自分のアプリに合わない時には対処が必要だということです。加えて、この内部処理はMac OS Xの古いバージョンだと機能しない場合があります。このサンプルアプリを作成した当時には、データブラウザ側にはStandard Event Handlerが実装されておらず、「削除…」のコマンドIDは’dele’の代わりに’clea’(クリア)だったのですが、何の問題も生じませんでした。ところが、最新のMac OS X 10.3では、データブラウザ側がコマンドIDの’clea’や’sall’を先んじて処理してしまい、ウィンドウまでイベントが送られて来ません(コントロールがフォーカスされている場合)。そこで、最近になって’clea’を’dele’に変更して処理がちゃん実行されるように改良したわけです。こうした小手先の操作ではなく問題を完全に解決したい場合には、データブラウザにもオリジナルのイベントハンドラを実装してイベントの流れを詳細にコントロールする必要があります。

次回は、setupCatalogWindow()で呼ばれているもう片方のルーチン、setupDataBrowser()とその関連ルーチンへと話を進めます。メインウィンドウにはファイル一覧を表示するためにデータブラウザが配置されていますが、それの初期化作業について解説します。

つづく

「Behind the WebObjects」  第31回  田畑 英和

 前々回から解説していますWOUnitTestフレームワークの3回目です。これまでセットアップからテストケースの作成方法までを解説しましたが、今回はフレームワークが提供しているテストケース作成用クラス”WOUTTestCase”の紹介をおこないます。

WOUTTestCaseの概要

 WOUnitTestフレームワークを用いてテストケース用のクラスを作成するには、フレームワークが提供する”WOUTTestCase”を親クラスとしたクラスの作成をおこないます。また”WOUTTestCase”はJUnitが提供するクラス”TestCase”のサブクラスとして実装されています。例えば”MyTestCase”という名前のテストケースクラスを作成したときには次のような継承関係になります。

“TestCase” <- "WOUTTestCase" <- "MyTestCase"

 前回は処理結果を評価するメソッドとしてassertTrueやassertFalseを紹介しましたが、これらのメソッドはJUnitの"TestCase"が提供するメソッドです。
 "TestCase"のサブクラス"WOUTTestCase"には、EOのテストを支援するメソッドが用意されていますので、具体的にどのようなメソッドが提供されているかを紹介していきます。

Validationのテスト

 EOCustomObjectクラスにはEOValidationインターフェースで定義されているEOの正当性検証用メソッドが実装されています。その1つvalidateForSave()はEOをデータベースに保存するときにデータの正当性を検証するためのメソッドです。このメソッドをオーバーライドすることにより独自の検証ロジックを組み込むことができます。”WOUTTestCase”には、次のようなデータ保存時の検証ロジックのテスト用メソッドが用意されています。

public static void assertValidForSave(EOValidation validationObject)
public static void assertInvalidForSave(EOValidation validationObject)

 assertValidForSaveはEOがデータベースに*保存できる*状態であることのテスト、assertInvalidForSaveはEOがデータベースに*保存できない*状態であることのテストをおこないます。
 フレームワークには以下のようなサンプルが用意されています。このサンプルでは、customer.foo()を実行した状態ではまだcustomerをデータベースに保存できる状態ではなく、customer.bar()を実行した状態で保存ができることをテストしています。

     public void testCustomerValidates() {
         Customer customer = new Customer();
         editingContext().insertObject(customer);
         customer.foo();
         assertInvalidForSave(customer);
         customer.bar();
         assertValidForSave(customer);
     }

 ちなみにこのサンプルでは、EOを新規に作成してValidationのテストをおこなっているだけで、実際にデータベースへの保存はおこなっていません。このままではEOが未保存のまま”WOUTTestCase”が提供するeditingContext上に残ってしまいますが、WOUTTestCase.javaに実装されているtearDown()内でediting Contextをrevertし、未保存のEOを削除しています。
 tearDown()は各テスト用メソッドの実行後に毎回自動的に呼び出されますので、このサンプルのようなコードを実行しても、次のテスト用メソッドが呼び出された時点でeditingContextはクリアされており、未保存のEOが影響をおよぼす心配はありません。

データベースの後始末

 では次にデータベースへの保存をおこなった場合の便利な機能を紹介します。テストケース上で新規作成したEOをデータベースに保存すると、当然データがどんどんたまっていってしまうことになります。別途データベースの初期化用スクリプトでも用意しておけばよいのですが、”WOUTTestCase”にはテスト中にデータベースに追加したデータを自動的に削除してくれる便利な機能があります。この機能を利用するには、データベースに追加するEOをあらかじめ次のメソッドで登録しておきます。

     protected void registerPersistentRootObjectForDeletion(
                                 EOEnterpriseObject anEnterpriseObject)


 すると、WOUTTestCase.javaのtearDown()でこのメソッドを用いて登録しておいたEOをデータベースから削除してくれます。フレームワークのサンプルに以下のようなコードがあります。このテストコードでは、実行中に実際にデータベースへデータの追加がおこなわれますが、テスト実行後に追加したデータがデータベース上から自動的に削除されます。ですので、データを書き込むテストをおこなった場合でも、テスト後にデータベースを別途初期化する手間が省けます。

     public void testCustomerSaves() {
         Customer customer = new Customer();
         editingContext().insertObject(customer);
         registerPersistentRootObjectForDeletion(customer);
         customer.foo();
         saveChanges(false);
         customer.bar();
         saveChanges(true);
     }


 なお、このサンプルコードではデータ保存に次のメソッドを用いていますが、このメソッドはWOUTTestCase.javaで実装されているメソッドです。
 このメソッドでは”WOUTTestCase”が管理するeditingContextに対してデータベースへの保存処理を実行します。

     protected void saveChanges(boolean assumeSuccess)

 boolean型の引数ですが、このメソッドでは保存処理に対するテストもおこなえるようになっています。この保存処理が成功するようなケースではtrueを、保存処理が失敗するようなケースではfalseを指定してください。

まとめ

 WOUnitTestフレームワークの機能をすべて紹介できたわけではありませんが、JUnitを拡張してEOのテストに便利な機能が提供されていることがお分かりいただけましたでしょうか。プロジェクトのビルド時に自動的にテストを実行するようなことも考えられますので、日常の開発に役立てていただければと思います。

さて、まもなく湘南ミーティングですね。
参加者の皆様、初台&湘南でお会いしましょう!!

ニュース・解説

今週の解説担当:新居雅行

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃Mac OS X 10.3.6のアップデートが公開
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Mac OS XおよびMac OS X ServerがVer.10.3.6へアップデートした。大幅な変更は少ないようだが、さまざまな点で小さいながらも変化はある。まず、Sambaのバージョンが3.0.5にアップデートされている。AFPも含めて、ファイル共有機能は安定性も向上させている。ディレクトリサービス関連も更新されており、Active Directoryのプラグインの更新や、ネットワークアカウントでのログインができない問題も修正が加えられている。Safariをはじめとして、アプリケーションに対する変更も行われている。SafariはWebアクセスで60秒経過するとタイムアウトになっていたが、無制限にレスポンスを待つようになった。また、スクリーンセーバでパスワード設定しているときに画面が真っ暗のままになってしまう点も解消されている。
同時に、オープンソース部分のDarwinもVer.7.6がリリースされており、Ver.10.3.6に対応したものが公開されている。

Mac OS X 10.3.6 Update (Delta) について
http://docs.info.apple.com/article.html?artnum=300080-ja
About the Mac OS X Server 10.3.6 Update
http://docs.info.apple.com/article.html?artnum=300100
Darwin 7.6
http://www.opensource.apple.com/darwinsource/10.3.6/

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃アプリケーションでメールを送信機能を付ける
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
アプリケーションがメールを送信する機能ではなく、Webサイトでよく見られるような、mailtoプロトコルのリンクのような機能をアプリケーションで実現する方法が解説されている。つまり、クリックなどをすると、メールアプリケーションが起動し、そのメールで新規メールを作成するといった状態を実現する方法である。NSWorkspaceを使った機能なので、掲載されているプログラム自体もシンプルであるが、openURLメソッドで、mailtoプロトコルをURLとして与えればいいということがポイントになる。

Technical Q&A: Sending an Email
http://developer.apple.com/qa/qa2004/qa1084.html

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃Finderでのファイル名ソートと同一機能を実現
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Finderではファイル名順に並べたとき、大文字小文字の区別をしないなどの処理が行われるが、システム機能としてはFinderの基準でテキスト比較する直接的なAPIは用意されていない。そこで、アプリケーションからFinderの基準で並べ替えを行う機能を組み込む方法がTechnical Q&Aで解説されている。
UCCompareTextDefaultというAPIを利用した比較関数を作成し、配列をその関数を利用して並べ替えるというソースコードのサンプルが掲載されている。

Technical Q&A: Sorting Like the Finder
http://developer.apple.com/qa/qa2004/qa1159.html

MOSAからのお知らせと編集後記は割愛します

MOSA Developer News   略称[MOSADeN=モサ伝]
Apple、Mac OSは米国アップルコンピュータ社の登録商標です。またそのほかの各製品名等はそれぞれ各社の商標ならびに登録商標です。
このメールの再配信、および掲載された記事の無断転載を禁じます。
http://www.mosa.gr.jp/
Copyright (C)2004-2006 MOSA. All rights reserved.