RailsによるアジャイルWebアプリケーション開発第二版を勉強するの巻(第2回:5章〜6章の途中まで)

これは何?

RailsによるアジャイルWebアプリケーション開発 第2版

RailsによるアジャイルWebアプリケーション開発 第2版

RailsによるアジャイルWebアプリケーション開発第二版を読みながら、そこで勉強したことを書くエントリです
分厚い上、27章まであるので、数回に分けて書きます

はじめに

Instant Rails2.0を使って作業をしていたため、デフォルトでは、Railsのバージョンが2.0.2であったことが判明
本では、Rails1.2をベースにかかれており、Rails2.0だと挙動が異なるscaffoldのあたりでハマる事件が発生
後、Rails2.0だと、SQLiteがデフォルトのDBなのね…

C:\InstantRails-2.0-win\rails_apps>rails --version
Rails 2.0.2


Railsのそれぞれのバージョンがインストールされているなら、以下の方法で、1.2.6版のRailsアプリを作る事も出来るらしい
バージョンを指定してRailsアプリケーションを作る | JAM☆ぱん


現状windows環境なので、泣く泣くInsant Rails 1.7をダウンロード(回線細いので時間かかる…)

C:\InstantRails-1.7-win\InstantRails\rails_apps>rails --version
Rails 1.2.3


今度こそおっけー

5章 Depotアプリケーション

ショッピングカートをインクリメンタルに作りながら、Railsを学ぼう的な内容
基本仕様と、ざっくりしたページフローとか

6章 タスクA:商品の管理

イテレーションA1:動くものを作る
  • Railsアプリを作る
C:\InstantRails-1.7-win\InstantRails\rails_apps>rails depot
C:\InstantRails-1.7-win\InstantRails\rails_apps>mysqladmin -u root create depot_development


./depot/config/database.yml に、上記DBの設定が入る

# コメントはかせいさん注
# 開発用DBの設定
development:
  # 使用中のDBの種類
  adapter: mysql
  # データベースの名前(上で設定した奴)
  database: depot_development
  # ユーザ名
  username: root
  # パスワード
  password:
  # DBを実行しているマシンのアドレス
  host: localhost

# テスト用DBの設定
test:
  …

# 本番用DBの設定
production:
  …


んで、DBがRailsと紐付いてるか確認

