WEB + DB PRESS 28号の【特集】Ruby on Rails入門に取り組む(6日目)

続き

続きをやります。
前回内容とKPTを見て思い出します。


多対多の解説を見て、失敗した事に気づきました。
それは、サンプルがログインシステム(第二章)の画面を使用している・・・
えぇ〜〜〜!!しまった。

ということで、ソースコードとDBデータのマージをしました。

Viewで多対多のデータを利用する

welcomeページに多対多レコードからSNS情報表示するように記述しました。

<h1>Account#welcome</h1>
<p>Find me in app/views/account/welcome.rhtml</p>

welcome, <%= h @me.name %>!

<h2>communities ( <%= @me.communities.size %> )</h2>
<% @me.communities.each {|community| -%>
<%= link_to h(community.name), :controller => "communities", :action => "show", :id => community %><br>
<% } -%>

<h2>Diary</h2>
<% @me.topics.each {|topic| -%>
  <h3><%= topic.written_on %> <%= h topic.title %></h3>
  <%= textilize topic.content %>
<% } -%>

確認

解説を見ながらソースコードを順を追ってみる。

  • 前提
    • 第二章のソースが完成されている。
    • @meにはログインされたユーザー情報が格納されている

【1】データの取得
meにはUser情報が格納されているので、ActiveRecordのMODEL関連付け設定により@meに関連付けされた、コミュニティや
日記情報を「@me.communities」「@me.topics」という感じで、アクセスできる。


【2】データの表示
<% -%>はロジックコード、<%= %>は表示。JSPPHPと使い方は同じ感じ。


【3】ヘルパーメソッドを使う
あとは、取得した配列をヘルパーメソッドを使って、リンク張ったり、表示形式などを整える。

よし!完成。

メソッドフック

メソッドの実行前後に、コールバックという機能でフックさせることができるらしい。
インターセプターと同じ感じなんでしょうか??

例えばこんな事ができるとのこと。

  • セッションのログイン状態確認
  • バリデーションの前に値を加工
  • 保存前に依存関係を解決

コールバックの呼ばれる順番もあるので、これは用途によって覚えておきたい。

こんな感じでModelにメソッド定義すれば使えるらしい。

class User < ActiveRecord::Base
  # 〜略〜
  def self.authenticate(name, password)
    find_first([ "name = ? and password = ?", name, password ])
  end
end

モデル中心に色々コードを書くのですね。なるほど。

最後トランザクション

saveの時は暗黙的にDBのトランザクションを使用しているらしく、明示的にUser.transactionメソッドを使ってもできるらしい。

終った!!第四章はすごかった。
Rails初心者には一つの壁っぽい。もうちょっとActiveRecordを使ってみたい。

第4章後半分の振り返り(KPT法使うよ)

ぷちKPT

  • KEEP【良かった事、続けていきたい事】
    • はてなDiaryに引き続き書いて、「見える化」とKPTができた。
    • 時間短縮を図った。
    • マージして、作り直しを避けた
    • KPTを確認してから作業に入った。
  • PROBLEM【悪かったこと、改善したい事】
    • Viewに関しては理解が足りない。
    • インターセプターとコールバックの違いがよくわからない。
    • vimの設定ファイルを触っていた時間が多かった。
  • TRY【今後取り込みたいこと】
    • Viewをたくさん書く。
    • ActiveRecordの機能を色々使ってみる。(抽象的な事なので優先度低で取り組む)
    • Scaffoldだけじゃなく、初心者かHOWTOから一歩進んだModelについて学ぶ。
    • 他のヘルパーメソッドを試す。

第5章 テスト駆動開発

勢いで、第5章もやってしまおう。
railsはUnitTest環境が作りやすいので、テスト駆動開発アジャイルととても相性がいい感じがする。


こんな事やってみたい

  • エクセル駆動からTracRedMineにするとか。
  • Tracでチケットばんばん発行とか。
  • Tracでコミット時にフックさせてチケットと連動できるらしいのですが、RedMineでもできないのかなぁ??

Railsのテストに関して

TestのクラスはRuby1.8標準のTest::Unitクラスを使うらしい。


setup、teardownメソッドで初期化と後始末ができて、テストメソッドの先頭はtestで始めるという規約があるとのこと。


