2006年 03月 06日

ruby-xslt の parameters=

parameters= に渡す値はすべて自分でクオートしとこう。

libxslt はもともとパラメータの値に XPath そのまんま書けるんだけど、なぜか ruby-xslt の parameters= ではクオートして必ず文字列で渡す。しかしながら、なんかこのクオートの実装が狂ってて、自分でクオート付け足してる癖に Invalid expression とか言い出してくれちゃう。やれやれだぜ!

正確に言うとクオートを付与する段階で、与えた文字列を中途半端に破壊的に変更するらしく、クオーテーションがかたっぽだけ付く。セッションとかで保存させながらのコードでもうハマったハマった。dup してどうにかしたけど、自分でクオートして明示的に文字列にしたほうがよさげ。

ソースは読んでないので間違っているかも。あくまで挙動からの推測

2006年 01月 19日

テンプレートに XSLT スタイルシートを使う利点

俺はもうテンプレートが XSLT じゃない日記システムないし CMS ツールは使えない人間になってしまったのだけど、なんで XSLT を使うかを考えてみた。

  1. アプリケーション間で使いまわししやすい。(仕様化されているので)
  2. ホスト言語をあまり制限しない。(仕様化され、ライブラリが存在するので)
  3. not well-formed にはまずならない。(XML プロセサが処理をするので)
  4. インデントがまとも。(XML プロセサが処理をするので)
  5. (タグなどに関しては) sanitize を言わなくてすむ。 (XML プロセサが処理をするので)
  6. やろうと思えば XPath 関数を増やせるので、拡張性が高い。
  7. パズルちっくで (書いていて) 楽しい。(裏を返せば読みにくいのだけど)

XPath の話も混ざってますけど、どうせ一緒に使うからいいよね。

メリットを書くならデメリットも書くべきだけど、そんなに思いつかない。

  1. とにかく元が XML じゃないと話にならない。
  2. ちょっと難しいことやると難読になる。
  3. XPath 1.0 が貧弱。
  4. 最初は template がどんな意味を持つのか理解できない。XPath が地味に難しい。
  5. 難読まで行かなくとも、読むのが面倒くさい。(上から順番に実行されるわけではないから)

思いつかないとかいいつつ結構あるね。

一番重要なのは、アプリケーション間で使いまわしやすいことだと思う。共通のテンプレートを作っておけば、それを include したりして、それぞれ別のシステムから利用できる。このサイトはヘッダとかフッタとかナヴィゲーションとかは XSLT の1ファイルにまとめて書いてある。だから CSS のスタイルを作っても適用するのは全く面倒くさくない。ようは別々になってるとめんどいのよ。面倒くささ解消のために標準化標準化

2006年 01月 03日

