Webフレームワークの新しい波 Waves探訪記

第4回 Twitter風WebアプリケーションTwを強化する

この記事を読むのに必要な時間:およそ 4.5 分

認証を追加する

認証機能を追加して,発言者が分かるようにします。Wavesの認証に関しては明示的に書かれたドキュメントが無く,お勧めのやり方がどれかはよく分からないので,ここではRackのBASIC認証を使ってみます。

設定ファイル(configrations/development.rb)の修正

RackはWavesでWebサーバの抽象化を担うライブラリです。configrations/development.rbを以下のように変更することで,簡単なBASIC認証を使うことができます。

BASIC認証を行う

module Tw
  module Configurations
    class Development < Default
      
      database :host=>'localhost', :adapter=>'sqlite', :database=>'db/tw_dev'
      host '0.0.0.0'
      port 3000
      reloadable [ Tw ]
      log :level => :debug  
      
      application do
        use Rack::Auth::Basic do |username, password|
          u = Tw::Models::User.find(:name=>username)
          u && u.password == password
        end
        use Rack::Static, :urls => ['/css', '/javascript'], :root => 'public'
        use Rack::ShowExceptions
        run Waves::Dispatchers::Default.new
      end       
      
    end
  end
end

「use Rack::Auth::Basic」以下のブロック内で,Userモデルの名前とパスワードの一致を確認しています。

スタティックファイルの処理

また「use Rack::Static, :urls => ['/css', '/javascript'], :root => 'public'」を追加しています。Rack::StaticはRackのミドルウェアのひとつで,指定したディレクトリ配下のファイルはWavesに渡されず,即座にクライアントにレスポンスとして返されます。スタイルシートのように内容の変化しない静的なファイルの指定に利用します。

さてこれでとりあえず認証画面が出るようになったはずです。ブラウザから「http://localhost:3000/words」と入力してみてください。IDとパスワードの要求画面になれば成功です。

ビューを修正する

ユーザと発言には一対多の関連を設定しました。次に発言をPOSTするとき,それがどのユーザのものであるか指定する必要があります。発言をPOSTするビュー(templates/word/list.mab)を修正します。

発言をPOSTするときにユーザidを設定する

ユーザを指定して発言をPOSTするコード

layout :default, :title=>'Tw' do
  @user = Tw::Models::User[:name=>request.env['REMOTE_USER']]
  form :action=>'/words', :method=>'post' do
    textarea '', :name=>'word.text', :cols=>80, :class=>'words'; br
    input :type=>:hidden, :name=>'word.user_id', :value=>@user.id
    input :type=>:submit, :value=>'Update'
  end
  @words.each do |word|
    view :word, :summary, :word=>word, :user=>@user
  end
end

まず「@user = Tw::Models::User[:name=>request.env['REMOTE_USER']]」でユーザを取得しています(ユーザのチェックをページ表示ごとに毎回行うのは効率が悪いので,Rack::Auth::Basicで認証を行ったときにセッションで格納するなどの手段をとるべきかもしれません⁠⁠。そして「input :type=>:hidden, :name=>'word.user_id', :value=>@user.id」にあるようにhidden要素の名前を'word.user_id',値を@user.idにすることで,wordレコードのuser_idフィールドに該当ユーザのidがセットされます。

ここで/wordsに対してPOSTすればレコードの生成になるのは前述したとおりです。

またsummaryテンプレートに「view :word, :summary, :word=>word, :user=>@user」と@userを渡すことで,各発言と一緒にユーザ名も表示できるようになりますので,summaryテンプレート(templates/word/summary.mab)の修正を行います。cssの組み込みを考えて,クラス名なども設定しましょう。

Markabyテンプレート

ここまでWavesのデフォルトのテンプレートライブラリであるMarkabyについて,あまり説明してこなかったので,ここで簡単に説明します。通常のテンプレートライブラリでは,特殊なテンプレート用の記法を導入しているものがほとんどです。例えばRailsのテンプレートエンジンであるerubyではhtmlのコードの中に「<%= %>」といった特殊な記法を導入し,その中でRubyの式を書きます。

Markabyはこうした独自の記法をまったく使わず,全てRubyのコードで記述します。Markabyの例をドキュメントから引用します。

Markabyのコードの例

html do
  head do
    title 'Products: ' + action_name
    stylesheet_link_tag 'scaffold'
  end

  body do
    p flash[:notice], :style => "color: green"

    self << content_for_layout
  end
end

Markabyでは,Rubyのメソッドして解釈されない名前がhtmlの要素として展開されます。例えばtableメソッドは<table>要素になります。パラメータとしてハッシュを渡すと,html要素のアトリビュートになります。ただし第一引数の文字列は要素にはさまれる文字列として展開されます。 ⁠a 'test', :href=>'/testhost/index'」とすると<a href="/testhost/index">test</a>」となるわけです。ネストする要素はブロック内に記述します。

要素のクラスとIDの指定には特別な記法が用意されています。contentsクラスのdiv要素なら「div.contents⁠⁠,IDがheaderのdiv要素なら「div.header!」と記述します。

Wavesの各コンポーネントはドキュメントが少ないのが弱点なのですが,その上Markabyのように機能を動的に生成するライブラリが多いので,Rdocを見ても記述がないものがたくさんあります。今のところ地道にサンプルコードやソースコードを当たるしかないようです。

summaryテンプレートを修正する

以下のように修正します。各要素に適当なクラスを設定して,cssでデザインを指定できるようにします。list.mabのviewメソッドで呼び出したときに@wordと@userを設定しているので,この中で利用しています。

summaryテンプレート

div.text do
  table(:class=>(@word.user.name==@user.name)?'mywords':'words') do
    tr do
      td.name(:rowspan=>2, :style=>'width:20%;') {@word.user.name}
      td.word {@word.text}
    end
    tr do
      td.date {@word.created_on.strftime("%Y-%m-%d %H:%M") if @word.created_on}
    end
  end
end

あとは適当なcssを設定すれば見栄えを変えることができるようになりました。 public/css/base.cssを作り,簡単に設定しておきます。

public/css/base.css

body {
  padding: 2em;
}

table.words {
  margin: 2px;
  width: 600px;
  border: 1px dotted orange;
}

table.mywords {
  margin: 2px;
  width: 600px;
  border: 1px solid orange;
}

table.words tr {}

table.words td,
table.mywords td {
  padding: 2px 10px;
}

まとめと次回の予定

ユーザの認証,自発言と他人の発言の区別,cssによるデザインの設定などができるようになりました。次回はユーザ間の関連,Twitterで言うfollow/followingのような友人関係を組み込みたいと思います。

著者プロフィール

松永肇一(まつながけいいち)

株式会社ライフメディア 創造推進部プロジェクトマネージャ。東京都出身。千葉大学工学部での卒論テーマは人工知能関連だったが,富士通に入社後はPCのソフトウェア開発に関わる。"GNU for Towns"のために,リチャード・ストールマンに会いにいくような妙な仕事を経て現職。Ruby on Railsを使ってWebアプリを開発する毎日。ブログは「ma2の日記」。

URLhttp://d.hatena.ne.jp/ma2/