title: Apollo - Update Information
author: YOSHIDA Kazuhiro
mailto: moriq@moriq.com

1. Update Information

1.1.1. new_sub_menu, new_item, new_line

deco.rb (deco project) で落ちたため， CompoAlloc を ChildAlloc に変更

1.1. 2000/08/30

1.1.1. Phi.mainloop

Phi で新規に開いた Form がないとき， %c:Phi.mainloop% すると抜けられなかった。そこで， %c:Phi.mainloop% 内部 ( Phi_mainloop ) の loop の前に PhiAliveTest するようにした。

しかし，こうすると PhiExport したとき，既存の Form だけになると mainloop からすぐ抜けるので， Phi のイベントハンドラが無効になる。

これを避けるため， Form_alloc では ChildAlloc ではなく FormAlloc するようにした。 FormAlloc は，もともと Form を解放しないので free は問題ない。

問題ないと思ったが，問題があった。 FormsClose で terminate できなくなった。 ClearEvents の扱いも問題となった。

既存の Form だけになると mainloop からすぐ抜けるのは， ClearEvents のためだと気づいた。

結局，次のように対処した。

* Phi_mainloop では loop の前に PhiAliveTest する。
* Form_alloc では ChildAlloc する ( 以前と同じ ) 。
* FormsClose ( PhiTerminate で呼ばれる ) で RemoveParentAttr, ClearEvents するのは PhiObjectList に格納されている Form のみ，つまり export した Form は含まない。

いまのところ，これでうまくいっている。

1.1.1. Phi.export

%c:PhiSetInitProc% で %c:PhiExport% する，従来の export 法は欠陥があった。
--code
procedure init;
begin
  PhiExport('App');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ruby_init;
  ap_env_path;
  PhiSetStdoutProc(stdout);
  PhiSetGetsFunc(gets);
  PhiSetGetcFunc(getc);
  PhiSetInitProc(init);
  Application.OnException := OnAppException;
end;
--
%c:PhiSetInitProc% で登録した procedure は， %c:require 'phi'% した時点で呼ばれる。

さて， %c:TEditor% を使ったアプリケーションでは %c:TEditor% オブジェクトを export するために %c:require 'Editor'% が必要であるが， %c:require 'Editor'% は %c:require 'phi'% の後に置かないといけない。なぜなら， %c:mPhi% などは %c:require 'phi'% で初期化されるからだ。

よって， export を %c:require 'Editor'% の後に行えるようにする必要がある。

%c:Phi.export 'App'% ( あるいは %c:Phi.export :App% ) で任意の時点で export できるようにした。

1.1.1. Jpeg, Action, Grid, Shell, Filesel

Jpeg を使えるように TJpegImage ( Jpeg Unit ) を Phi に組み込んだら， Phi.dll がやたらと大きくなった。これはいけないと思い，この機会に，主要でない Unit は Phi.dpr から追い出すことにした。

1.1.1. deco

deco project に TEditor を組み込んだ。 deco project とは， Apollo で INCM を作るという企画である。今のところ，サポートする掲示板システムは MQ-BBS SYSTEM だけだ。

1.1. 2000/08/10

1.1.1. MainMenu, MenuItem, DefineMenus in PhiExportFunc

1.1. 2000/07/30

1.1.1. 変数パラメータを持つ手続き の DLL 呼び出し
変数パラメータを持つ手続き，例えば
--code
procedure ap_data_get_object(v: Tvalue; AClass: TClass; var ret);
--
を %c:external PhiDLL% すると， DLL 呼び出しに失敗する。なぜだろう ?

1.1. 2000/07/26

1.1.1. >>, shr

C の >> は右シフト演算子である。 Delphi の shr は符号なし右シフト演算子である。

1.1. 2000/07/23

1.1.1. Apollo 開発史

Project Apollo はいつ頃始まったのだろうか。正確な日時は定かではないが，現在，最も古いファイルスタンプは $(APOLLO_SRC)\Phi.res の 00/03/14 12:01 である。たぶん， 3/14 に Phi.dll が初めて作られたのであろう。 $(APOLLO_SRC)\rb\*.pas が作られたのは，これより少しさかのぼる。しかしそれは，おそらく 3 月中である。つまり， 2000 年 3 月に Project Apollo は始まったのだ。

