Rails2.2でサンプルアプリを作ってみる(2)

Rails2.2を利用したサンプルアプリ作成の続きです。

すでにここまで作成が終わっているものとして話を進めていきます。

  1. Rails2.2でサンプルアプリを作ってみる(1) - 積み重ねた日々

今回の作業内容

今回は、以下の変更を行っていきたいと思います。

  • 商品情報に列(price)を追加する
  • 商品情報の入力値検証を追加する
  • 一覧画面の見栄えを美しくする

商品情報に列(price)を追加する

以下のようにデータベースを直接操作する必要はありません。

depot@localhost[depot_development]> alter table products add column price decimal(8, 2);

代わりに価格の列を追加するマイグレーションを作成します。

$ script/generate migration add_price_to_product price:decimal
      exists  db/migrate
      create  db/migrate/20081215142647_add_price_to_product.rb

作成されたマイグレーション用ファイル db/migrate/20081215142647_add_price_to_product.rb を確認してみます。upメソッド内に列の追加処理が、downメソッドには列の削除処理が入っていることが分かります。

class AddPriceToProduct < ActiveRecord::Migration
  def self.up
    add_column :products, :price, :decimal
  end

  def self.down
    remove_column :products, :price
  end
end

このままだと実際に追加したい形にはなっていないので、3行目のadd_columnに渡す引数に:precision, :scale, :defaultを追加します。

    add_column :products, :price, :decimal, :precision => 8, :scale => 2, :default => 0

マイグレーションを実行します。

$ rake db:migrate
(in /Users/kouichi/Development/Ruby/depot)
==  AddPriceToProduct: migrating ==============================================
-- add_column(:products, :price, :decimal, {:scale=>2, :default=>0, :precision=>8})
   -> 0.0955s
==  AddPriceToProduct: migrated (0.0958s) =====================================

列が追加されたことを確認します。

depot@localhost[depot_development]> show columns 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    |                | 
| created_at  | datetime     | YES  |     | NULL    |                | 
| updated_at  | datetime     | YES  |     | NULL    |                | 
| price       | decimal(8,2) | YES  |     | 0.00    |                | 
+-------------+--------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)

またデータベースが現在どのマイグレーションのバージョンまで適用されたのかを知ることができます。具体的には、schema_migrationsのversion列を見ることで分かります。

depot@localhost[depot_development]> select * from schema_migrations;
+----------------+
| version        |
+----------------+
| 20081209132148 | 
| 20081215142647 | 
+----------------+
2 rows in set (0.00 sec)

列を追加したので、それにともなってテンプレートファイルを変更する必要があるので、わせて追加します。今回、対象となるのは、以下の4ファイルです。(変更内容は割愛します)

  • app/views/products/index.html.erb
  • app/views/products/new.html.erb
  • app/views/products/edit.html.erb
  • app/views/products/show.html.erb

http://localhost:3000/products/ にアクセスします。その後、データを登録してみます。

前回、登録した商品データの価格が:defaultで指定した 0.0になっていることを確認します。(これはマイグレーションを実行した際に反映されています)

現状では、商品の表示画面では、 description のみHTMLタグをエスケープするようになっているため、HTMLタグがそのまま表示されてしまいます。ためしにRailsの便利メソッド h を削除してみます。(HTMLエスケープの実施有無の変更が簡単に行えることが分かります)

商品情報の入力値検証を追加する

データベースに登録する前に、商品データの登録内容が妥当かどうかチェックする必要があります。

例えば、タイトル(title), 説明文(description), 画像URL(image_url)が入力されているか、価格(price)が0.0ドル以上になっているかなどのチェックがあると思います。

Railsでは、Modelにてバリデーションの記述を入れます。

class Product < ActiveRecord::Base
  validates_presence_of :title, :description, :image_url
  validates_numericality_of :price
end

http://rails.rubyonrails.com/classes/ActiveRecord/Validations/ClassMethods.htmlによると、標準で用意されているバリデーションには、以下があります。

  • validates_acceptance_of
  • validates_associated
  • validates_confirmation_of
  • validates_each
  • validates_exclusion_of
  • validates_format_of
  • validates_inclusion_of
  • validates_length_of
  • validates_numericality_of
  • validates_presence_of
  • validates_size_of
  • validates_uniqueness_of

上記の場合、validates_presence_ofが必須入力チェック、validates_numericality_ofが数値チェックになります。

正しくバリデーションが行われているか確認します。まずは必須入力チェックを確認します。

次に数値チェックの確認をします。

現状のままでは、価格(price)にマイナス値を指定することができてしまうので、独自のバリデーションを app/models/product.rb に追加します。(追加する内容は以下になります)

  validate :price_must_be_at_least_a_cent

  protected
  def price_must_be_at_least_a_cent
    errors.add(:price, 'should be at least 0.01') if price.nil? || price < 0.01
  end

これは、価格(price)が未指定もしくは、0.01未満であった場合にバリデーションエラーとなることをあらわしています。

続けて、以下のバリデーションを追加します。

これらのバリデーションを app/models/product.rb に追加します。(追加する内容は以下になります)

  validates_uniqueness_of :title
  validates_format_of :image_url,
                      :with    => %r{\.(gif|jpg|png)$}i,
                      :message => 'must be a URL for GIF, JPG or PNG image.'

一覧画面の見栄えを美しくする

見栄えを美しくする前に商品データを何件か登録しておきます。

開発者にWeb画面上からデータを登録するのは、面倒なので、マイグレーションを利用してテストデータを作成します。

$ script/generate migration add_test_data
      exists  db/migrate
      create  db/migrate/20081215160124_add_test_data.rb

商品登録データは、以下をそのまま利用します。

マイグレーションを実行します。

$ rake db:migrate
(in /Users/kouichi/Development/Ruby/depot)
==  AddTestData: migrating ====================================================
==  AddTestData: migrated (0.0191s) ===========================================

で、ここから本題です。一覧をCSSを利用して見栄えを美しくします。

scaffoldによって生成されたスタイルシート(scaffold.css)は、public/stylesheets/scaffold.cssに存在します。

同じディレクトリ上にCSSファイルを作成します。(ファイル名は、depot.cssになります)

以下のページに公開されているCSSを利用します。

app/views/layouts/products.html.erb に読み込むCSSファイルへのリンク記述があるので、以下の通りに変更します。

<%= stylesheet_link_tag 'scaffold', 'depot' %>

http://localhost:3000/products/ にアクセスし、ソースファイルを見るとCSSファイルのリンクが追加されていることが分かります。

<link href="/stylesheets/depot.css" media="screen" rel="stylesheet" type="text/css" />

CSSファイルにあわせて、app/views/products/index.html.erb ファイルも変更します。これもすでに公開されているindex.html.erbを利用します。

画像ファイルもあわせてダウンロードし、public/images/ 以下に配置します。

最後に http://localhost:3000/products/ にアクセスし、見栄えが変わったことを確認します。

次回に続きます。
Rails2.2でサンプルアプリを作ってみる(3) - 積み重ねた日々 へ続きます。