Swikiをアクセスするものを進化させて、pukiwikiなどに対応しようとしている。
■おとといからの経過
1)エンティティ変換を実装した(マークアップのみ)
MultiStringでは#asUnHtmlは動かない。
2)ハードコーディングを極力なくした
3)Pukiwikiのページ編集・ページ取得・ファイルアップロード実装
ローカルで行ったテストはパス
4)POST時の'Content-Disposition'ヘッダをまともにした。
■おとといからの経過
1)エンティティ変換を実装した(マークアップのみ)
MultiStringでは#asUnHtmlは動かない。
2)ハードコーディングを極力なくした
3)Pukiwikiのページ編集・ページ取得・ファイルアップロード実装
ローカルで行ったテストはパス
4)POST時の'Content-Disposition'ヘッダをまともにした。
MultiStringで文字実体参照を元にもどそうと#asUnHtmlを使ったが、どうもダメだった。
メソッドには
となっていた。実際
となって変換されない。さぁ、どうすべ。
メソッドには
asUnHtml self flag: #toBeImplemented.
となっていた。実際
'例::&' asUnHtml -->'例::&'
となって変換されない。さぁ、どうすべ。
SqueakのMVC関連リンク
-Forms, Views, and Windows
-Method for making Morphs
-FormsViewsAndWindows
-MVC
-Hierarchical text organizer example (MVC)
-MVC
-Customizing the Squeak UI
-Krasner Pope tutorial on MVC
-Responsibilities of Model, View and Controller
-Debugging UI
-How to develop GUI applications in Squeak
-How to use the MVC elements (an example)
-Pluggable Widgets
-ModelViewControllerExercises
-MVC
-Model View Controller or MVC
-Forms, Views, and Windows
-Method for making Morphs
-FormsViewsAndWindows
-MVC
-Hierarchical text organizer example (MVC)
-MVC
-Customizing the Squeak UI
-Krasner Pope tutorial on MVC
-Responsibilities of Model, View and Controller
-Debugging UI
-How to develop GUI applications in Squeak
-How to use the MVC elements (an example)
-Pluggable Widgets
-ModelViewControllerExercises
-MVC
-Model View Controller or MVC
ひさびさの更新です。Yaxoとかの解析とかしててblogから遠のいてました。
前回はWorkspaceから操作する方法であったが,Projectメニューの'手元のディスクにだけプロジェクトを保存する'で保存できるようにProject>>writeFileNamed:fromDirectory:toServer:を下記のように変更した。これでメニューから保存できるようになった。だが、読込はどうしようか?やはりProjectメニューの'プロジェクトをファイルから読込…'にいれるべきかな。読み込みも出来て、ある程度テストしたらチェンジセットにまとめて公開します。
前回はWorkspaceから操作する方法であったが,Projectメニューの'手元のディスクにだけプロジェクトを保存する'で保存できるようにProject>>writeFileNamed:fromDirectory:toServer:を下記のように変更した。これでメニューから保存できるようになった。だが、読込はどうしようか?やはりProjectメニューの'プロジェクトをファイルから読込…'にいれるべきかな。読み込みも出来て、ある程度テストしたらチェンジセットにまとめて公開します。
| local resp gifFileName f project writer projectFile | projectFile := localDirectory oldFileNamed: localFileName. gifFileName _ self name,'.png'. localDirectory deleteFileNamed: gifFileName ifAbsent: []. local _ localDirectory fileNamed: gifFileName. thumbnail ifNil: [ (thumbnail _ Form extent: 100@80) fillColor: Color orange ] ifNotNil: [ thumbnail unhibernate. ]. f _ thumbnail colorReduced. "minimize depth" f depth > 8 ifTrue: [ f _ thumbnail asFormOfDepth: 8 ]. project := FileStream readOnlyFileNamed: projectFile fullName. project binary. writer := PNGSqeuakObject on: local. [ writer nextPutImage: f interlace: 0 filter: 0 Object: project contents] ensure: [local close. project close. projectFile close.]. [local _ StandardFileStream readOnlyFileNamed: (localDirectory fullNameFor: gifFileName). (primaryServerDirectory isKindOf: FileDirectory) ifTrue: [primaryServerDirectory deleteFileNamed: gifFileName ifAbsent: []]. resp _ primaryServerDirectory putFile: local named: gifFileName retry: false. ] on: Error do: [:ex |]. local close. primaryServerDirectory updateProjectInfoFor: self. primaryServerDirectory sleep. "if ftp, close the connection"
とりあえず使い方とソース。クラス名などへぼへぼですが、PNGのチャンクの操作方法のサンプルということで。
プロジェクトファイル(名無し2.001.pr)とそのサムネイル画像(名無し2.gif)からプロジェクトファイルを埋め込んだPNG画像(002.png)生成:
上記PNG画像からプロジェクト読込:
ソースプログラム:
PNGSqeuakObject.st
プロジェクトファイル(名無し2.001.pr)とそのサムネイル画像(名無し2.gif)からプロジェクトファイルを埋め込んだPNG画像(002.png)生成:
| writer image project newName | project := FileStream readOnlyFileNamed: '名無し2.001.pr'. project binary. newName := '002'. image := ImageReadWriter formFromFileNamed: '名無し2.gif'. writer := PNGSqeuakObject on: (FileStream newFileNamed: newName, '.png'). [ writer nextPutImage: image interlace: 0 filter: 0 Object: project contents] ensure: [writer close. project close]
上記PNG画像からプロジェクト読込:
| reader | reader := PNGSqeuakObject on: (FileStream readOnlyFileNamed: '002.png'). reader nextImage
ソースプログラム:
PNGSqeuakObject.st
プロジェクトをファイル保存するとサムネイル画像(gif)とプロジェクトファイルを圧縮したファイルができるが、どうもうまく管理するのが難しい。そこでサムネイル画像側のリソースにプロジェクトファイルを埋め込むことを思いつく。
画像にリソースを埋め込める画像形式としてTIFFやPNGがあるが、TIFFはSqueakで正式サポートされていない。というわけでPNG画像に独自のプライベートチャンクを付加してそこにプロジェクトファイルを埋め込んでみた。プライベートチャンクならば通常の画像表示時には無視される。コーディングはProjectLoadingクラスの#openName:stream:fromDirectory:withProjectView:を参考にPNGReaderWriterクラスを継承したクラスに実装した。このメソッドはかなり込み入っているので、ここ2,3日費やしてしまった。現在の実装は予め保存したプロジェクトファイルとサムネイル画像(gif)からプロジェクトを埋め込んだPNG画像を生成する。PNG画像読込時に該当するプライベートチャンクがあれば、プロジェクト読込処理を行うようにした。
ソースなどはもう少しチェックしてから公開します(といっても、利用する人はいないだろうが)。
画像にリソースを埋め込める画像形式としてTIFFやPNGがあるが、TIFFはSqueakで正式サポートされていない。というわけでPNG画像に独自のプライベートチャンクを付加してそこにプロジェクトファイルを埋め込んでみた。プライベートチャンクならば通常の画像表示時には無視される。コーディングはProjectLoadingクラスの#openName:stream:fromDirectory:withProjectView:を参考にPNGReaderWriterクラスを継承したクラスに実装した。このメソッドはかなり込み入っているので、ここ2,3日費やしてしまった。現在の実装は予め保存したプロジェクトファイルとサムネイル画像(gif)からプロジェクトを埋め込んだPNG画像を生成する。PNG画像読込時に該当するプライベートチャンクがあれば、プロジェクト読込処理を行うようにした。
ソースなどはもう少しチェックしてから公開します(といっても、利用する人はいないだろうが)。
で、http://squeak.hpl.hp.com/svn/squeak/trunk/platforms から最新ソースを取ってきて見たけどやっぱり、sqSocketSendDataBufCountとなっていた。
そこで、初めてVMをbuildしてみた。例の部分をsqSocketReceiveDataBufCountに直して、VMMakerでGenerate Allを実行して、build.batを実行してみる。すると,Squeak.exeとFFTPlugin.dllが出来た。でも配布されているのはSqueakFFIPrims.dllで名前が違う。何か間違えたかも。不安を残しつつ、UDP受信コードを実行したら、動いた(ちゃんと受信できた)!もしこんなのが原因だったら、すぐ直されてるだろうし。もしかしたら、動かしているメソッドがすでに、Obsoleteだったりして。。。もしわかる人がいたら、教えて欲しいな。でそのコードを
そこで、初めてVMをbuildしてみた。例の部分をsqSocketReceiveDataBufCountに直して、VMMakerでGenerate Allを実行して、build.batを実行してみる。すると,Squeak.exeとFFTPlugin.dllが出来た。でも配布されているのはSqueakFFIPrims.dllで名前が違う。何か間違えたかも。不安を残しつつ、UDP受信コードを実行したら、動いた(ちゃんと受信できた)!もしこんなのが原因だったら、すぐ直されてるだろうし。もしかしたら、動かしているメソッドがすでに、Obsoleteだったりして。。。もしわかる人がいたら、教えて欲しいな。でそのコードを
| listen port buff received | buff := ByteArray new: 100. port := 55555. listen := Socket newUDP setPort: port. [ received := listen receiveUDPDataInto: buff. received first = 0] whileTrue. Transcript show: buff. received inspect. listen close
以前、Socketが動かなくなったと書いたが具体的にはUDP受信ができないというもんだった。そこで昨日から本格的に調べてたんだが、どうもうまくいかない。というわけで、3.7.1のSocketPluginのソースを眺める。するとUDP受信でsqSocketSendDataBufCountが呼び出されている。なんで!?
どうみても、sqSocketReceiveDataBufCountのような気がするんだが?
後で、最新ソースを
http://squeak.hpl.hp.com/svn/squeak/trunk/platforms
から引っ張ってみて確認しよう。
int sqSocketReceiveUDPDataBufCountaddressportmoreFlag( SocketPtr s, int buf, int bufSize, int *address, int *port, int *moreFlag) { int nRead; if(UDPSocketType != s->socketType) return interpreterProxy->primitiveFail(); /* bind UDP socket*/ sqSocketConnectToPort(s, *address, *port); if(interpreterProxy->failed()) return 0; /* receive data */ nRead = sqSocketSendDataBufCount(s, buf, bufSize); if(nRead >= 0) { *address= ntohl(ADDRESS(s)->sin_addr.s_addr); *port= ntohs(ADDRESS(s)->sin_port); } return nRead; }
どうみても、sqSocketReceiveDataBufCountのような気がするんだが?
後で、最新ソースを
http://squeak.hpl.hp.com/svn/squeak/trunk/platforms
から引っ張ってみて確認しよう。
なにをいまさらSqueakでXML-RPCサーバかと思われますが(実際多数の実装実績がある)、まぁスキルアップってことで。
まずXML-RPCサーバをインストールします。今までのおさらいの意味を含めてYAXO(XML-Parser)からのインストール方法を下記に示します。
インストールはこれでOKのはずです。
次に下記に示すテスト用のクラスを作成します。これはRPCのAPIを実装するクラスとなるもので、文字列を引数にしてそれを評価した結果を返す、test.evaluateというAPIを用意します。
XMLRPCmyTest.st
詳細は次回で説明します。
HTTPサーバを起動します。
Spy-XML-RPCで先ほどのAPIを実行します.次の内容をprint itします。
どうですか?意外と簡単でしたね。次回は上記の内容を少し詳しく説明します。
まずXML-RPCサーバをインストールします。今までのおさらいの意味を含めてYAXO(XML-Parser)からのインストール方法を下記に示します。
- YAXOをSqueakMapよりインストール
- Spy-XML-RPCを試すを参考にSpy-XML-RPCをインストール
- Comanche/httpserverをインストールしてみるを参考に'Comanche/httpserver'をインストール
- Squeak SWikiよりXMLRPC-Server-mfrit.mczをダウンロードしてファイルリストよりインストール(SqueakMapではインストールに失敗する)
インストールはこれでOKのはずです。
次に下記に示すテスト用のクラスを作成します。これはRPCのAPIを実装するクラスとなるもので、文字列を引数にしてそれを評価した結果を返す、test.evaluateというAPIを用意します。
XMLRPCmyTest.st
詳細は次回で説明します。
HTTPサーバを起動します。
| ma xmlrpc | xmlrpc := XMLRPCHttpModule new. ma := ModuleAssembly core. ma serverRoot: FileDirectory default fullName. ma alias: '/xmlrpc' to: [ma addPlug: [:request | xmlrpc process: request]]. ma documentRoot: FileDirectory default fullName. ma directoryIndex: 'index.html index.htm'. ma serveFiles. (HttpService startOn: 8080 named: 'httpd') plug: ma rootModule
Spy-XML-RPCで先ほどのAPIを実行します.次の内容をprint itします。
| url proxy | url := Url absoluteFromText: 'http://localhost:8080/xmlrpc/'. proxy := XMLRPCProxy withUrl: url. proxy invokeMethod: 'test.evaluate' withArgs: #('2 + 3')
どうですか?意外と簡単でしたね。次回は上記の内容を少し詳しく説明します。
WorkspaceではSmalltalkコードを書いて'do it'できたりするが、どうしてるんでしょうか?またワークスペースで変数を特別な宣言無しに使えますが、どうしてるんでしょうか?
というわけで、ちょっとWorkspaceについて調べてみる。
■'do it'
WorkspaceのPluggableTextMorph経由でCompilerクラスの#evaluate:in:to:notifying:ifFail:logged:でUndefinedObjectクラスに#DoIt,#DoItIn:メソッドを追加して実行しています。
■Workspace変数
次のコードをワークスペースに記入して'do it'します。
すると以後starでこのオブジェクトにアクセスできますが、どうなってるかPointer Finderを使って調べてみます。次のコードでインスペクタを起動します。インスペクタから'chase pointers'でPointer Finderを起動します。おもむろにインスペクタを閉じてPointer Finderで'Search again'します。
すると添付画像のように'bindings: Dictionary'を選択してinspectするとbindingsというWorkspaceのインスタンス変数に束縛されていることがわかります。で、ここを参照して'do it'したりできるわけですね。
というわけで、ちょっとWorkspaceについて調べてみる。
■'do it'
WorkspaceのPluggableTextMorph経由でCompilerクラスの#evaluate:in:to:notifying:ifFail:logged:でUndefinedObjectクラスに#DoIt,#DoItIn:メソッドを追加して実行しています。
■Workspace変数
次のコードをワークスペースに記入して'do it'します。
star := StarMorph new.
すると以後starでこのオブジェクトにアクセスできますが、どうなってるかPointer Finderを使って調べてみます。次のコードでインスペクタを起動します。インスペクタから'chase pointers'でPointer Finderを起動します。おもむろにインスペクタを閉じてPointer Finderで'Search again'します。
star inspect.
すると添付画像のように'bindings: Dictionary'を選択してinspectするとbindingsというWorkspaceのインスタンス変数に束縛されていることがわかります。で、ここを参照して'do it'したりできるわけですね。
N7においてFileStream>>nextPutAll:がSqueak3.7と挙動が異なる。N7ではFileStreamはMultiByteFileStreamが実際の処理を行う、一方Squeak3.7ではStandardFileStreamが実際の処理を行うようになっている。
次の検証コードを実行するとその違いがわかる。このコードをN7で実行すると、できたファイルのサイズが異なる。ダンプをとるとMultiByteFileStreamでは'C2'が挿入されていた。
で、なんでこんな話をするかというとSqueakのHTTPサーバKomHttpServerをインストールしようとすると,次のようなエラーとなる(SqueakDebug.logの内容)。発生場所はKomHttpServer-6.2.sarのプレアンブルである。もちろんSqueak3.7ではエラーは起きない。
エラー発生場所(プレアンブル):
SARInstaller class>>installSARFromUrl:の実装は上記プレアンブルにありファイルインされて実行される。下記コードのtempFileNameで新規作成されるファイルのサイズがSqueak3.7と異なる。デバッガでみると、contentsの値はSqueak3.7/N7ともに12,866である。作成されるファイルサイズはN7が19,029バイト,Squeak3.7は12,866バイトとなる。
この原因を追跡した結果上記の実装の違いによるものとわかった。で実際KomHttpServer-6.2.sarのプレアンブルを次のように(strm binaryを追加)すると正しく上記部分ではエラーが起きなかった.
とりあえずどうしましょうかね。ここはやはり、地道にプレアンブル部を手作業で実行するほかないような…うまい方法教えて頂戴な。
次の検証コードを実行するとその違いがわかる。このコードをN7で実行すると、できたファイルのサイズが異なる。ダンプをとるとMultiByteFileStreamでは'C2'が挿入されていた。
| b strmStd strmMulti | b := ByteArray newFrom: {16r50. 16r4B. 16r03. 16r04. 16r14. 16r00. 16r00. 16r00. 16r08. 16r00. 16r2D. 16r12. 16r37. 16r2F. 16rB1.}. strmStd := StandardFileStream newFileNamed: 'std.bin'. strmMulti := MultiByteFileStream newFileNamed: 'multi.bin'. strmStd nextPutAll: b asString. strmMulti nextPutAll: b asString. strmStd close. strmMulti close.
で、なんでこんな話をするかというとSqueakのHTTPサーバKomHttpServerをインストールしようとすると,次のようなエラーとなる(SqueakDebug.logの内容)。発生場所はKomHttpServer-6.2.sarのプレアンブルである。もちろんSqueak3.7ではエラーは起きない。
ZipArchive(Object)>>error: Receiver: a ZipArchive Arguments and temporary variables: aString: 'bad signature 07000017 at position 18686' Receiver's instance variables: members: an OrderedCollection() centralDirectorySize: 320 centralDirectoryOffsetWRTStartingDiskNumber: 3189955 zipFileComment: a ByteArray() writeCentralDirectoryOffset: 0 writeEOCDOffset: 0
エラー発生場所(プレアンブル):
SARInstaller installSARFromUrl: 'http://people.advantive.com/(省略)/KomPackaging-1.0.sar'
SARInstaller class>>installSARFromUrl:の実装は上記プレアンブルにありファイルインされて実行される。下記コードのtempFileNameで新規作成されるファイルのサイズがSqueak3.7と異なる。デバッガでみると、contentsの値はSqueak3.7/N7ともに12,866である。作成されるファイルサイズはN7が19,029バイト,Squeak3.7は12,866バイトとなる。
!SARInstaller class methodsFor: '*KomPackaging' stamp: 'svp 5/6/2003 01:10'! installSARFromUrl: aUrlOrString | url num tempFileName strm pkgName contents | url := aUrlOrString asUrl. contents := url retrieveContents contents. pkgName := url path last sansPeriodSuffix. num := 1. [FileDirectory default isAFileNamed: (tempFileName := pkgName, '.', num asString, '.temp')] whileTrue: [num := num + 1]. [strm := FileStream newFileNamed: tempFileName. [strm nextPutAll: contents] ensure: [strm close]. self installSAR: (FileDirectory default fullNameFor: tempFileName)] ensure: [FileDirectory default deleteFileNamed: tempFileName] ! !
この原因を追跡した結果上記の実装の違いによるものとわかった。で実際KomHttpServer-6.2.sarのプレアンブルを次のように(strm binaryを追加)すると正しく上記部分ではエラーが起きなかった.
[strm := FileStream newFileNamed: tempFileName. strm binary.
とりあえずどうしましょうかね。ここはやはり、地道にプレアンブル部を手作業で実行するほかないような…うまい方法教えて頂戴な。
YAXOネタでSpy-XML-RPCを試す。本当はYAXOネタはもう打ち止めとしようとしてたんだけど、WEBサービス 郵便専門ネットを外部システムから活用するというサイトで、XML-RPCサービスしていたので、サンプルに使わせてもらいました。Spy-XML-RPCはXML-RPCのクライアントを実装したものです。インストールは下記参照。
ただN7ではXMLRPCProxy>>sendXmlRpcを次のように修正する必要がありました。
で上記サイト(郵便専門ネットをXML-RPCで活用する)を参考に郵便番号から住所を取得してみるコード。
補足:本来はXMLDocument>>encodingで得たエンコードにしたがって、XMLRPCDecoder>>decode:でコード変換すべき。サーバはSqueakMapにXMLRPCが登録されています。
参考リンク:
■XML-RPC Server
次回は梅澤さんのSIXXにチャレンジかなと…
- Spy-XML-RPCをダウンロード・ファイルイン
- YAXOのChangeSetをダウンロード・ファイルイン
ただN7ではXMLRPCProxy>>sendXmlRpcを次のように修正する必要がありました。
"xml _ s getRestOfBuffer: (res at: 3) contents readStream." xml _ (s getRestOfBuffer: (res at: 3)) readStream.
で上記サイト(郵便専門ネットをXML-RPCで活用する)を参考に郵便番号から住所を取得してみるコード。
| url proxy result | url := Url absoluteFromText: 'http://yubin.senmon.net:80/service/xmlrpc/'. proxy := XMLRPCProxy withUrl: url. result := proxy invokeMethod: 'yubin.fetchAddressByPostcode' withArgs: #('1160013'). result do: [ :dic | dic keysDo: [ :key | (dic at: key) class == String ifTrue: [ | t | t := (dic at: key) convertFromWithConverter: (TextConverter newForEncoding: 'utf-8'). dic at: key put: t.]]]. result --> #(a Dictionary('addr_name'->'' 'addr_name_kana'->'' 'city'->'荒川区' 'city_kana'->'アラカワク' 'data_type'->'p' 'jiscode'->'13118' 'other'->'' 'postcode'->'1160013' 'pref'->'東京都' 'pref_kana'->'トウキョウト' 'town'->'西日暮里' 'town_kana'->'ニシニッポリ' 'yid'->38111 ))
補足:本来はXMLDocument>>encodingで得たエンコードにしたがって、XMLRPCDecoder>>decode:でコード変換すべき。サーバはSqueakMapにXMLRPCが登録されています。
参考リンク:
■XML-RPC Server
次回は梅澤さんのSIXXにチャレンジかなと…
Nihongo7リリースおめでとうございます。昨日は飲んで爆睡でした。
日本語版リリース記念と言うわけではないですが、漢字コード変換example。
日本語版リリース記念と言うわけではないですが、漢字コード変換example。
"Shift-JIS" | fs | fs := FileStream readOnlyFileNamed: 'SJIS.txt'. fs converter: (TextConverter newForEncoding: 'shift-jis'). "from #encodingNames" (StringHolder new contents: fs contents) openLabel: 'text'. fs close. "UTF-8" | fs | fs := FileStream readOnlyFileNamed: 'UTF-8.txt'. fs converter: (TextConverter newForEncoding: 'utf-8'). "from #encodingNames" (StringHolder new contents: fs contents) openLabel: 'text'. fs close. "EUC" | fs | fs := FileStream readOnlyFileNamed: 'EUC.txt'. fs converter: (TextConverter newForEncoding: 'euc-jp'). "from #encodingNames" (StringHolder new contents: fs contents) openLabel: 'text'. fs close.