RailsでWikiクローンを作る05
ページ表示
LoadingModule
後回しになっていた show アクションを実装します。 show は、Wiki のペー ジを表示するアクションです。つまり wiki ソースをパースして、HTML に変 換することになります。
この部分は Hiki のソースを利用させてもらい、以下の二つのファイルを作り ました(ソースは後で公開します)。
- parser_default.rb
- Hikiの style/default/parser.rbをほぼそのまま流用。 Hiki::Parser_default というクラスを定義している。 HikiのWiki文法で書かれた文字列を構文解析してトークン列に変換する。
- formatter_default.rb
- Hikiの style/default/html_formatter.rbを元に、 minki 用に InterWikiName やプラグインなどの機能削って手直ししたもの。 Hiki::Formatter_default というクラスを定義している。トークン列を HTML に変換する。
(注: 現在のHikiはHikiDocという新パーサを使用しています。この文章執筆時はHikiDocは無かったため、ここで利用しているHikiのparserやformatterは、HikiDocを利用する以前の古いものです。)
これらは Rails アプリの勉強という面では、あまり本質とは関係ないため、 ここでは詳しい説明は省きます。要は、Hikiのparserとhtml_formatterを流用 して、Wiki文法(Hiki文法)のソースを HTML に変換する、ということだけ押さ えておいてください。
問題は、これらのファイルをどこに配置すれば良いか、です。 lib あるいは vendor というディレクトリに置いておいて、どこかで require するという手 もありますが、ここでは Rails の LoadingModule という機能を使います (LoadingModule について、詳しくは http://wota.jp/ac/?date=20050810#p01 等を参照してください)。
app/controllers/hiki というディレクトリを作って、そこに parser_default.rb と formatter_default.rb を入れておくと、Rails アプリ 中で Hiki::Parser_default や Hiki::Formatter_default というクラスを使 おうとすると、自動的にそれらのファイルを load してくれます。
そして、app/controllers/wiki_controller.rb の show メソッドを以下のように書き換えます。
def show @page = Page.find_by_name params[:id] @title = (@page.title and !@page.title.empty?) ? @page.title : @page.name conf = Struct.new(:use_plugin, :use_wikiname).new(false, false) parser = Hiki::Parser_default.new conf tokens = parser.parse @page.content formatter = Hiki::Formatter_default.new tokens, self, conf @body = Hiki::Formatter_default.apply_tdiary_theme(formatter.to_s) render :text => @body, :layout => true end
@page = Page.find_by_name params[:id] で、params[:id]で示されるページ 名を検索して、該当する行を @page に代入します (該当する行がなかった場 合の処理は後ほど追加します)。 そして、@page.title に何か入っていればそ れを、なければ @page.name を @title に代入します。
次の conf = ... の部分は、まだ管理画面を作っていないので Struct でダミー の conf オブジェクトを生成しています。後程、管理画面を作って、conf に は設定情報オブジェクトが入るようになる予定です。
parser = Hiki::Parser_default.new conf でHikiパーサオブジェクトを作り、 tokens = parser.parse @page.contentで、@page.content に入っている Wiki ソースをトークン列に分解します。そして、Hiki::Formatter_default でトー クン列をHTML文字列に変換して @body に代入しています。
最後のrender :text => @body, :layout => trueは、@body の内容でビューを レンダリングするという意味で、HTMLタグも一切エスケープされずにそのまま 出力されます。その場合テンプレートは使われませんが、:layout => true と 指定しているので layout は利用します。
動作確認
では、今まで実装した部分の動作確認をしてみます。まず http://localhost:3000/wiki/new にアクセスしてページの新規作成画面を出 します。そして、何か適当なページ名(ここではSandBox)を入力してページを 作成します。

編集画面に移るので、Hiki文法で何か適当に文章を記述します。

保存して正しくページが表示されることを確認します。

かなりWikiらしくなってきました。
flash
さて、show アクションに存在しないページ名をつけて呼び出したらどうなる でしょうか? 例えば http://localhost:3000/wiki/show/hogehoge にアクセス すると以下のようになってしまいます。

show アクションの中では、params[:id]で示されるページが存在していること を前提としているため、ページが存在しない場合は上記のようなエラーが発生 してしまいます。本当は、show アクションは次のように実装する必要があり ます。
- 呼び出しパラメータのページ名をDBで検索し、見つかったらその編集フォーム画面を表示する
- 指定ページが見つからなかったら、見つからない旨を表示しつつ新規作成画面を表示する
指定のページが見つからなかった場合は、Hikiの動作にならい、「指定のペー ジは存在しません」というメッセージを添えて新規作成画面を表示するように したいのですが、どうすれば良いでしょうか?
Railsにはこのような用途に使う flash という機構があります。 flashの実体 はハッシュのようなもので、アクションの処理に関する各種データ、例えばメッ セージなどの文字列を入れておくことができます。 flashに入れたデータはセッ ションという機構を使って次のリクエストだけに引き継がれ、その後は自動的 に消されます。
その flash 機構を利用して show メソッドを以下のように書き換えます。
def show
@page = Page.find_by_name params[:id]
if @page
@title = (@page.title and !@page.title.empty?) ? @page.title : @page.name
conf = Struct.new(:use_plugin, :use_wikiname).new(false, false)
parser = Hiki::Parser_default.new conf
tokens = parser.parse @page.content
formatter = Hiki::Formatter_default.new tokens, self, conf
@body = Hiki::Formatter_default.apply_tdiary_theme(formatter.to_s)
render :text => @body, :layout => true
else
flash[:notice] = '指定のページは存在しません。ぜひ作成してください:-)'
redirect_to :action => 'new', :id => params[:id]
end
end
前回の show に比べて、Page.find_by_name でページが見つからなかった場合 の処理(else節)を加えています。 flash[:notice] の部分で、flash に「指定 のページは存在しません」というメッセージを格納しています。そして、new アクションへリダイレクトしていますが、flash のデータはそのアクションに 引き継がれます。
一方、new のテンプレート app/views/wiki/new.rhtml は、flash にメッセー ジが格納されていれば、それを表示する必要があるので、以下のように修正し ます。
<% @title = '新規作成' %> <div class="body"> <%if flash[:notice] %> <p style="color: green"><%= flash[:notice] %></p> <% end %> <%= error_messages_for 'page' %> <%= start_form_tag :action => 'create' %> <%= text_field 'page', 'name' %> <%= submit_tag "Create" %> <%= end_form_tag %> </div>
追加したのは
<%if flash[:notice] %> <p style="color: green"><%= flash[:notice] %></p> <% end %>
の3行で、flash[:notice] に値が入っていればそれを表示するようにしています。
それでは動作確認です。http://localhost:3000/wiki/show/hogehoge (存在しないページ名)にアクセスすると、

このように new アクションにリダイレクトされて、「指定のページは存在し ません。ぜひ作成してください:-)」というメッセージが表示されます。 その ままリロードするとflashのメッセージは消えますし、直接 new アクションを 呼び出してもメッセージは表示されません。 flashの内容が保持されるのは、 次のアクションまでというのがわかります。
しかし、このままでは入力フォームが空っぽでちょっと不親切です。入力フォー ムに指定したページ名を入れるため、 app/controllers/wiki_controller.rb の new メソッドに一行追加して修正します。
def new @page = Page.new @page.name = params[:id] if params[:id] end
ID が指定されていれば、それを@page.nameに代入しているだけです。これで、 http://localhost:3000/wiki/show/hogehoge にアクセスすると、

このように、あらかじめ入力フォームに指定したページ名が入るようになります。

Keyword(s):
References:[RailsでWikiクローンを作る]