1.1.1. Image, Picture

1.1. 2000/07/11

1.1.1. App Path

start で起動できるように App Path を登録するようにした。 thank you, tk.

1.1.1. RGUI

Hbox, Vbox の空白は一考の余地がある。凝った実装にすると，小さくした時に縮退して Windows ごと落ちてしまうことがある。

1.1. 2000/07/05

1.1.1. Phi.mainloop

Application.Run の中なら Phi.mainloop は特に必要ない。

1.1.1. ActiveScriptRuby

ActiveScriptRuby から Phi を使えるようにした。起動が遅すぎる。

1.1. 2000/07/03

1.1.1. Apollo.exe JumpError

例外で終了したときに，例外が飛んだファイルを開き，キャレット位置を設定するようにした。

1.1.1. ap_env_path

これまで， drag and drop で起動すると load path ($:) が正しく設定されず， 'phi' が見つからなくてスクリプトが動かない状況であった。

その原因は， RUBYLIB 環境変数が有効でなかったことだ。 RUBYLIB は ruby_options を評価するときに load path に追加される。 (ruby.c)

Apollo では， ruby_options は Ap.exe にだけ使っている。現段階では Apollo.exe に ruby_options を使うとうまくいかない。それは， Apollo.exe の Ruby は組み込み Ruby なので，くりかえし load し，評価できるからだ。

1.1.1. ini

1.1.1. event handle proc

1.1. 2000/06/27

1.1.1. overload となる event handler の扱い

自動的に Ruby Object に写像できるようになると， Delphi 側で定義した event handler の overload が問題となる。

( Phi の ) 実装の容易さ・ ( Delphi アプリケーションの ) 実行効率の面から， handler の overload は行わないことにした。 overload を許すような handler は，明示的に次の handler を呼ばなければならない。

もちろん，これは Delphi の event handler と，それを overload しようとする Ruby の event handler の関係を考えているのであって， Ruby の event handler が Ruby の event handler を overload することには関知しない。

1.1.1. PhiDefineControls

Delphi Control を Ruby Object へ写像する手続きを用意した。

1.1.1. Delphi 集合型

'set of 基本型 ' で表される Delphi 集合型は Ruby 配列として扱う。今回，ビットフラグに見立てた整数で表すことを考えたが，

  Ruby 側でややこしいビット演算を行わなければならない

  : 配列での表記 ( 現仕様 )
    
--ruby
[Phi::MB_OK, Phi::MB_CANCEL]
--
  : 整数での表記 ( 不採用 )
    
--ruby
1 << Phi::MB_OK | 1 << Phi::MB_CANCEL
--
  Delphi 側で集合型から整数型へのキャストができないので，要素をひとつずつ調べることになり，配列にした場合と同じくらい手間がかかる

という理由から不採用になった。

1.1.1. double 型

C の double 型は Extended 型にしていたが Double 型に直した。

1.1. 2000/06/26

1.1.1. Apollo:module

Apollo.exe 自体を Phi に組み込むことに成功した。普通の Delphi アプリケーションに Ruby Unit, PhiExtension Unit を組み込めば Ruby を通した無限のカスタマイズ性を付加できる。

1.1. 2000/06/24

1.1.1. rbdoc after here document

here document の直後にある行では修飾が行われていなかった。

1.1.1. Phi.gets

terminate ボタンを押したときには Ctrl+D と同じ処理をすることで，正常に終了するようになった。

%c:Phi.gets% は %c:$_% を更新するようになった。これは， Ruby 組み込みの関数 %c:gets% と同じ振る舞いである。なぜこのようにしたか。それは次のように書いた場合， %c:$_% を更新しないのは直感に反すると考えるからである。
--ruby
require 'phi'
include  Phi
while gets
  print
end
--
この場合の %c:gets% は %c:Phi.gets% である ( %c:include% が効いている ) 。

1.1.1. call event handler of freed object

%c:on_resize, on_close, on_deactivate% は，その reciever であるところの object が GC によって解放されたあとに呼ばれることがある。この呼び出しを %c:funcall2()% で Ruby 側にまわすと当然落ちるので，次のように検査する。
--code
  if ObjectList.IndexOf(Sender) = -1 then Exit;