テストメソッドはassertが用意されていているらしい。

  • assert
  • assert_nil
  • assert_equal
  • assert_raise

他にも色々あるみたいです。

テスト専用のDBも使用できるとのことで、fixtureという機構を用いてデータベースを構築するらしい。
rakeでまとめてテスト実行もできる。

fixtureに関して

fixtureはモデルテストの初期値を設定するしくみで、ActiveRecordを通してDBのテーブルを元に作られるらしい。
DBのテストはアプリケーションでDB値を書き換えしてしまうので、assertが書きにくい為、予め初期値をYAML形式で
保持しておくことができらしい。

さっそくテストをやってみる。

test/unit/user_test.rbにテストケースを書いて、Fixturesにデータを定義する

bob:
  id: 3
  name: bob
  password: s3cret
require File.dirname(__FILE__) + '/../test_helper'

class UserTest < Test::Unit::TestCase
  fixtures :users

  # 存在するデータに対するテスト
  def test_bob
    assert_equal "bob", @bob.name
    assert_equal "s3cret", @bob.password
  end

  # 認証で存在するデータと存在しないデータをテストする
  def test_auth
    assert User.authenticate("bob","s3cret")
    assert_nil User.authenticate("bob","hogehoge")
  end
end

そして、unitテスト実行

C:\InstantRails\rails_apps\wdpress\test\unit>ruby user_test.rb
  1) Error:
test_bob(UserTest):
NoMethodError: You have a nil object when you didn't expect it!
The error occurred while evaluating nil.name
    user_test.rb:8:in `test_bob'

2 tests, 2 assertions, 0 failures, 1 errors

失敗は発生しないはずが、インスタンスメソッドに対してテストを行うメソッド、test_bobでエラーが発生している。
pメソッドで、ログ出力したらインスタンスnilだった。

データに対する仕組みがよくわかっていないので、とりあえず先に進むことにする。

コントローラーのテスト

functionalディレクトリにcontroller用のテストファイルがあるらしい。

ログイン機能に対するテストを書いた

require File.dirname(__FILE__) + '/../test_helper'
require 'account_controller'

# Re-raise errors caught by the controller.
class AccountController; def rescue_action(e) raise e end; end

class AccountControllerTest < Test::Unit::TestCase
  fixtures :users
  # 自動生成されたコード
  def setup
    @controller = AccountController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
  end

  # 認証をPOST値からチェック
  def test_auth_bob
    post "authenticate", "me" => {"name" => "bob", "password" => "s3cret"}
    assert_redirected_to :action=>"welcome"
    assert_equal @bob, session["me"]
  end

  # 不正なログインのチェック
  def test_invalid_login
    post "authenticate", "me" => {"name" => "bob", "password" => "hogehoge"}
    assert_redirected_to :action => "login"
    assert_nil session["me"]
  end
  
  # ログインとログアウトをチェック
  def test_login_logout
    post "authenticate", "me" => {"name" => "bob", "password" => "s3cret"}
    assert_redirected_to :action => "welcome"
    assert session["me"]

    get "logout"
    assert_template "logout"
    assert_nil session["me"]
  end

  # 自動生成されたコード
  # Replace this with your real tests.
  def test_truth
    assert true
  end
end
C:\InstantRails\rails_apps\wdpress\test\functional>ruby account_controller_test.rb

postやgetのアクションをエミュレートできるらしい。
すげぇな。

あとこんなテストメソッドがあるらしい。

  • HTTPのレスポンスの状態をチェック
  • redirect先をチェック
  • URLとControllerのアクションの対応をチェック
  • タグ名や属性地などを元にHTMLをチェック

値の取得先は

  • セッションに格納されている値
  • コントローラでインスタンス変数として保持している値
  • flashに格納されている値
  • Cookieの値

などが使用できるみたい。
今回はセッションとインスタンスを利用した例のようです。

今回もエラーが1件インスタンス変数が取得できなかった。
なんでだー。時間ができたら調べてみよう。

この勉強の最後の最後BreakPoint

BreakPointもできるよー。と少ないスペースに記載がありました。
GUIIDEでは目新しい機能ではないが、CUI環境でも使用できるようです。
BreakPointデバッグがしたいが為にGUIIDEを使用している人も多いんじゃなかな?と思っています。


使い方は簡単でした。

  1. ソースコードに『breakpoint』と一行追加する
  2. scirpt/serverでサーバーを起動させる
  3. 別コンソールで script/breakpointerでブレイクポイント機能を実行する

そうするとBreakpointerが待ち状態になって、breakpointがキャッチされると、irbデバッグできました。
とても便利です。この特集でやったことは便利だと思った数を、へぇ単位換算して何へぇゲットしたか知りたいくらい。

デバッグ例(パラメータとセッションを表示)

No connection to breakpoint service at druby://localhost:42531 (DRb::DRbConnError)
Tries to connect will be made every 2 seconds...
Executing break point at ./script/../config/../app/controllers/account_controller.rb:24 in `authenti
irb(#<AccountController:0x4a22214>):001:0> @params
=> {"commit"=>"Login", "me"=>{"name"=>"Tom", "password"=>"Tom"}, "action"=>"authenticate", "controll
unt"}
irb(#<AccountController:0x4a22214>):002:0> @session
=> #<CGI::Session:0x4a21ddc @session_id="1f7acd798a3292ea3543eeabf0be0515", @dbprot=[#<CGI::Session:
4a21c60 @p=#<PStore:0x4a1bc98 @table=nil, @abort=false, @rdonly=false, @transaction=false, @filename
/../config/../tmp/sessions//ruby_sess.9c7014fd142e657e">, @hash={"me"=>#<User:0x4a18a5c @topics=nil,
ies=nil, @attributes={"name"=>"Tom", "phone_number"=>"03-1234-0000", "id"=>"1", "password"=>"Tom"},
 "flash"=>{}}>], @dbman=#<CGI::Session::PStore:0x4a21c60 @p=#<PStore:0x4a1bc98 @table=nil, @abort=fa
ly=false, @transaction=false, @filename="./script/../config/../tmp/sessions//ruby_sess.9c7014fd142e6
sh={"me"=>#<User:0x4a18a5c @topics=nil, @communities=nil, @attributes={"name"=>"Tom", "phone_number"
-0000", "id"=>"1", "password"=>"Tom"}, @pet=nil>, "flash"=>{}}>, @data={"me"=>#<User:0x4a18a5c @topi
ommunities=nil, @attributes={"name"=>"Tom", "phone_number"=>"03-1234-0000", "id"=>"1", "password"=>"
t=nil>, "flash"=>{}}, @new_session=false>
irb(#<AccountController:0x4a22214>):003:0> exit

Executing break point at ./script/../config/../app/controllers/account_controller.rb:28 in `authenti
irb(#<AccountController:0x4a22214>):001:0> @params
=> {"commit"=>"Login", "me"=>{"name"=>"Tom", "password"=>"Tom"}, "action"=>"authenticate", "controll
unt"}
irb(#<AccountController:0x4a22214>):002:0> exit

Server exited. Closing connection...

No connection to breakpoint service at druby://localhost:42531 (DRb::DRbConnError)
Tries to connect will be made every 2 seconds...

感想

Railsのひととおりの機能を堪能した感じ。

Railsのさわりで、ここまでできてしまってすごい。自分がすごいと感じちゃ駄目なんですよね(^^;
もっとやってみたい。やりまくりたいと思いました。
なにしろ面白いですし、なんか一つ一つに惹かれる魅力がある。


vimと相性がいい所も好き。
rails.vimの移動系が特に楽で、終盤にインストールしたautocomplpop.vimも役立ってくれました。


違う参考書などで他にも色々とアプリを作ってみたい。

WEB + DB PRESS 28号の【特集】Ruby on Rails入門の振り返り

6日間を振り返って・・・

ぷちKPT

  • KEEP【良かった事、続けていきたい事】
    • GUIIDEに頼らずvimを利用できた。(GUIIDEの非難ではなく)
    • やり通せた。
    • 何を作れば、MVCモデルが実現できるかわかった。
    • KPTを行った。
  • PROBLEM【悪かったこと、改善したい事】
    • 一日で実行できた量が少ない
    • rails.vimの機能をあまり使い切れなかった。
  • TRY【今後取り込みたいこと】
    • 他のrailsアプリを作ってみる
    • Modelの使い方をもっと覚える
    • Viewのヘルパーとか、その他機能も使ってみる
    • テストを書く