FrontPage  Index  Search  Changes  Login

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)を入力してページを 作成します。

SandBox 新規作成

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

SandBox 編集

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

SandBox 表示

かなりWikiらしくなってきました。

flash

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

NoMethodError

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 (存在しないページ名)にアクセスすると、

新規作成+flash

このように 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 にアクセスすると、

新規作成+入力値

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

Last modified:2005/11/27 12:48:22
Keyword(s):
References:[RailsでWikiクローンを作る]