--
1.1.1. Ruby thread

Ruby thread のサンプル philos.rb を Apollo で実行して，終了する。 thread の終了は rb_exit(0) で行っている。そこまではいいのだが，そのあと続けてほかのサンプル，例えば global.rb を実行してみると， galobal.rb の出力の前に philos.rb の出力がひょっこり顔を出すのだ ! ほんと，やってられないよ。

%c:ruby_run()% などで使われている %c:rb_thread_cleanup()% が，解決の糸口かもしれない。

1.1. 2000/06/22

1.1.1. Font#on_change

Font は Component ではない。ということは tag はない。なのに on_change と結び付けていたため， recv を tag から拾おうとしたときに illegal access となり，落ちていた。

1.1.1. rbdoc join.rb, half.rb, form.rb

here document は触らないように修正した。

1.1. 2000/06/21

1.1.1. rbdoc self[]

self[], self== など，いわゆる記号メソッドの anchor がおかしかった。

1.1.1. Ap.exe gets

Ap.exe の gets がおかしかった。 CGI.exe も同じ。

1.1.1. Ap.exe args

Ap.exe の引数の扱いがおかしかった。

1.1. 2000/06/20

1.1.1. PhiHandle.Free

*.exe において Phi の Handle が解放されていなかった。

1.1.1. MenuItem.on_click

なぜか MenuItem.on_execute になっていた ( おそらく Dialog 関係と同時に実装したからだろう ) 。

1.1.1. ChDir

script の load 時に script の directory を current directory にする。どこから起動しても Icon.new.load('icon1.ico') はうまくいくだろう。もちろん， icon1.rb が script と同じ directory になければならないが。

1.1.1. philos.rb

難問であった， philos.rb を正常終了させる問題が解決した。 SystemExit という表示は， Delphi 側から rb_exit() を呼んだことを意味する。

副作用として，全て出力し終わらずに Ruby 側が終了してしまうことがあるようだ。 Thread を使っていなくても，標準出力の途中で SystemExit と表示されるので，この不具合だとわかる。

1.1.1. gets

gets は $stdin が File の instance でない場合， $stdin.gets を使う。 p, print は $stddef が File の instance でない場合， $stdin.write を使う。今回， Phi.gets を用意し， require 'phi' 時に $stdin = Phi となるようにした。

これでやっと console は console になったわけだ。 Ctrl+C, Ctrl+D, Ctrl+Z が最初にある文字列を送ると，入力完了とみなす。

1.1. 2000/06/18

1.1.1. initialize

Ruby の initialize は紳士協定である。拡張ライブラリを書くもの，紳士でありたい。つまり，なんらかのクラスの new を実装するときに，明示的に新規に生成したオブジェクトの initialize を funcall で呼ぶ必要がある。

このたび， Phi, BitBtn, Dialogs, RDB は紳士になった。

1.1.1. InitMenuItem

MenuItem ... メモリ管理でどれだけ頭を悩ませたことか。あらゆる component の生成時の owner を nil にしたのに，どうもおかしい。 $(DELPHI)\Source\Vcl\menus.pas を読むと分かるのだが， NewMenu の中で InitMenuItem という手続きを呼んでいる。これがちゃっかり menu_item たちの owner を設定していたのだ。

1.1. 2000/06/17

1.1.1. Exception

{ in PhiHandle.pas }

PhiFail 時に term_threads するようにした。これでイベントハンドラの中で Ruby の例外が飛ぶと Ruby 側は終了する。
--ruby
def btn.on_click(foo) # ArgError
end
--
{ in Phi.dpr }

load_protect で rb_load_protect() を呼んでいるが，これを try .. except で囲み， Delphi 例外 E を拾う。ここで新たに DelphiError という Ruby 例外を生成し，その message として E.message を渡す。
--ruby
form.icon = Icon.new.load 'icon1.ico' # DelphiError
--
{ in Spinel.pas }

このほかに起こり得る例外としては，イベントハンドラの中ではない，いいかえると， Phi.mainloop の中ではないところで飛ぶ Delphi 例外が考えられる。この例外は Application.OnException で拾う。ここで新たに DelphiError という Ruby 例外を生成し，その message として E.message を渡す。

