バックナンバー
著者: speakillof <speakillof at yahoo dot co dot jp>
皆さん RDoc を使っていますか? ご存知の方も多いでしょうが、RDoc というのは Ruby スクリプトのコメントに書かれた文章からリファレンスマニュアルを生成するツールです。RDoc は Ruby に標準添付されているので、リファレンスマニュアルを作るために多くの人が利用しています。でも、生成される HTML にはフレームがたくさん付いていて、見にくいと感じる人が多いのではないでしょうか?
RDoc のフレームがうっとおしいという意見
私も見やすいリファレンスマニュアルが欲しいと思う一人です。そこで、この記事では CGIKit と RDoc (というか ri ) を使ってオリジナルのリファレンスマニュアルを作成します。CGIKit の紹介をかねているので、この記事で作るサンプルは解説をしやすくするためシンプルなものにします。
インストールの方法については 前回の記事 や CGIKit の Wiki を参照してください。
それは面倒なので、今回は ri のデータを利用することにします。
ri は refe と同じくコマンドラインからリファレンスマニュアルを検索するツールです。ri は RDoc の一部であり、ri のデータは RDoc を使って生成されます。ri について詳しく知りたい場合は http://www.ruby-lang.org/ja/man/?cmd=view;name=ri を参照してください。
通常、ruby のインストールが終わると、一緒に ri がインストールされます。ri のある場所は ruby と同じディレクトリ(フォルダ)です。何も設定せずに Ruby One Click Installer をインストールした場合は
に ri があります。Unix 系 OS をお使いの人は
として、どこに ri がインストールされているか確かめてみてください。
ri がインストールされていても、ri のデータが無いことがあります。
に ri のデータがあるか確かめてみましょう(他のディレクトリにインストールされていることもあります)。
環境設定で変化しますが、Windows で Ruby One Click Installer を使っている人は上のそれぞれのディレクトリが
になります。
もし、 ri のデータがない場合は Ruby のソースを取ってきて
として下さい($はユーザーを、#はrootを表します)。コンパイルする環境があればこれで /hogehoge/share/ 以下に ri のデータがインストールされますので、上記のディレクトリにコピーしてください。
コンパイルする環境が無い人は
とすることで ri 用のリファレンスマニュアルのデータが作られます。例えば、CGIKit なら下のようにします。
これで自分のホームディレクトリの下に CGIKit の ri のデータがインストールされます。
ri のデータがインストールされているか試してみましょう。
今回作成するリファレンスマニュアルには ri のデータを使うわけですが、そのためには ri の データにアクセスする必要があります。RDoc には ri のデータにアクセスするためのクラス( rdoc/ri/ri_reader.rb の RI::RiReader )が用意されており、この記事ではそのクラスをラッピングしたライブラリを使用します。
ファイルの名前は ri2ref_utils.rb です。CGIKit と関係がないので、実装についてはすべて省略し、各メソッドについては後ほど紹介します。ri2ref_utils.rb
今回はダイレクトアクションという機能を使います。ダイレクトアクションには下のような利点があります。
最後の利点は 前回の記事 で言われた文句のうち
を解決することになります(前回のアルバム CGI で見られた URL の変な文字列というのは session ID、 context ID と呼ばれるものです)。
ダイレクトアクションを使うことで CGIKit の幾つかの機能が使いにくくなりますが、今回の記事の範囲ならダイレクトアクションを使っても困ることはありません。
簡単に言うと、アクセスされた URL と Ruby のメソッドを結びつけることを言います。例えば、
が CGIKit の CGI スクリプトとします。この時に
にアクセスすると、Hoge というクラスの foo_action というメソッドが実行されます。URL の /d/ というのはダイレクトアクションを使うという目印です。URL の /d/ の次の部分の Hoge はクラス名、その次の foo が呼び出されるメソッド名を示します。この URL に対して Hoge#foo_action メソッドが対応することから分かるようにaction という接尾語が付いていないメソッドは呼び出すことができません。ダイレクトアクションの詳細については CGIKit の Wiki に説明がありますので、そちらに任せます。
ダイレクトアクションを使った簡単な例を紹介します。
RI2Reference/hoge/hoge.rb
require 'cgikit'
require 'webrick'
require 'cgikit/webrick'
class HogePage < CGIKit::Component
attr_accessor :title
def init
@title = 'Hoge'
end
end
class FooPage < CGIKit::Component
attr_accessor :body
def init
@body = 'FOO'
end
end
class HogeAction < CGIKit::DirectAction
def hoge_action
a = page(HogePage)
a.title = a.title + ' ' + Time.now.strftime('%Y%m%d')
a
end
def foo_action
a = page(FooPage)
a.body = a.body + ' ' + rand(10).to_s
a
end
alias default_action hoge_action
end
if $0 == __FILE__
port = (ARGV.shift || 8080).to_i
app = CGIKit::Application.new
app.direct_action_class = HogeAction
server = WEBrick::HTTPServer.new({:Port => port})
server.mount('/', WEBrick::CGIKitServlet::ApplicationHandler, app)
trap("INT"){ server.shutdown }
server.start
end
各ページの HTML や ckd ファイルは省略します。この例では hoge.rb に HogePage、 FooPage というページと HogeAction というダイレクトアクションが定義されています。WEBrick用に作ってあるので、試しに RI2Reference/hoge に移動して
で実行して http://localhost:8080/ にアクセスしてみてください。
…エラーになりましたね? CGIKit::Application にダイレクトアクションの設定をしていない場合、/d/を付けないと CGIKit はダイレクトアクションを使用しません。そのため http://localhost:8080/ にアクセスすると、 CGIKit::Application#main に設定されたページを表示させようとします(このサンプルの場合はMainPage)。残念ながらこのサンプルには MainPage が定義されていませんので、エラーメッセージが表示されます。
次に http://localhost:8080/d/HogeAction/hoge にアクセスしてみましょう。今度は HogePage が表示されますね。 /d/ の付いた URL にアクセスがあると、CGIKit が該当するメソッドを判定してそのメソッドを呼び出します。この場合は HogeAction#hoge_action ですね。
HogeAction#hoge_action では CGIKit::DirectAcion#page を使って HogePage というページを生成します。このオブジェクトが HogeAction#hoge_action の返値になるので、HogePage が表示されます。ダイレクトアクションの返値は CGIKit によって表示されます。
今度は http://localhost:8080/d/HogeAction/foo にアクセスしてみましょう。予想したとおりになったでしょうか? この場合は HogeAction#foo_action が実行されて FooPage が表示されます。
では、 http://localhost:8080/d/HogeAction にアクセスしてみてください。メソッド名が指定されていないので、エラーになると考えた人が多いのではないでしょうか? メソッドの指定がない場合や該当するメソッドがない場合、CGIKit は default_action を呼びます。このサンプルでは HogeAction#default_action は HogeAction#hoge_action のエイリアスですので、 HogePage が表示されます。
最後は http://localhost:8080/d/foo にアクセスして下さい。FooPage が表示されますね。 CGIKit::Application#direct_action_class にダイレクトアクションのクラスを設定すると、CGIKit はクラス名の指定がない時に CGIKit::Application#direct_action_class に指定されたクラスを使います。
うーん。今のところ CGIKit で URL に Session ID、 Context ID を入れない方法はこの方法しかありません。現時点では諦めてください…。
主なページは 4 つあります。それぞれの名前は
です。ClassListPage は ri にあるクラス・モジュールの一覧を表示し、 ClassPage はクラス・モジュールの情報を、MethodPage はメソッドの情報を表示させます。また、URL から検索可能にするため SearchResult に検索結果を表示させます。
大雑把に下のようになります。
components 以下のファイルを省いてありますので、完全な構成を知りたい場合はサンプルをダウンロードしてください。ri2ref_sample.zip
前回紹介した ckproject を使用すると、少し楽ができます。cgikit-2.x.x に CGIKit のソースがあると仮定すると、
で、上とよく似た構成のファイル/ディレクトリが出来上がります。 足りないものは components 以下のファイルと lib/ri2ref_utils.rb、 www ディレクトリです。余分なものは lib/session.rb、 resources ディレクトリです。 余分なファイル/ディレクトリは削除しても構いません。
簡単にそれぞれのファイル/ディレクトリの紹介をします。
lib/application.rb | RI2Reference::Application が定義されています。このクラスは CGIKit::Application を継承していますが、特にいじる所はありません。今回は CGIKit::Application の代わりに RI2Reference::Application を使います。 |
lib/directaction.rb | RI2Reference::DirectAction があります。これは CGIKit::DirectAction の子クラスで、今回のダイレクトアクションのためのクラスです。これから作るページはこのクラスから呼ばれます。 |
lib/ri2ref_utils.rb | ri のデータを扱うための RI2Reference::Utils が定義されています。 |
components ディレクトリ | ClassListPage、 ClassPage、 MethodPage、 SearchResult などのファイルがあります。 |
www/ref.css | 今回使用する CSS ファイルです。 |
cgikitconf.rb | CGIKit の設定ファイルです。 |
RI2Reference.rb、 RI2Reference.cgi、 RI2Reference.rbx | 起動用ファイルです。それぞれ WEBrick、 CGI、 mod_ruby 用です。今回は WEBrick しか使いません。ちなみに mod_ruby は CVS の CGIKit でしか使えませんので、注意して下さい。 |
今回も WEBrick で開発を行います(CGI 用のスクリプトは省略します。前回の内容を踏まえて御自分でいじってみて下さい)。それでは ckproject によって生成された RI2Reference.rb を変更しましょう。
#!/usr/local/bin/ruby
# RI2Reference.rb [port]
$LOAD_PATH.unshift('lib')
require 'webrick'
require 'cgikit-all'
require 'cgikit/webrick'
require 'application'
require 'directaction'
port = (ARGV.shift || 8080).to_i
app = RI2Reference::Application.new
app.load_all_components('./components')
app.load_configuration('./cgikitconf.rb')
app.direct_action_class = RI2Reference::DirectAction
app.default_request_handler = app.direct_action_request_handler
server = WEBrick::HTTPServer.new({:Port => port})
server.mount('/', WEBrick::CGIKitServlet::ApplicationHandler, app)
server.mount('/www', WEBrick::HTTPServlet::FileHandler, './www')
trap("INT"){ server.shutdown }
server.start
最初に 18・19行目の
を追加します。これはダイレクトアクションの設定です。1つ目は今回使用するダイレクトアクションがどのクラスかということを CGIKit::Application に伝えます。二つ目は…おまじないと思っていて下さい。
もう一つ追加する部分は23行目です。
これは css ファイルを読み込むための設定です。変更しなくても見栄えは悪いですが、動きます。それと、ファイルの先頭にある
を削っておきましょう。
修正する部分はこれでおしまいですが、見なれない部分があるので、補足しておきます。
15行目の CGIKit::Application#load_all_components は 指定されたディレクトリの .rb をすべて require します。これでいちいち require しなくても良くなりますが、.rb と名前が付けば何でもかんでも require するので、注意が必要です。16行目の CGIKit::Application#load_configuration は設定ファイルの読み込みです。
この修正をした RI2Reference.rb を実行すると、 http://localhost:8080/ に WEBrick のサーバーが起動します。/www 以下に アクセスがある場合はその URL に該当するファイルが www ディレクトリから探索されます。 /www 以外の URL にアクセスすると、RI2Reference::DirectAction によって 生成されたページが表示されます。
cgikitconf.rb の最初に
の一行を加えます。これでどこからでも RI2Reference::Utils を使うことができます。それと @main への RI2Reference::MainPage を代入する部分をコメントにします。今回は CGIKit::Application#main を使いませんし、 RI2Reference::MainPage がないので、この行無効にしないとエラーになってしまいます。
他にも色々設定することが出来ますが、今回は説明しません。
上で HogeAction の例を見せましたが、同じように CGIKit::DirectAction を継承したクラスを書いてそこにメソッドを定義します。今回定義するダイレクトアクションのクラスは RI2Reference::DirectAction です。そこに 4 つのメソッドを定義します。クラスを置く場所は lib/direcaction.rb です。
require 'ri2ref_utils'
module RI2Reference
class DirectAction < CGIKit::DirectAction
def list_action
pg = page(ClassListPage)
pg.list = RI2Reference::Utils.get_all_class_names
pg
end
def class_action
pg = page(ClassPage)
full_name = CGIKit::Utilities.unescape_url( request.form_value('n') )
pg.setup( RI2Reference::Utils.get_class_description(full_name) )
pg
end
def method_action
pg = page(MethodPage)
full_name = CGIKit::Utilities.unescape_url( request.form_value('n') )
pg.setup( RI2Reference::Utils.get_method_description(full_name) )
pg
end
# gorgeous implementation
def search_action
keys = request.form_values.keys
if keys.empty?
list_action
else
keyword = CGIKit::Utilities.unescape_url( keys[0].to_s )
results = RI2Reference::Utils.search_name(keyword)
all_classes = RI2Reference::Utils.get_all_class_names
methods = results - all_classes
classes = results & all_classes
pg = page(SearchResult)
pg.method_list = methods
pg.class_list = classes
pg
end
end
alias default_action list_action
end
end
ダイレクトアクションによって呼び出されるのはページではなくメソッドです。紹介した4つのページはすべてダイレクトアクションを通じて CGIKit によって表示されます。URL、 メソッド、 表示されるページの関係は下のようになります。
URL | Method | ページ |
http://localhost:8080/d/list | list_action | ClassListPage |
http://localhost:8080/d/class | class_action | ClassPage |
http://localhost:8080/d/method | method_action | MethodPage |
http://localhost:8080/d/search | search_action | SearchResult |
各ページの遷移先は下のようになりますが、各ページの内容を見ないと下のように遷移するという事は分からないと思います。
では、それぞれのメソッドについて見てみましょう。
ダイレクトアクションのメソッドの指定がない場合や無効なメソッドが指定された場合に実行されるメソッドです。 CGIKit::DirectAction#page (実装は前回使った CGIKit::Component#page と同じ) を呼び出して ClassListPage を生成し、それに表示したいクラス・モジュールの一覧を ClassListPage#list に設定します。後は CGIKit が ClassListPage を表示してくれます。
表示したいクラス・モジュールの一覧は RI2Reference::Utils.get_all_class_names で取得します。実行例は下のようになります。
今回は http://localhost:8080/d/class?n=Array のように GET を用いてクエリーを渡し、どのメソッド・クラス・モジュールを表示したいのかを決定します。そのためプログラム側でクエリーに指定されたメソッド・クラス・モジュール名を取得する必要があります。前回のようにダイレクトアクションを使わない場合は、この部分を CGIKit が行ってくれるのですが、今回は自分でやらなければなりません。
GET/POST の値は CGIKit::Request に格納されており、CGIKit::DirectAction (CGIKit::Component) からは request メソッドを使って CGIKit::Request のオブジェクトにアクセスすることが出来ます。取得した CGIKit::Reuqest オブジェクトに対して CGIKit::Reuqest#form_value を使うことで GET で指定された値にアクセスすることが出来ます。
例えば、http://localhost:8080/d/class?n=Array に対して request.form_value(‘n’) を実行すると、’Array’ という文字列を得ることが出来ます。この値は URL エンコードされていますので、CGIKit::Utilities.unescape_url を使って元の文字列に戻します。
次に、得られた名前に対応する ri のデータを取得します。この役目は RI2Reference::Utils.get_{class、method}_entry が担当します。取得した ri のデータは ClassPage、 MethodPage のオブジェクトの setup というメソッドで設定します。
ClassPage#setup、 MethodPage#setup については後ほど説明します。RI2Reference::Utils.get_{class、 method}_entry についてもそこで紹介します。
URL の「?」の後ろの文字列、つまり、GETによるクエリーのデータに対して検索を行います。例えば、 http://localhost:8080/d/search?push にアクセスされた時は「push」に対して検索を行います。
すでに述べたように CGIKit::Request#form_value でクエリーのデータにアクセスすることができますが、この方法ではクエリーデータのキーにはアクセスできません (上の例では push がクエリーデータのキーです)。
そこで、クエリーデータの元に近い形のオブジェクトを使うことにします。 CGIKit::Request#form_values にはクエリーデータが Hash のデータとして格納されているので、32行目で
としてクエリーデータのキーのすべてを取得します。 もし、検索ワードが指定されていれば keys は空の配列になりません。 次に36行目で
として「push」に相当する文字列を取得します(多分 to_s はなくても大丈夫です)。37-41行でその文字列に一致するメソッド名の配列を methods に、 クラス・モジュール名の配列を classes に格納しています。
search_action で使われている RI2Reference::Utils.search_name(str) は str を含む メソッド・クラス・モジュール名を検索し、その結果を配列として返します。
また、 RI2Reference::Utils.get_all_names は ri にある メソッド・クラス・モジュールの名前一覧です。
この後の処理は CGIKit とは直接関係がないので、ふーんと思っていて下さい。ちなみにこの実装は富豪的です。検索文字列に一致する項目が見つけたら、 SearchResult にその結果を設定します。
34行目は検索する文字列が指定されていない場合の処理です。この時は ClassListPage を表示させます。list_action の返す値は ClassListPage なので、そのまま list_action を実行するだけです。
そうですね。じゃあ、最初は ClassListPage にしましょうか。
module RI2Reference
class ClassListPage < CGIKit::Component
attr_accessor :i, :list
def query
{'n' => CGIKit::Utilities.escape_url(@i) }
end
end
end
{
:rep => {
:element => Repetition,
:list => :list,
:item => :i,
},
:class_link => {
:element => Link,
:string => :i,
:action_class => RI2Reference::DirectAction,
:direct_action => 'class',
:query => :query,
:session_id => false
},
}
このページは RI2Reference::DirectAction#list_action によって表示されるページです。ここでは ri から取得できるクラス・モジュールの一覧を表示します。ユーザーはこのページを基点として各マニュアルを参照します。
ri で取得できるすべてのクラス・モジュールの名前は RI2Reference::Utils.get_all_class_names で取得できます。すでにそのリストが @list に格納されていますので、これを使って各クラス・モジュールのページへのリンクを張ります。@list のクラス・モジュール名を CGIKit::Repetition の item 属性で ClassListPage の @i に格納し、リンクを繰り返し表示させます。
今回はHTMLに目印を付けるために ckid 属性を用いています。繰り返されるのは下の HTML の <li> … </li> です。<a ckid=’class_link’> が各クラス・モジュールへのリンクになります。
この辺りの処理が理解しづらい人は CGIKit の Wiki を参照してください。
リンクの生成には前回同様 CGIKit::Link を使います。ソースを見てみましょう。
前回は href 属性を使いましたが、今回は action_class、 direct_action、 query、 session_id 属性が使われていますね。
action_class にはダイレクトアクションに使用するクラスを指定します。今回は RI2Reference::DirectAction ですね。direct_action は呼び出したいメソッド名です。ここにはメソッド名から action を除いた部分を指定します。ClassListPage からはクラスのマニュアルを表示させるので、指定する値は “class” となります。
query 属性は クエリーに指定する値のペア(Hashのオブジェクト)です。ここで指定した値は CGIKit::Link が生成する URL に付加されます。
例えば
とすると、URL には
という GET のクエリーデータが付加されます。query の値は CGIKit 側で自動的に URL エンコードされます。先ほど述べたようにここで指定した値が RI2Reference::DirectAction#class_action で参照されます。
前回は一般的な CGI スクリプトで使用される GET/POST を使いませんでしたが、これは CGIKit がセッションや GET/POST の部分を自動的に処理してくれたからです。今回のようにダイレクトアクションを使用する場合はセッションや GET/POST の処理が自動的には行われませんので、query 属性等を使って自分で GET/POST を処理しなくてはなりません。
session_id 属性は URL にsession ID を付加するかどうかを決定します。これを有効にすると URL に変な文字列が付いてしまうので無効にします。無効にするには session_id 属性に false を指定します。
この設定で
の部分は
のようになります。先ほども述べましたが、/d/ の後ろにクラスの指定がない場合は CGIKit::Application#direct_action_class に指定したクラスが使用されます。今回の場合は RI2Reference::DirectAction ですね。上の URL にアクセスすると、 RI2Reference::DirectAction#class_action が実行されることになります。
ClassPage の基本構成は、
とします。
先にページを構成する3つのファイルをお見せします。先ほどの CGIKit::Link のダイレクトアクションの使い方が分かれば難しいところはそれほど無いと思います。
{
:is_class => {
:element => Conditional,
:condition => :class_page?,
},
:is_module => {
:element => Conditional,
:condition => :class_page?,
:negate => true,
},
:class_name => {
:element => String,
:value => :full_name,
},
:class_comment => {
:element => String,
:value => :class_comment,
:escape => false,
},
:superclasses => {
:element => Repetition,
:list => :superclasses,
:item => :mod
},
:included_modules => {
:element => Repetition,
:list => :included_modules,
:item => :mod,
},
:module_link => {
:element => Link,
:string => :module_name,
:action_class => RI2Reference::DirectAction,
:direct_action => 'class',
:query => :module_query,
:session_id => false,
:class => 'module_link',
},
:constants => {
:element => Repetition,
:list => :constants,
:item => :con,
},
:constant_name => {
:element => String,
:value => :constant_name
},
#:constant_value => {}
:ims => {
:element => Repetition,
:list => :instance_methods,
:item => :i,
},
:attrs => {
:element => Repetition,
:list => :attributes,
:item => :i,
},
:cms => {
:element => Repetition,
:list => :class_methods,
:item => :i,
},
:instance_method_link => {
:element => Link,
:string => :method_name,
:action_class => RI2Reference::DirectAction,
:direct_action => 'method',
:query => :method_query,
:session_id => false,
:class => 'method_link',
},
:attribute_link => {
:element => Link,
:string => :method_name,
:action_class => RI2Reference::DirectAction,
:direct_action => 'method',
:query => :method_query,
:session_id => false,
:class => 'method_link',
},
:class_method_link => {
:element => Link,
:string => :method_name,
:action_class => RI2Reference::DirectAction,
:direct_action => 'method',
:query => :class_method_query,
:session_id => false,
:class => 'method_link',
}
}
RI2Reference::DirectAction で出てきた setup です。これは与えられた引数を HTML として表示させるために簡単なデータ変換を行います。変換されたデータはインスタンス変数に格納されて、データを表示するときに参照されます。setup メソッドの引数は RI2Reference::DirectAction によって決定されます。その引数の entry は full_name 対応する ri のデータです。
setup によって設定された ClassPage のインスタンス変数と ri のデータとの対応関係は下のようになります。
@full_name | 表示されるページのクラス・モジュール名 |
@superclasses | スーパークラスの名前一覧 |
@included_modules | includeされているモジュール名一覧 |
@constants | 定数 |
@instance_methods | インスタンスメソッド |
@attributes | アトリビュート |
@class_methods | クラスメソッド |
各変数には実際に何が格納されているのか見てみましょう。
RI2Reference::DirectAction で呼び出された RI2Reference::Utils.get_class_entry(full_name, plural = false) は full_name に対応するクラス・モジュールの情報を返します。対応する情報がない場合は nil が返ります。plural は full_name に対応するクラス・モジュールが複数あった場合にそのうちのひとつを返すのかすべてを返すのかを決めます。
返値は RI::ClassDescription のオブジェクトです。このオブジェクトには クラスやモジュールの説明が格納されており、下のようなメソッドがあります。ClassPage のインスタンス変数に格納した値はこれらのメソッドの返値です。
表示するページによってクラス名だったりモジュール名だったりするので、それに合わせて表記を「class」や「module」に変えなくてはいけません。そのために if 文に相当する CGIKit::Conditional を使ってクラス名の場合には「class」を、モジュール名の場合には「module」を表示させます。
表示するページがクラスの場合、Object 以外にはスーパークラスがあるはず(将来は違うのかな…)なので、 RI2Reference::ClassPage#class_page? を上のように定義します。
次に ckd の設定です。is_class、 is_module の目印の付いた部分に CGIKit::Conditional を設定します。:condition が true になると目印の付いた部分は表示され、false になると表示されません。is_class、 is_module は全く正反対の動作をさせたいので、is_module の方に negate属性 を true にします。negate 属性が true だと、condition 属性が true の時に内容が表示されず、false の時に表示されるようになります。
概要の部分には RI2Reference::Utils.struct2html を使っているんですよね…。ri のデータには簡単な HTML のタグが含まれています。そのため CGIKit のエレメントを使ってその内容を表示させるのは少々難しいのです。仕方が無いので RI2Reference::Utils.struct2html を使って、CGIKitに頼らずに HTML を生成します。RI2Reference::Utils.struct2html(entry) の引数には get_method_entry、 get_class_entry の 返値を渡します。このメソッドは再帰呼び出しを使って ri から得られたデータを HTML にするので、分かりにくいかもしれません。
RI2Reference::Utils.struct2html で生成された HTML は CGIKit::String を使って ClassPage.html の中に埋め込みます。前回の記事でも書きましたが、CGIKit::String の value 属性に 「<」「>」のような文字が含まれると、CGIKit::String は自動的にその部分を escape します(つまり、「<」は「<」に、「>」は「>」に変換されます)。そのため HTML を埋め込む場合はその処理を無効にしておかないと意図したように表示されません。これを回避するためには CGIKit::String の escape 属性に false を指定します。
残った部分は前回までの説明でほとんど理解できるはずです。御自分で色々考えてみて下さい。
1点だけ説明を追加しておきます。
この部分です。最後に class 属性が使われていますが、これは CGIKit::Link には無い属性です。CGIKit では各エレメントに定義されていない属性を使用すると、その属性は HTML の属性とみなされます。
これにより上の場合は
のように HTML の a タグに class 属性が設定されることになります。クラス属性は CSS でデザインする時に用いるので動作そのものには関係有りませんが、覚えておくと応用が利きます。
そうですね。よく似ています。
{
:method_name => {
:element => String,
:value => :full_name,
},
:parameter => {
:element => String,
:value => :parameter,
},
:visibility => {
:element => String,
:value => :'entry.visibility',
},
:aliases => {
:element => String,
:value => :aliases,
},
:method_comment => {
:element => String,
:value => :method_comment,
:escape => false,
},
}
MethodPage#setup は分かりますね。単にインスタンス変数に必要な情報を代入しているだけです。setup の引数である entry は RI2Reference::Utils.get_method_entry の返値です。
RI2Reference::Utils.get_method_entry(full_name, plural = false) は full_name に対応するメソッドの情報を返します。対応する情報がない場合は nil が返ります。plural は get_class_entry と同じ役割です。このメソッドの実行例を下に示します。
返値は RI::MethodDescription のオブジェクトです。RI::ClassDescription と同様にメソッドに関するいくつかの情報が含まれています。MethodPage に配置されたエレメントから RI::MethodDescription のメソッドを呼び出すことでメソッドのリファレンスを作成します。各メソッドの役割は下のようになります。
HTML の visibility の目印のところには見慣れない記述がありますね。
value 属性に :’entry.visibility’ という Symbol オブジェクトが設定されていますが、これは self.entry.visibility を呼び出すことを意味します( self は RI2Reference::MethodPage のオブジェクトとします)。 Ruby の文法では「:」の後ろに文字列を指定することができ、 CGIKit は Symbol オブジェクトの文字列の部分を評価して、属性の値とします。 上の場合であれば CGIKit は
を
に変換し、
のようにして評価します。 そして、その結果が value 属性の値になります。これによって
は CGIKit::String エレメントにより self.entry.visibility の値に変換され、
のようになります。
…おっしゃるように説明しません。今まで見てきたページと構成はほとんど変わりませんので、御自分で考えてみて下さい。
module RI2Reference
class SearchResult < CGIKit::Component
attr_accessor :class_list, :method_list, :i
def query
{'n' => CGIKit::Utilities.escape_url(@i)}
end
end
end
{
:classes => {
:element => Repetition,
:list => :class_list,
:item => :i,
},
:methods => {
:element => Repetition,
:list => :method_list,
:item => :i,
},
:class_link => {
:element => Link,
:string => :i,
:action_class => RI2Reference::DirectAction,
:direct_action => 'class',
:query => :query,
:session_id => false
},
:method_link => {
:element => Link,
:string => :i,
:action_class => RI2Reference::DirectAction,
:direct_action => 'method',
:query => :query,
:session_id => false
},
}
うーん、この記事で使うサンプルは各ページがシンプルな構成になるように書いています。あまり複雑なことをすると、読みにくくなりますからね。
今回は WEBrick だけですので、RI2Reference のディレクトリで
として
等にアクセスして色々試してください。
使いやすいリファレンスマニュアルが欲しいということで作ったのですが、
などなど不満が色々とあります。この中で1つ目は何とか対処したいのですが、今の RDoc では各メソッドの概要を記述することが出来ないので対応が難しいです。
宿題
エレメントリファレンスやダイレクトアクションの説明などがあります。
リファレンスマニュアルの構成を考える際に以下のサイトを参考にしました。
某サービス業をしています。エンタープライズアプリケーション1を使って仕事をしています。
記事のリリース直前に Frameless RDoc template が出て、へこんだのは内緒です。
誰かこのアプリを rewrite してくれません?いやマジで。 ↩