XPath1.0 いろいろ一覧

  • child 子
  • parent (..) 親
  • attribute (@) 属性
  • descendant-or-self (//)
  • self (.)
  • descendant 子孫
  • ancestor 祖先
  • following-sibling 後兄弟
  • preceding-sibling 前兄弟
  • following 後
  • preceding 前
  • namespace
  • ancestor-or-self
  • comment()
  • text()
  • processing-instruction([target])
  • node()
.
self::node()
..
parent::node()
//
descendant-or-self::node()
@
attribute::

@ 以外は軸とノードテストを合わせた省略形

http://lowreal.net/logs/2006/01/03/1#content

2005年 12月 21日

このサイトの構築構造

全部 XML から XSLT プロセッサを通して XHTML やらなにやらを生成してる。日記部分は動的に、/about とかは静的に、それぞれ別のスクリプトが変換を担当している。日記は taglibro と呼ばれるスクリプト。静的リソースは名前のない build.rb というスクリプトが変換してる。build.rb は Makefile の類でもよさそうだけど、Ruby で書いておいたほうがなんでもできるのでいいや、みたいな。

common.xsl
サイト共通のテンプレートが書いてある。例えば head 要素内やつ。CSS とか、スクリプトを加えたいときはコレだけ編集してサイト全体を再変換させている。XHTML 用。
2html.xsl
静的リソースを変換するためのファイル。ソース XML はちょっとキモイんだけど、実用上問題ないのでいいや、みたいなヤッツケ的スタイルシート。だいぶ前に書いた。
logs/common.xsl
taglibro (日記) 用の共通部分。各種変数 (Content-type 非依存) なものがまとめて書いてある。flavor.(\w+).xsl から include されてる。
logs/flavor.xhtml.xsl
taglibro の XHTML 用テンプレート。

本当なら、taglibro は Accept ヘッダを解析してそれにあったやつを返すべきなんだけどめんどっちくて実装してない。これを実装すると IE に application/xhtml+xml を送らないようにする処理がなくせていいんだけど……(text/html を受け付けるなら flavor.html.xsl を読み込むようにする、flavor.html.xsl は flavor.xhtml.xsl なり、共通部分を分離した他の .xsl を include してやる、みたいな。)

2005年 11月 08日

ruby-xslt 0.9.1

ちゃんとエラークラス吐くバージョンがリリース。kiyoya さんのパッチ。

ruby extconf.rb --enable-error-handler して Makefile 作成。ちゃんと exslt もリンクさせるため、OBJS に /lib/libxml2.dll.a /lib/libexslt.dll.a /lib/libxslt.dll.a を追加。なんか知らんけど明示的に指定しないとリンクされなくて ld が怒る@cygwin (このバージョンに限らず)

XML::XSLT::XSLTError と、そのサブクラス XML::XSLT::ParsingError, XML::XSLT::TransformationError が追加される。変換用 XML ファイルのエラーなのか、XSLT ファイルのエラーなのかはわからなく。そのかわりエラー発生のタイミングが変わった。serve 時ではなく、xml= のときに出るようになった。だから問題ない。

エラーの message の意味がないっぽいので、--enable-error-handler 時には rb_raise 第2引数を STR2CSTR(rb_ary_to_s(rb_cvar_get(cXSLT, rb_intern("@@errors")))) とかにしたほうがいい感じ。--enable-error-handler は errors が定義されるのがキモイけど俺はそんなに気にならない。

第二引数を変えるだけだと以下のコードがダメ。

xslt = XML::XSLT.new()
xslt2 = XML::XSLT.new()
begin
xslt.xml = File.read("examples/fuzface.xml")
xslt.xsl = File.read("examples/fuzface.xsl")
out = xslt.serve()
print out;
rescue XML::XSLT::XSLTError => e
p XML::XSLT.class_eval("@@errors") #=> (1)
p e.message #=> (2)
end
p XML::XSLT.class_eval("@@errors") #=> (3)
begin
xslt2.xml = File.read("examples/fuzface.xml")
xslt2.xsl = File.read("examples/fuzface.xsl")
out = xslt2.serve()
print out;
rescue XML::XSLT::XSLTError => e
p e.message  #=> (4)
end

(3) で (2) のときと同じ (4) のとき前のエラーメッセージが紛れ込む。クラス変数使ってるからだろうけど、単純にインスタンス変数にしないのにはなんか理由があるんだろうからとりあえずそのままにして、

void xslt_raise(VALUE cError) {
#ifdef USE_ERROR_HANDLER
VALUE errors = rb_cvar_get(cXSLT, rb_intern("@@errors"));
VALUE error_str = rb_ary_to_s(errors);
rb_ary_clear(errors);
rb_raise(cError, STR2CSTR(error_str));
return;
#endif
rb_raise(cError, "");
}

とか作って経由させ、リセットさせてみる。XML::XSLT.errors は常に空の配列が返るようになって無意味になる。

ruby-xslt.0.9.1.error_message.patch

スレッドの切り替えってどういうタイミングなんだろう。

2005年 10月 23日

ruby-xslt 0.8.2

新しくなっているのに気付いた。 ruby extconf.rb --enable-error-handler (off by default) とか書いてあるのでちょっと期待しつつ。

とりあえず cygwin で make できんので Makefile 修正。OBJS に /lib/libxml2.dll.a /lib/libxslt.dll.a を追加して強制リンキング。exslt ははずす。

いろいろやってみたんだけど、よくわからん。USE_ERROR_HANDLERXML::XSLT.errors が定義されるんだけどエラー起こしても特に何も入ってない。/* this is really quite inelegant */ とか書いてあって苦労してんなぁとかなんとか。

とりあえずさらに really quite inelegant なコードで解決を図る。Ruby 側で…

require '../xslt'
xslt = XML::XSLT.new
xslt.xmlfile = "fuzface.xml"
xslt.xslfile = "fuzface.xsl"
begin
err = STDERR.dup
pipe = IO.pipe
STDERR.reopen(pipe[1])
out = xslt.serve # raise RuntimeError
rescue RuntimeError => e
p XML::XSLT.errors #=> []
if e.message =~ /^(XSL|XML|Stylesheet) /
p e
p pipe[0].readpartial(4096) #=> エラーメッセージ
else
raise
end
ensure
STDERR.reopen(err)
end
__END__
起こりうるエラーたち (grep)
parser.c:53:      rb_raise( rb_eRuntimeError, "XSL parsing error" );
parser.c:59:      rb_raise( rb_eRuntimeError, "XSL Stylesheet parsing error" );
parser.c:65:      rb_raise( rb_eRuntimeError, "XSL Stylesheet parsing error" );
parser.c:84:      rb_raise( rb_eRuntimeError, "XML parsing error" );
parser.c:90:      rb_raise( rb_eRuntimeError, "XML parsing error" );
parser.c:97:      rb_raise( rb_eRuntimeError, "Stylesheet transformation error" );

tests/fuzface.rb の改造。xmlfile= は使うなという警告がでるけどとりあえず放置。$stderr じゃなくて STDERR にメッセージを吐くらしくめんどっちいことをやってる。readpartial は cygwin の ruby が 1.8.3 になったから使ってみた。便利よねコレ

ごちゃごちゃやってみたけど使わない。XREA に入れるのめんどいし……

これでドウダ

class XML::XSLT
class XSLTError < StandardError; end
class XSLParsingError < XSLTError; end
class XSLStylesheetParsingError < XSLTError; end
class XMLParsingError < XSLTError; end
class StylesheetTransformationError < XSLTError; end
alias __org__serve serve
def serve
ret = nil
err = STDERR.dup
pipe = IO.pipe
Thread.critical = true
STDERR.reopen(pipe[1])
begin
ret = __org__serve
rescue RuntimeError => e
STDERR.reopen(err)
Thread.critical = false
message = pipe[0].sysread(4096)
error_class = XSLTError
case e.message
when "XSL parsing error"
error_class = XSLParsingError
when "XSL Stylesheet parsing error"
error_class = XSLStylesheetParsingError
when "XML parsing error"
error_class = XMLParsingError
when "Stylesheet transformation error"
error_class = StylesheetTransformationError
end
raise error_class.new(message)
ensure
STDERR.reopen(err)
Thread.critical = false
end
ret
end
end
xslt = XML::XSLT.new
xslt.xmlfile = "fuzface.xml"
xslt.xslfile = "fuzface.xsl"
begin
xslt.serve
rescue XML::XSLT::XSLTError => e
p e
end

将来完璧な実装になっても消すだけ! これなら使えそう? ごめん嘘。エラークラス云々があるのでそのままじゃ無理。

ruby-xslt on XREA

make がうまくいったっぽいのでメモ。ちなみに数度要望の末 ruby-xslt を入れてもらってある (この過程で別の鯖と違う点があるかもしれない)。でもやっぱり自分でビルドできないかなぁという試行。

#!/bin/sh
PATH="/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:$PATH"
export PATH
ruby extconf.rb #1
#make #2
# test
#cd tests
#ruby fuzface.rb

みたいなのを make.sh として作って保存しとく。ちなみに AddHandler cgi-script-debug .sh してある。念のため一発でやらずに一行ずつ実行させる。

ruby extconf.rb は Permission Denied がでつつも一応通るので問題なしということにしとく。んで、make なんだけど libxml/* が見つからないとかいわれる。しかたないのでローカルの /usr/include/libxml2/libxml を同じディレクトリに転送する。エラー一切なく make が通った。テストもしてみたけど大丈夫っぽい。微妙だけど。

関係ないけど XREA に拡張ライブラリ置くとき怪しいことをすることにした。/virtual/username/lib/ruby/RUBY_PLATFORM を掘ってその中に入れる。ローカルは cygwin なので /virtual/username/lib/ruby/i386-cygwin/xml/xslt.so、XREA は /virtual/username/lib/ruby/i686-linux/xml/xslt.so。んで使う場合は $LOAD_PATH.unshift("/virtual/lowreal/lib/ruby", "/virtual/lowreal/lib/ruby/#{RUBY_PLATFORM}") しとく。もともと XREA 上の RSS パーサが古いのでアレしてコレした感じ。

s63.xrea.com 上で作った xslt.so。ちゃんと動くかは知らない。

ちなみに上のセクションのスクリプトは動かないです。1.8.2 なので。readpartial を sysread にすればとりあえず動きます。どっちでもよさげだから sysread にしとくのが吉か。

2005年 07月 10日

XML Master ツレ受験オフ

いくつかの感想はおそらく今書くと危険 (謎) なので、簡単なところを書いておけば、受けた人は全員合格していたし、みんなやる気があるのかないのか謎なてけとー状態だった。バナナのおかげよ?

試験問題は同じ日付・会場でも別の問題がでるようだった。出題頻度がかなり高いのは XML Schema。次に XSLTDTD と続く感じ? 終わるまで今何問中何問目をやっているかがどこに表示されているかわからなくてだいぶ時間を気にした……けど普通にやると2回全問やるぐらいの時間がある。

最終的に俺がいいてぇのは試験料がたけぇってことだよ!

XML Master についてもう少し書く。

複数選択可能な問題はチェックボックス、一つ選ぶ問題はラヂオボタンになっているのでぼーっとしてても選択し忘れは少ないと思う。ただ回答の部分が全てクリック対象になっちょる (<label><input type="checkbox" />DTD なんていらない</label> みたいに) のがちょっとやりにくい。「次へ」をクリックしようとして D を選択してしまうとかが怖い。

あと「資料」は常に手前表示にして欲しい……いちいち開くのがだるかった。しかも問題がホイールでスクロールできないし。使いにくい。

XPath の問題で、かなりカスな問題があった。5つの選択肢のうち、問題文の条件にあった3つを選べとかいうのだったけど、その5つのうち2つはそもそも XPath としてパースエラーなため「問題文の条件」の意味がない。ある意味ひっかけ?

2005年 07月 03日

リハビリと ruby-xslt

ruby-xslt 使うと XSLT から Ruby のメソッドを呼べるみたいなので、とても使ってみたい。(何をするか、はともかく)

ただ、ちょっと実装が残念な形。

class XML::XSLT
def round_trip( arg )
arg
end
def type( arg )
arg.class.to_s
end
end
xslt = XML::XSLT.new()
xslt.xsl = "functions.xsl"
xslt.xml = "test.xml"
xslt.extFunction("roundTrip", "http://test.none", xslt, "round_trip")
xslt.extFunction("type", "http://test.none", xslt, "type")

俺としては Proc オブジェクトとか、ブロックを直接渡したい。すなわち次のように

xslt.extFunction("roundTrip", "http://test.none/", xslt) do |arg|
arg
end
#または
round_trip = lambda {|arg| arg}
xslt.extFunction("roundTrip", "http://test.none", xslt, round_trip)
# あるいはまとめたクラス / インスタンスを登録する形。
# もとのヤツに似ているけど名前が衝突しない。
class XSLTFunctions
def round_trip(arg)
arg
end
def type(arg)
arg.class.to_s
end
end
xslt.set_ext_functions("http://test.none", XSLTFunctions) # XSLTFunctions.new
# この場合 XSLT から呼ぶ関数名は Ruby でのメソッド名と同じになる。とか。
# やはり Ruby は書いていて気持ちいい。

ちょっとどうでもいいけど、なんかこの PC 描画がおかしい。温度のせい?

再起動したらなおった? ちょっと時間おかないと分からない。

限りなく不安定な PC だ。グラフィックボードにもファンがついているから、グラフィックボードが暑くて困っているのかもしれない。しかしゲームのとき気にならない (or 症状が出ていない?) のはなぜだろう。最も影響うけやすそうなんだけど。

ruby-xslt

configure make make configure -opt make make とかウザい。

それはともかく、拡張できないよ。/..snip../ruby-xslt/extfunc.c:57: undefined reference to `_xml*' が永遠出る。ライブラリは指定されているみたいなんだけど……意味がわからない。cygwin ruby-xslt とかでぐぐってもこのサイトがでてきてウザい。

なんで環境によってライブラリ・ヘッダーファイルのパスが違うとかいうバカげた状況なんだろう。configure とかうざい。うざい。

あれだよね、拡張ライブラリは作る時点で八割りがたウマくいかないし、Pure Ruby なライブラリは使うときにウマくいかない。スーパーハカーなら自分で作るんだろうなぁ。