1.1.1. stdio

%c:$stdout, $stderr, $>% は %c:require 'phi'% 時に %c:Phi% になるようにした。次のような指定は，もはや必要ない。
--ruby
$> = Phi
$stdout = Phi
$stderr = Phi
--
これで， phi を使わない一般的な Ruby script でもそのまま Apollo で実行できるようになった。

1.1.1. registered properties

DefineProp で自動登録した property を TreeView で表示するようにした。もともとデバッグしやすくするために付けた機能だが，役に立つと思う。

将来は， Ruby の Module にあるメソッドを使って，メソッドのきちんとした一覧表を作るようにしたい。

1.1. 2000/06/14

1.1.1. rbdoc include

テキストファイルの内容をそのまま埋め込めるようになった。 Project Apollo ML 開店に伴う拡張である。 (^-^)

: 書式
  %c:include: ap-list.html%

1.1.1. apollo TObject, TMethod な property を自動登録

これまで property は基本的な型 (Integer, Ord, Set, Float, String) のものだけ自動登録できていた。今回， TObject を読み書きする property (TStrings, TFont, TCanvas, etc.) と TMethod を読む property (event handler) の自動登録に成功した。デバッグのために FormPropInfo を新たに作成した。この影響は大きく，不具合を潰していく過程で source は大幅に書き換わっている。

いまだ理解できない挙動がいくつか見られる。 %c:Memo#text% は自動登録するくせに，落ちてしまうので，手動で上書きしている。

TObject な property は，単純な実装では読み書き時に新たな Ruby Object を生成してしまうので， new 時に attribute (instance variable) として格納し，これを読み書きするようにした。この方法だと， Ruby 側で property を読むときに毎回同じ Object が返ってくるのだ。

1.1. 2000/06/13

1.1.1. rbdoc nav

階層構造上を探索できる top, up, prev, next リンクを自動的に張れる仕組みを作った。

1.1. 2000/06/12

1.1.1. rbdoc item が hierarchy に対応

今まで rbdoc の here document tag のひとつ 'item' はインデントをつけてもフラットな item list を生成していた。これを，インデントを反映して階層構造を持つ item list を生成するように改良した。 'enum' tag も同様に改良した。

1.1. 2000/06/11

1.1.1. DBGrid のペイント

Delphi4 プログラミングバイブル p.544 12.4.1. DBGrid のペイントを Apollo で実現してみた。このために新たにいくつか class を実装した。

* Phi
  * Rect
  * Canvas
  * Bitmap
* RDB
  * Column
  * Columns

Rect は Delphi(Object Pascal) 側では TRect という名の Record 型である。これまでの class はすべて Delphi 側では Class 型のものを移植したものだから，かなり毛色が違う。結局， rb_data_object_alloc() の free 引数を ap_dispose() にする形を新たに導入した。

1.1. 2000/06/10

1.1.1. EventQueue

EventQueue を使わなくなった。そもそも EventQueue を使っていたのは歴史的な理由からである。初期の Apollo は Apollo.exe そのものと，そこから起動した Ruby を別の thread に置いて管理していた。そのため， Apollo.exe が受け取った event を Ruby 側に直接送ることはできなかったので， Queue を介して送ったのである。

代わりに，今度は Apollo.exe が受け取った event は直接 Ruby に送られることになったために不具合が生じた。 Apollo の event handler で rb_funcall2() を呼ぶようになったわけだが，この rb_funcall2() の中で Ruby 例外が飛んだらどうなるだろうか。一見すると，起動時の rb_load_protect() が守ってくれるように思われる。しかし， Apollo の，つまり Delphi の event handler は main thread に対して割り込むようにして実行される。このため，すべての event handler に rb_protect() が必要になった。

実際， event handler の中で Ruby 例外が飛ぶように書いておいて実行してみると，例外は途中でつかまることなく rb_protect() までやってきて status code を返すのだが， main thread はそのまま動きつづける。これはなかなか奇妙な振る舞いだと思われるが， event handler で起こる例外について特に注意を払わなくても継続して実行できるのは案外便利である。

1.1.1. etc.

Phi 起動時に $:.push get_dir.tr('\','/')

Application.HandleMessage を使っていたところを Application.ProcessMessages に変更