C:\InstantRails-1.7-win\InstantRails\rails_apps>rake db:migrate
rake aborted!
No Rakefile found (looking for: rakefile, Rakefile, rakefile.rb, Rakefile.rb)
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1849:in `load_rakefile'
(See full trace by running task with --trace)


なんか失敗!
色々調べてみたら、単にアプリのあるディレクトリで実行してないだけでした…。
参考:Ruby ‎(T3) # rake aborted! No Rakefile found‎


改めて、確認

C:\InstantRails-1.7-win\InstantRails\rails_apps\depot>rake db:migrate
(in C:/InstantRails-1.7-win/InstantRails/rails_apps/depot)


なんか上手く行った様子
本には、上手くいなかった場合の解決法がいろいろ書いてあるけど、一番うっかりやらかしそうなこの解説は無かった…

Productsモデルとテーブルの作成

データベースマイグレーション

  • データベースの変更の履歴を残しつつ、比較的手軽にDBの内容を設定できる


まずはやってみる

C:\InstantRails-1.7-win\InstantRails\rails_apps\depot>ruby script/generate model product
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/product.rb
      create  test/unit/product_test.rb
      create  test/fixtures/products.yml
      create  db/migrate
      create  db/migrate/001_create_products.rb


001_create_products.rbが、マイグレーションファイル

class CreateProducts < ActiveRecord::Migration
  def self.up
    create_table :products do |t|
    end
  end

  def self.down
    drop_table :products
  end
end


upメソッドに、このマイグレーションを適用した時の処理を書く
downは、ロールバックした時用
デフォルトで、products テーブルの生成と削除が書かれている


ここのupメソッドにて、テーブルのカラムを定義する

class CreateProducts < ActiveRecord::Migration
  def self.up
    create_table :products do |t|
      t.column :title,       :string
      t.column :description, :text
      t.column :image_url,   :string
    end
  end

  def self.down
    drop_table :products
  end
end


入力した内容をRakeに実行させてテーブルを作らせてみる

C:\InstantRails-1.7-win\InstantRails\rails_apps\depot>rake db:migrate
(in C:/InstantRails-1.7-win/InstantRails/rails_apps/depot)
== CreateProducts: migrating ==================================================
-- create_table(:products)
   -> 0.2040s
== CreateProducts: migrated (0.2040s) =========================================


DBを見てみる

C:\InstantRails-1.7-win\InstantRails\rails_apps\depot>mysql -u root depot_development
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4 to server version: 5.0.27-community

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> SHOW TABLES;
+-----------------------------+
| Tables_in_depot_development |
+-----------------------------+
| products                    |
| schema_info                 |
+-----------------------------+

きちんと、products テーブルができている

  • schema_infoは、マイグレーションのバージョンを保存しているテーブルでRakeが勝手に作るらしい


products のテーブル定義

mysql> show fields from products;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| title       | varchar(255) | YES  |     | NULL    |                |
| description | text         | YES  |     | NULL    |                |
| image_url   | varchar(255) | YES  |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+


ちゃんと作られているっぽい!


ついでにschema_infoの中身

mysql> select * from schema_info;
+---------+
| version |
+---------+
|       1 |
+---------+


バージョンだけ。シンプル

  • 関係ないけど、show fields schema_info; ってやったらエラーがでて怒られた。何でだろ?
コントローラの作成

adminという名前のコントローラを作ってみる

C:\InstantRails-1.7-win\InstantRails\rails_apps\depot>ruby script/generate controller admin
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/admin
      exists  test/functional/
      create  app/controllers/admin_controller.rb
      create  test/functional/admin_controller_test.rb
      create  app/helpers/admin_helper.rb


./depot/app/controllers に admin_controller.rb が生成される
なかみはこんだけ

class AdminController < ApplicationController
end
管理アプリケーションの追加

ここに、scaffold :productを追加

class AdminController < ApplicationController
  scaffold :product
end


Railsを起動して、http://localhost:3000/admin にアクセスしてみる

おおお、うまくいった!


New productから、新規にデータを入れてみる


うまく入った様子

Railsのscaffold

「足場」の意味
モデルを操作する為のフレームワークで、ジェネレータによって自動生成される
対象にしたテーブル(今回ならproduct)の中身を見て、いい感じに表示、入力用のHTMLを生成してくれる
大抵、アプリの開発が進むにしたがって、scaffoldは消えてなくなる
テスト用にデータを入力、表示したい時や、開発者用の外部に出さない部分等に使う感じらしい

イテレーションA2:足りない列の追加

productに、価格(price)列を追加する


新しいマイグレーションの生成

C:\InstantRails-1.7-win\InstantRails\rails_apps\depot>ruby script/generate migration add_price
      exists  db/migrate
      create  db/migrate/002_add_price.rb


002_add_price.rb が生成されるので、中にprice追加の処理を追記する

class AddPrice < ActiveRecord::Migration
  def self.up
    # 列追加
    #   precision :値の有効桁数
    #   scale     :有効桁数の内、小数点の桁数
    #   default   :デフォルト値
    add_column :products, :price, :decimal, :precision => 8, :scale =>2, :default => 0
  end

  def self.down
    # 列削除
    remove_column :products, :price
  end
end


んで、マイグレーションの実行

C:\InstantRails-1.7-win\InstantRails\rails_apps\depot>rake db:migrate
(in C:/InstantRails-1.7-win/InstantRails/rails_apps/depot)
== AddPrice: migrating ========================================================
-- add_column(:products, :price, :decimal, {:precision=>8, :scale=>2, :default=>0})
   -> 0.2340s
== AddPrice: migrated (0.2340s) ===============================================


もっかい、http://localhost:3000/admin にアクセスしてみる


priceが追加されてる!

  • 開発モードで動いているRailsは、ブラウザからリクエストがあるたびに、テーブルを見直しているとのこと

Railsの再起動なしにこれはすごい!

イテレーションA3:検証!

入力内容をチェックする処理を追加する
モデル層にて、入力した内容をDBに格納する前にチェックを行う
具体的には、./depot/app/models/product.rb に追記する

class Product < ActiveRecord::Base
  # 必須チェック
  validates_presence_of :title, :description, :image_url
  # 数値以外エラー
  validates_numericality_of :price
  # ユニークであること
  validates_uniqueness_of :title
  # 正規表現でフォーマットをチェック
  validates_format_of	:image_url,
  			:with		=> %r{\.(gif|jpg|png)$}i,
  			:message	=> "は、GIF,JPG,PNGのURLでなければなりません"
  
  # 自作のバリデータ
  # (validate内に記述する)
  private
  def validate
    # errors.addで、バリデート時のエラーメッセージを表示
    errors.add( :price, "は、0.01以上の値である必要があります") if price.nil? || price < 0.01
  end
end


そうすると、こんな感じに入力チェックをするようになる


今日はここまで、イテレーションA4の静的なscaffoldに続きます
そんな感じー