Rails入門(Rails 3)

訳注:Rails3の「Getting Started with Rails」の内容です。 特別な理由がない限りは、Railsの最新のバージョンを利用することをお勧めします。

Railsの入門ガイドです。 このガイドを読むことで、次の事が学べるはずです。

  • Railsのインストールし、Railsアプリケーションのプロジェクト生成、データベース接続
  • Railsアプリケーションの一般的なレイアウトについて
  • MVC(Model, View, Controller)とRESTの基本的な仕組み
  • Railsアプリケーションの効率的な開発

Rails3.2のガイドになります。そのため、このガイドのいくつかのコードは、 他のバージョンのRailsでは動かないので注意してください。

このガイドはRails3.2のバージョンがベースとなっています。 そのため、ここで紹介するコードの幾つかは現在最新のバージョンRailsでは動作しません。

1.ガイドの前提

このガイドはRailsアプリケーションを一から始めたい初心者のために書かれています。 Railsアプリケーションの経験があることを想定していませんが、このガイドを最大限活かすために 予め必要な物をインストールしておく必要があります。

  • Rubyはバージョン1.8.7、またはそれ以降です。

    Ruby1.8.7 p248とp249はマーシャリングバグがあるため、Rails 3.0とそれ以降でクラッシュします。 Rubyエンタープライズエディションは1.8.7-2012.02でこれらのバグが修正されています。 Ruby1.9系では、1.9.1は、完全なSegmentation faultがRailsの3.0とそれ以降で発生するため使用できません。 そのため、Rails3.0またはそれ以降を使用したいのであれば、1.9.x系ならば、1.9.2を使用すると良いでしょう。

  • RubyGemsパッケージシステム
    • もし、RubyGemsの詳細を学びたいのであれば、RubyGemsユーザーガイド参照してください。
  • SQLite3データベースのインストール

RailsはRuby言語で動くWebアプリケーションフレームワークです。 もし、Rubyの経験が無いのであれば、Railsをいきなり試すのは険しい道になると思われます。 Rubyを学ぶ上で、下記のような優れたフリーの教材が存在します。

また、このガイドのサンプルコードは、RailsのGithubhttps://github.com/rails/railsリポジトリの rails/railties/guides/code/getting_startedでも利用可能です。

2.Railsとは何なのか?

このセクションでは、Railsフレームワークの背景、哲学の詳細について触れていきます。 このセクションをスキップして、後ほど読み返しても構いません。 セクション3から、初めてのRailsアプリケーションの構築が始まります。

RailsはRuby言語で書かれたWebアプリケーションを開発するためのフレームワークです。 全てのプログラマがWebアプリケーションを作り始めるにあたって、必要であろう物を仮定することによって簡単にプログラム出来るように設計されています。 他の多くの言語やフレームワークと比べ、なるべくコードを書かずに済むようになっています。 また、経験豊富なRails開発者はWebアプリケーションの開発がより楽しくなったと報告しています。

Railsは固執(?)的なソフトウェアです。 Railsは、それを行うのはこれが"ベスト"な方法であると仮定してしまい、その方法を奨励し別の方法は推奨しないように設計されています。 もし、"The Rails Way"を学ぶと、生産性が飛躍的に向上するかもしれません。 もし、Railsの開発に他の言語の古い手法を用いるのであれば、幸福な体験を得ることは難しいでしょう。

Railsの哲学にはいくつかの指針が含まれす。

DRY - "Don’t Repeat Yourself(繰り返すな)"
何度も同じコードを書くことは悪いことであることを示唆している。
Convention Over Configuration(設定より規約)
Railsは無数の設定ファイルの細かい指定をさせるより、やりたい事・それをする方法を決めつけてしまいます。
RESTはWebアプリケーションの最良なパターンである
アプリケーション周りのリソースとHTTPの動詞で構成するのが最速の方法です。

2.1 MVCアーキテクチャ

Railsのコアであるモデル、ビュー、コントローラーのアーキテクチャは MVCと呼ばれ、MVCの利点は次のとおりです。

  • ユーザーインターフェースとビジネスロジックを分離します。
  • DRYコードの保持を容易にしてくれます。
  • メンテンナンス性を良くするために、形式に沿っていないコードを明確にしてくれます。
2.1.1 Model(モデル)

モデルは、アプリケーションの情報(データ)を表し、データの操作をルール化します。 Railsでは、モデルはデータベーステーブルに一致するマッピングルールに沿って優先的に使用されます。 ほとんどの場合、データベースの各テーブルは1つのモデルと対になっています。 アプリケーションの大部分のビジネスロジックは、モデルに集約されます。

2.1.2 View(ビュー)

ビューはアプリケーションのユーザーインターフェースです。 Railsでは大抵の場合、ビューはデータ表示にのみ関連する組み込み式RubyのHTMLファイルです。 ビューは、Webブラウザまたは他のツールのために提供されたデータを扱います。

2.1.3 Controllers(コントローラー)

コントローラーは、モデルとビューの間の"糊(のり)"としての役割を提供します。 Railsでは、コントローラーはWebブラウザから送られてくるリクエストのプロセスに対して責任を持つ立場にあり、 データ取得のためモデルに問い合わせを行い、表示するためにデータをビューに渡します。

Railsのコンポーネント

Railsは多くの独自コンポーネントを持っています。 各コンポーネントについて、下記を参照してください。 もし、新たにRailsを始めるのであれば、後ほど詳細を説明しますが、 このセクションでコンポーネントについて把握しておくと良いでしょう。

例えば、Rackアプリケーションがあったとして、このガイドを読み続けるのにあたり、 それらを知る必要はありません。

  • Action Pack
    • Action Controller
    • Action Dispatch
    • Action View
  • Action Mailer
  • Active Model
  • Active Record
  • Active Resource
  • Active Support
  • Railties
2.2.1 Action Pack

Action Packは、Actionコントローラー、Actionビュー、Actionディスパッチを含む単一のGemで、"MVC"の"VC"の部分に該当します。

2.2.1.1 Action Controller

ActionコントローラーはRailsアプリケーションのコントローラーを管理するコンポーネントです。 Actionコントローラーは、Railsに送られてくるリクエストを処理し、パラメータを抜き取り、指定したアクションに遷移させます。 Actionコントローラーによって提供されるサービスには、セッション管理、テンプレートのレンダリング、およびリダイレクト管理などがあります。

2.2.1.2 Action View

ActionビューはRailsアプリケーションのビューを管理し、デフォルトではHTMLとXML出力の両方を作成することが可能です。 Actionビューは入れ子、部分テンプレート、組み込みAjaxサポートを含むテンプレートのレンダリングを管理します。 ビューテンプレートについては、レイアウトとレンダリングで更に詳しく説明しています。

2.2.1.3 Action Dispatch

Actionディスパッチは、アプリケーションまたは他のRackアプリケーションのWebリクエストのルーティングと遷移処理を扱います。 Rackアプリケーションの詳細は、Rails on Rack のガイドを参照してください。

2.2.2 Action Mailer

Actionメーラーは、メールサービスを構築するためのフレームワークです。 Actionメーラーを使って送られてくるメールの受信と処理を行ったり、 シンプルなプレーンテキストまたは柔軟なテンプレートを基にした複雑なmultipartメールを送信することが可能です。

2.2.3 Active Model

Activeモデルは、ActionパックのgemサービスとActiveレコードのような関係性がマッピングされたオブジェクトの間に 定義されたインターフェースを提供します。

Activeモデルは、必要に応じて他のORMフレームーワークを利用することを許可しています。

2.2.4 Active Record

Activeレコードは、そのRailsアプリケーション内でモデルの基盤となり、 データベースの独立、基本的なCRUD機能、優れた検索機能、相互のモデルを関連付ける機能など様々なサービスを提供します。

2.2.5 Active Resource

Activeリソースは、ビジネスオブジェクトとRESTfulなWebサービス間のマッピングを行うフレームワーク提供します。 これは、CRUDのセマンティクスでローカルオブジェクトにはWebベースのリソースをマッピングする方法を実装しています。

2.2.6 Active Support

Activeサポートは、コアコードとアプリケーションで使用される汎用クラスとRailsで使用されるRubyの標準ライブラリ拡張を豊富に備えたコレクションです。

2.2.7 Railties

Railtiesは、新しいRailsアプリケーションを構築するRailsコードのコア部分で、 異なるフレームワークとプラグインをRailsアプリケーションと紐付ける糊の役割を果たします。

2.3 REST

"Rest"は、「Representational State Transfer」の略で、RESTfulなアーキテクチャの基盤となるものです。 これは一般的に、Roy Fieldingの博士論文「Architectural Styles and the Design of Network-based Software Architectures」であるとされています。 RailsのRESTは要約すると2つの原理を読み取ることが出来ます。

  • リソースを表すために、URLのようなリソース識別子を利用します。
  • システムコンポーネント間のリソース状態の表現を転送します。

例えば、次のようなHTTPリクエスト、

DELETE /photos/17

は、photoリソースをID:17で参照、その「リソースを削除する」というアクションだと判断されます。 RESTはWebアプリケーション構築のための道理のあるスタイルであり、RailsはRESTfulの複雑さやブラウザの癖から開発者を守ってくれます。

もし、RESTについてより詳しく知りたいのであれば、下記の資料を参照してください。

3.新しいRailsプロジェクトの作成

このガイドの最良の使用方法は、それが実行されるよう各ステップに従って進めていくことです。 この例では、アプリケーション作成に必要なコードや手順は省略されていないので、 文字通りステップ毎に進めることが可能です。

完全なコードはこちらにあります。

このガイドに沿って進めていけば、とてもシンプルなBlogシステムを作成することが出来ます。 アプリケーションを構築する前に、まずはRailsをインストールしましょう。

このサンプルでは、ターミナルプロンプトで#$記号を使用しています。 もし、Windowsを使用している場合、c:\source_code>のようにみなしてください。

3.1 Railsのインストール

ほとんどのケースで、Railsをインストールする一番簡単な方法はRubyGemsであると考えられます。

通常、rootユーザーとしてこれを実行します。
# gem install rails

もし、Windows上で作業してるのであれば、Railsインストーラーを使用して、 素早くRubyとRailsをインストールすることが可能です。

全てが正しくインストールされたことを、下記を実行して確かめてください。

$ rails --version

もし、"Rails 3.2.3"のように表示されたのであれば、このまま進めてください。

3.2 Blogアプリケーションの作成

ターミナルを開き、ファイルを作成する権限のあるフォルダに移動して、下記コマンドを実行してください。

$ rails new blog

blogというディレクトリ内にBlogという名前のRailsアプリケーションが作成されます。

rails new -hを実行すると、Railsのアプリケーション生成時に使用可能な、全切り替えオプションを確認することが出来ます。

アプリケーションを生成したら、フォルダーを切り替えて作業を続けてください。

$ cd blog

rails new blogコマンドは、blogフォルダ内に下記のRails構造のフォルダ群を生成します。 チュートリアルのほとんどは、app/フォルダ内で行われますが、 Railsがデフォルトで生成する各ファイルとフォルダについて、ここで説明しておきます。

ファイル/フォルダ 用途
app/ アプリケーションのコントローラー(Controllers)、モデル(Models)、ビュー(Views)、アセット(assets)を含みます。 ガイドはこの後、このフォルダに焦点を絞って進めていくことになります。
config/ アプリケーションの実行ルール、経路、データベース、その他の設定を行います。 設定の詳細については、Configuring Rails Applicationsを参照してください。
config.ru アプリケーションを起動するのに使用するRackベースサーバのRack設定ファイルです。
db/ データベースマイグレーションや、現在のデータベースのスキーマが含まれます。
doc/ (作成中の)アプリケーションの詳細なドキュメントです。
Gemfile
Gemfile.lock
これらのファイルに、どのGemの依存関係がアプリケーションに必要とされるかを指定します。
lib/ 拡張モジュールを格納します。
log/ アプリケーションのログファイルを格納します。
public/ 唯一の公開フォルダです。 静的なファイルとコンパイルされたアセットファイルが含まれます。
Rakefile このファイルは、コマンドラインから実行されるタスクを見つけてロードします。 タスク定義はRailsのコンポーネントを通じて定義されています。 Rakefileの変更が必要な場合、それを変更するよりアプリケーションのlib/tasksフォルダにファイルを追加することで、 独自のタスクを追加すべきです。
README.rdoc これは、アプリケーションの端的な指示マニュアルです。 このファイルを編集して、他の人にアプリケーションがどんなものなのか、どのようにセットアップするかなどを教えられるようにすべきです。
script/ アプリケーション構築のためのrailsを含むスクリプトが含まれ、デプロイやアプリケーションの実行などで使用されます。
test/ Unitテスト、fixtures、または他のテスト構造を含みます。 詳細は、Railsアプリケーションのテスト を参照してください。
temp/ 一時的なファイルを格納するフォルダ
vendor/ 全てのサードパーティ製のコードを格納します。 一般的なRailsアプリケーションでは、これはRuby Gems、Railsのソースコード(任意にプロジェクト内にインストールしていれば)、 追加機能のパッケージプラグインが含まれます。

3.3 データベースの設計

ほぼすべてのRailsアプリケーションで、データベースが使用されます。 データベースを使用するための設定がファイル(config/database.yml)内で行われます。 新しいRailsアプリケーションでは、デフォルトのデータベースにSQLite3が設定されていることを確認できると思います。 このファイルは、Railsをデフォルトで実行する3つの異なる環境のために区切られたセクションを含みます。

development
development(開発)は、開発/ローカルコンピューター上で、アプリケーションを直接編集されるのに使用されます。
test
test(テスト)は、自動テストを実行するのに使用される環境です。
production
production(製品)は、アプリケーションをデプロイ、公開するために使用される環境です。

手動でデータベース構成を更新する必要はありません。 もし、アプリケーションジェネレーターのオプションを見ているのであれば、 その中に-databaseというオプションがあることに気づいたかもしれません。 このオプションによって、よく使われるリレーショナル・データベースからアダプターとするものを選択することが出来ます。
もう一度、cd .. && rails new blog —database=mysqlとして、ジェネレーターを実行するが出来ます。 config/database.ymlファイルが上書きされ、SQLiteの代わりにMySQLが設定されていることを確認出来ます。 一般的なデータベース接続の詳細な例は下記の通りです。

3.3.1 SQLite3データベースの設定

Railsは組み込みでSQLite3をサポートしており、これはサーバを必要としない軽量なデータベースアプリケーションでうs. SQLiteではアクセスの多いproduction環境では負荷が大きいため、developmentとtest環境で試すのが良いでしょう。 新しいプロジェクトを作成した際にデフォルトでSQLiteデータベースが使用されますが、いつでも後ほど変更することが出来ます。

これはデフォルトの設定ファイル(config/database.yml)の、development環境用の接続情報セクションです。

development:
  adapter: sqlite3
  database: db/development.sqlite3
  pool: 5
  timeout: 5000

このガイドでは、データストレージとしてSQLite3を使用します。 また、RailsはMySQLとPostgreSQL(“out of the box”?)をサポートし、 多くのデータベースシステムのためのプラグインを持ちます。

3.3.2 MySQLデータベースの設定

SQLiteの代わりにMyQLを選ぶ場合、config/database.ymlは少し異なってきます。 これは、その場合のdevelopmentセクションです。

development:
  adapter: mysql2
  encoding: utf8
  database: blog_development
  pool: 5
  username: root
  password:
  socket: /tmp/mysql.sock

もし、開発用のコンピューターのMySQLがrootユーザーの空のパスワードでインストールされたのであれば、この設定で動作するはずです。 そうでなければ、usernameとpasswordを適した内容に変更してください。

3.3.3 PostgreSQLデータベースの設定

もし、PostgreSQLを選択したのであれば、config/database.ymlは次のように変更します。

development:
  adapter: postgresql
  encoding: unicode
  database: blog_development
  pool: 5
  username: blog
  password:
3.3.4 JRubyプラットフォーム用のSQLite3データベースの設定

もし、SQLite3とJRubyを使用しているのであれば、config/database.ymlは次のように変更されます。

development:
  adapter: jdbcsqlite3
  database: db/development.sqlite3
3.3.5 JRubyプラットフォーム用のMySQLデータベースの設定

もし、MySQLとJRubyを使用しているのであれば、config/database.ymlは次のように変更されます。

development:
  adapter: jdbcmysql
  database: blog_development
  username: root
  password:
3.3.6 JRubyプラットフォーム用のPostgreSQLデータベースの設定

もし、PostgreSQLとJRubyを使用しているのであれば、config/database.ymlは次のように変更されます。

development:
  adapter: jdbcpostgresql
  encoding: unicode
  database: blog_development
  username: blog
  password:

各usernameとpasswordは状況に応じて適したものを設定してください。

3.4 データベースの作成

データベースの設定が出来たので、Railsで空のデータベースを作成してみましょう。 下記のrakeコマンドを実行してください。

$ rake db:create

db/フォルダに開発とテスト用のSQLite3のデータベースが作成されます。

Rakeは、Railsの多くの一般的な用途で使用されるコマンドランナーです。 アプリケーション内で利用可能なrakeコマンドのリストは、rake -Tで確認することが出来ます。

4.Hello, Rails!

新しい言語を始める際に行う伝統的なことの一つに、スクリーンにテキストを表示させることがあります。 ここでもそれを行うため、まずRailsアプリケーションのサーバを動かす必要があります。

4.1 Webサーバの起動

実はもう既にRailsアプリケーションとして機能します。 それを確認するためには、Webサーバを開発マシンで起動する必要があります。 次のコマンドを実行して、サーバを起動してください。

$ rails server

CoffeeScriptをJavaScriptにコンパイルするのにはJavaScriptランタイムが必要であり、ランタイムが無いとexecjsエラーが発生します。 通常、Mac OS XとWindowsはJavaScriptランタイムはインストールされています。 Railsは、新しいアプリのためにtherubyracergemをGemfileにコメントアウトされた状態で追加するので、必要ならコメントを外してください。 therubyrhinoは、JRubyユーザーに対して推奨されているランタイムで、JRuby下で生成されたアプリケーションではデフォルトで追加されています。 ExecJSを参照して、サポートされているすべてのランタイムについて調べることができます。

デフォルトでWEBrickのWebサーバーインスタンスを起動します。(Railsは、他のWebサーバも使用することが出来ます) アプリケーションを確認するためにブラウザでhttp://localhost:3000を開いてみてください。 Railsのデフォルトのインフォメーションページが表示されるはずです。

rails_welcome

Webサーバを止めるには、Ctrl+Cを実行中のターミナルウインドウから叩きます。 一般的にdevelopmentモードでは、Railsはサーバを止める必要はありません。 ファイルの内容を変更した場合、サーバは自動的にそれを反映します。 ( ただし、config/initializersなどサーバを再起動しないと反映されないものもあります)

"Welcomeページ"は、新しいRailsアプリケーションのスモークテスト用のものです。 アプリケーションの設定が十分かをこれで確認します。 あmた、このページのリンクからアプリケーションの環境の概要を参照することが出来ます。

4.2 Railsに“Hello”と出力させる

Railsに"Hello"と出力させるために、最低限のコントローラーとビューを作成する必要があります。 幸いにも、それを一つのコマンドで行うことが出来ます。ターミナルから下記のコマンドを実行してください。

$ rails generate controller home index

もし、このコマンドを実行して「command not found」のエラーになってしまった場合、 RubyにRailsコマンドのパスを明確に指定して、実行してみてください。 ruby \path\to\your\application\script\rails generate controller home index

Railsは、app/views/home/index.html.erbを含むいくつかのファイルを作成します。 これはhomeコントローラー無いのindexアクション(メソッド)の結果を表示するのに使用されるテンプレートです。 テキストエディタでこのファイルを開き、下記の1行のコードを追記してください。

<h1>Hello, Rails!</h1>

4.3 アプリケーションのHomeページ設定

コントローラーとビューを作成したので、Railsに"Hello Rails!"が表示させるように指示する必要があります。 このガイドでは、サイトのURLルート(http://localhost:3000)で、 "Welcome"ページの代わりにこれを表示させようと思います。

まずは、"Welcome"ページのファイルを削除します。

$ rm public/index.html

Railsはpublicフォルダ内の静的ファイルを優先するため、生成したコントローラー、ビューのコンテンツを表示させるためにこれが必要になります。

次にRailsに正式なHomeページはどこなのかを教える必要があります。 config/routes.rbをエディタで開いてください。 これは、特別なDSL(ドメイン特化言語)を保持するアプリケーションの経路ファイルで、 Railsにリクエストをどのコントローラーとアクションに接続するのかを教えます。 このファイルには、経路指定した多くのサンプルがコメントアウトされており、 その中の一つにどのようにして、任意のコントローラーとアクションをサイトのルートとして接続するかを示したものがあります。 root :toで始まる行を探して、そのコメントを外してください。次のようになるはずです。

Blog::Application.routes.draw do

  #...
  # You can have the root of your site routed with "root"
  # just remember to delete public/index.html.
  root :to => "home#index"

root :to => "home#index"は、Railsにサイトルートがhomeコントローラーのindexアクションであることを指示しています。

それでは、http://localhost:3000にブラウザでアクセスしてください。 "Hello, Rails!"が表示されるはずです。

Railsの経路について更に詳しく知りたければ、Routing from the Outside Inを参照してください。

5.Scaffoldingを使って素早くアプリを構築・実行

Railsのscaffolding(スキャフォールド)は、アプリケーションの部品を素早く生成する手法です。 もし、モデル、ビュー、コントローラーを単一の操作の新しいリソースとして生成したいのであれば、 スキャフォールドでそれを行うことが可能です。

6.リソースの作成

ブログアプリケーションのケースでは、Post(ブログの投稿機能)リソースのスキャフォールドの生成を行うことからスタートします。 ターミナルから下記のコマンドを実行してください。

$ rails generate scaffold Post name:string title:string content:text

スキャフォールドジェネレーターは、アプリケーション内のフォルダに沿っていくつかのファイルを生成し、 config/routes.rbを編集します。 下記は生成されたものの早見表です。

ファイル 用途
db/migrate/20100207214725_create_posts.rb データベースにpostsテーブルを作成するためのマイグレーションファイルです。 (ファイル名のタイムスタンプ部分は異なるはずです)
app/models/post.rb Postモデルです。
test/unit/post_test.rb Postモデル用のユニットテストファイルです。
test/fixtures/posts.yml テストで使用するためのpostデータのサンプルです。
config/routes.rb postsの経路情報を含めるために、編集されます。
app/controllers/posts_controller.rb Postsコントローラー
app/views/posts/index.html.erb 全てのpostを表示する一覧ページのビューです。
app/views/posts/edit.html.erb 登録されているpostを編集するためのビューです。
app/views/posts/show.html.erb 単一のpostデータを表示するためのビューです。
app/views/posts/new.html.erb postデータを新規作成するためのビューです。
app/views/posts/_form.html.erb newとeditのビューのフォームとして使用される部品で、フォームとしての全体的な外観と機能を備えています。
test/functional/posts_controller_test.rb postsコントローラーの機能テストを行うファイルです。
app/helpers/posts_helper.rb postのビューで使用されるヘルパー関数のファイルです。
test/unit/helpers/posts_helper_test.rb postヘルパーのためのユニットテストファイルです。
app/assets/javascripts/posts.js.coffee postsコントローラーのためのCoffeeScriptのファイルです。
app/assets/stylesheets/posts.css.scss possコントローラーのためのCSS(scss)ファイルです。
app/assets/stylesheets/scaffolds.css.scss スキャフォールドの見栄えを良くするためのCSS(scss)ファイルです。

スキャフォールドは便利ですが、生成されたコードがアプリケーションに完全にフィットするものでは無いはずです。 もしかしたら、このジェネレータの出力をカスタマイズしたいと思っているかもしれません。 多くのRails開発者達がスキャフォールドを避けて全て、または大部分のソースコードを一から書いていました。 しかし、Railsにおいて生成されるモデル、コントローラー、ビューまたは他のソースファイルのテンプレートをカスタマイズすることは簡単です。 詳細を知りたければ、Creating and Customizing Rails Generators & Templatesを参照してください。

6.1 Migration(マイグレーション)の実行

スキャフォールドのコマンドによって生成されるものの一つにデータベースマイグレーションのファイルがあります。 マイグレーションは、DBテーブルをシンプルに作成、変更するために設計されたRubyクラスです。 Railsはrakeコマンドを使ってマイグレーションを実行します。また、DBにマイグレーションが反映された後でもそれを取り消すことが可能です。 マイグレーションのファイル名には、それが生成された日時が確認することの出来るタイムスタンプ文字が含まれます。

もし、db/migrate/20100207214725_create_posts.rb(実際にあなたが出力するものと異なることを忘れないでください)というファイルがあるとして、 中身は次のようになっていると思います。

class CreatePosts < ActiveRecord::Migration
  def change
    create_table :posts do |t|
      t.string :name
      t.string :title
      t.text :content

      t.timestamps
    end
  end
end

上記のマイグレーションは、このマイグレーションを実行するときに呼び出されるchangeという名前のメソッドを作成します。 この定義されたメソッドは可逆的に動作することも定義されており、このマイグレーションによって変更されたことを後ほど元に戻せることをRailsは知っています。 このマイグレーションを実行すると、2つの文字列カラムと1つのテキストカラムを持つpostsテーブルが作成されます。 また、Railsがpostデータの作成・更新日時を記録する2つのタイムスタンプも作成されます。 マイグレーションについて更に詳しく知りたい場合は、Rails Database Migrationsガイドを参照してください。

これで、マイグレーションを実行するためにrakeコマンドを使用することが可能になりました。

$ rake db:migrate

Railsがこのマイグレーションを実行すると、Postsテーブルが作成されたことを次のようにして教えてくれます。

==  CreatePosts: migrating ==================================
-- create_table(:posts)
   -> 0.0019s
==  CreatePosts: migrated (0.0020s) =========================

デフォルトでdevelopment環境で作業しているため、このコマンドはconfig/database.ymlファイルのdevelopmentセクションの定義内容が適用されます。 もし、同じようなマイグレーションをproductionのように違う環境で実行したい場合、環境を次のように明確に示して実行剃る必要があります。

rake db:migrate RAILS_ENV=production

6.2 リンクの追加

投稿した内容を確認できるように、Homeページにリンクを追加します。 app/views/home/index.html.erbを開いて、次のように変更してください。

<h1>Hello, Rails!</h1>
<%= link_to "My Blog", posts_path %>

link_toメソッドは、Rails組み込みのビューヘルパーの一つです。 表示するテキストを基に、目的のページに遷移する(このケースではpostsページ)ハイパーリンクを生成します。

6.3 ブラウザによるPostの動作

これでpostを動作させる準備が整いました。 http://localhost:3000を開いて、 "My Blog"リンクをクリックしてください。

posts_index

これは、Railsがpostのindexビューをレンダリングした結果です。 データベースに投稿されたデータがまだ無いため、"New Post"リンクをクリックして新規登録をしてみましょう。 新規作成後、それを「編集」「詳細」の確認「削除」が出来ることに気づくと思います。 これら全てのロジックとそれを操作するHTMLがRailsのスキャフォールドコマンド一つで生成されるのです。

developmentモード(デフォルトの作業環境)では、Railsはブラウザのリクエストの度にアプリケーションを再読み込みするため、 Webサーバを停止・再起動する必要はありません。

おめでとうございます、これでRails(レール)に乗ることが出来ましたね。 それでは、これらがどのように動作しているのか確認していきましょう。

6.4 Model(モデル)

モデルファイルは、app/models/post.rbで、このようにシンプルです。

class Post < ActiveRecord::Base
  attr_accessible :content, :name, :title
end

このファイルに書かれていることは多くありませんが、PostクラスはActiveRecord::Baseを継承していることに注意してください。 Active Recordは、基本的なDBのCRUD(Create, Read, Update, Destroy)操作、データ検証、洗練された検索サポート、複数のモデルを関連付ける機能など Railsのモデルを自由に取り扱う強力な機能を提供します。 もう一つの重要なことは、attr_accessibleです。 これは、属性(カラム)のホワイトリストでここに指定されたものは、(update_attributesを通して)更新されることを許可します。

6.5 Validation(検証)の追加

Railsはモデルに送られたデータ検証の手助けをするメソッドを含みます。 app/models/post.rbファイルを開いて、次のように編集してください。

class Post < ActiveRecord::Base
  attr_accessible :content, :name, :title

  validates :name,  :presence => true
  validates :title, :presence => true,
                    :length => { :minimum => 5 }
end

これらの変更により、全てのpostは名前(name)と少なくとも5文字以上のタイトル(title)を持つよう確約させています。 Railsは、そのカラムが必須または一意であるか、フォーマット、関連するオブジェクトの存在、などのモデルの状態による様々な検証を行うことが可能です。 Validation(検証)の詳細は、Active Record Validations and Callbacksを参照してください。

6.6 コンソールの使用

アクションの検証には、コンソールを使用することが可能です。 コンソールはコマンドラインツールで、アプリケーション内からRubyコードの実行をさせてくれます。

$ rails console

デフォルトのコンソールでは、データベースを変更してしまいます。 代わりにrails console —sandboxとしてコンソールを開けば、変更がロールバックされるようになります。

コンソールの読み込み後、アプリケーションのモデルを次のように扱います。

>> p = Post.new(:content => "A new post")
=> #<Post id: nil, name: nil, title: nil,
     content: "A new post", created_at: nil,
     updated_at: nil>
>> p.save
=> false
>> p.errors.full_messages
=> ["Name can't be blank", "Title can't be blank", "Title is too short (minimum is 5 characters)"]

このコードは、Postインスタンスを新規に作成、DBへの保存を試みて失敗し、 保存に失敗した経緯がエラーとして表示されている状態です。

完了したら、exitと打ち込んででコンソールを終了します。

6.7 全Postの一覧

Postの一覧を表示する方法を確認するために、Railsのコードに少し触れてみましょう。 app/controllers/posts_controller.rbファイルを開き、indexアクションを見つけます。

def index
  @posts = Post.all

  respond_to do |format|
    format.html  # index.html.erb
    format.json  { render :json => @posts }
  end
end

Post.allはDBの全てのpostデータをPostレコードの配列として返し、 @posts変数にそれを格納しています。

Active Recordを検索するより詳細な情報は、Active Record Query Interfaceを参照してください。

respond_toブロックは、HTMLとJSONの両方を扱います。 http://localhost:3000/posts.jsonにブラウザでアクセスすると、 全てのpost内容が含まれたJSONが確認できると思います。 HTMlフォーマットは、app/views/posts/内からアクション名と一致するファイルを探します。 Railsはアクションから、ビューで使用可能な全てのインスタンス変数を作成します。 下記は、app/views/posts/index.html.erbです。

<h1>Listing posts</h1>

<table>
  <tr>
    <th>Name</th>
    <th>Title</th>
    <th>Content</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @posts.each do |post| %>
  <tr>
    <td><%= post.name %></td>
    <td><%= post.title %></td>
    <td><%= post.content %></td>
    <td><%= link_to 'Show', post %></td>
    <td><%= link_to 'Edit', edit_post_path(post) %></td>
    <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?',
                                     :method => :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New post', new_post_path %>

このビューは、@posts配列からその内容とリンクを繰り返し表示します。 ビューについて、下記の点に注意してください。

  • link_toは、独自の遷移を行うハイパーリンクを生成します。
  • edit_post_pathnew_post_pathは、RESTful経路の一部としてRailsが提供するヘルパーです。 異なるアクション、コントローラー毎にこれらのヘルパーがあることを覚えておいてください。

以前のRailsでは、HTMLエスケープを行うために<%= h post.name %>と書かなければいけませんでした。 Rails3以降では、この処理はデフォルトで行われるようになりました。 エスケープ無しのHTMLが必要な場合、<%= raw post.name %>と書いてください。

レンダリングプロセスの詳細を知りたければ、Layouts and Rendering in Railsを参照してください。

6.8 レイアウトのカスタマイズ

ビューはブラウザにHTMLを表示させる方法のほんの一部に過ぎません。 Railsはレイアウトの概念も持ち、ビューのためのコンテナになります。 Railsがビューを描画する際に、レイアウトのHTMLの中にビューを配置します。 以前のバージョンのRailsは、scaffoldコマンドで自動的に app/views/layouts/posts.html.erbのような指定したコントローラーのレイアウトを作成しました。 Rails3では、これが変更されました。 アプリケーションのレイアウトであるapp/views/layouts/application.html.erbは、全てのコントローラーで使用されます。 このレイアウトファイルをエディタで開き、bodyタグに下記のようにスタイルを指定します。

<!DOCTYPE html>
<html>
<head>
  <title>Blog</title>
  <%= stylesheet_link_tag "application" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body style="background: #EEEEEE;">

<%= yield %>

</body>
</html>

/postページをリロードすると、ページ背景が灰色になっていることに気づくと思います。 同じ灰色の背景が全てのビューで使用されます。

6.9 Postの新規作成

postの作成は、2つのアクションによって行われます。 まず、newアクションで空のPostオブジェクトのインスタンスを作成します。

def new
  @post = Post.new

  respond_to do |format|
    format.html  # new.html.erb
    format.json  { render :json => @post }
  end
end

new.html.erbのビューは、空のPostの状態を表示します。

<h1>New post</h1>

<%= render 'form' %>

<%= link_to 'Back', posts_path %>

<%= render 'form' %>行は、Railsにおけるpartialの初めての紹介になります。 partialは、様々な場所で再利用されるHTMLとRubyのスニペットコードです。 このケースでは、formがpostの新規作成、postの編集ページ両方で全く同じように使用され、 両ページとも名前(name)とタイトル(title)のテキストフィールドと、内容(content)のテキストエリアを持ち、 それぞれに合わせて新規作成、更新のボタンが表示されます。

views/posts/_form.html.erb fileを確認すると、次のようになっています。

\u003C%= xxx %\u003E
<%= form_for(@post) do |f| %>
  <% if @post.errors.any? %>
  <div id="errorExplanation">
    <h2><%= pluralize(@post.errors.count, "error") %> prohibited
        this post from being saved:</h2>
    <ul>
    <% @post.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :title %><br />
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :content %><br />
    <%= f.text_area :content %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

partialは、呼び出すビューに定義された全てのインスタンス変数を受け取ります。 このケースでは、コントローラーが@postに新しいPostオブジェクトを割り当て、 ビューとpartialの両方で@postとして利用可能です。

更に詳細を知りたければ、Layouts and Rendering in Railsを参照してください。

form_forブロックは、HTMLフォームを生成するのに使用されます。 このブロック内で、フォーム要素を構築するための様々なメソッドを使用します。 例えば、f.text_field :nameは、Railsにフォーム上にinput textを作成し、name属性(カラム)をそれに紐付けることを指示します。 モデルの属性に関連するメソッドのみ、これらのメソッドを使用することが出来ます。(このケースでは、name、title、content) Railsは、より簡単で指定したモデルのインスタンスにフォームが明確に紐づくことから、 直接HTMLを書くことより、form_forを使用することを推奨しています。

もし、モデルに紐付かない独自のHTMLフォームの作成が必要な場合は、form_tagメソッドを使用してください。 このメソッドは、モデルインスタンスを必要としないフォーム構築のショートカットを提供します。

ユーザーがこのフォームでPostを新規作成するボタンをクリックした場合、 ブラウザはコントローラーのcreateアクションに情報を送信します。 (Railsは、HTTPのPostリクエストであることから、createアクションを呼び出します。 これは先に述べた慣習の一つです。)

def create
  @post = Post.new(params[:post])

  respond_to do |format|
    if @post.save
      format.html  { redirect_to(@post,
                    :notice => 'Post was successfully created.') }
      format.json  { render :json => @post,
                    :status => :created, :location => @post }
    else
      format.html  { render :action => "new" }
      format.json  { render :json => @post.errors,
                    :status => :unprocessable_entity }
    end
  end
end

createアクションは、フォームから送信されRailsによってハッシュにされた情報を元に、新しいPostオブジェクトのインスタンスを作成します。 新しいpostの保存が成功した後に、ユーザーがリクエストしたフォーマット(このケースではHTML)を返します。 ユーザーを作成したpostのshowアクションにリダイレクトさせ、Postの新規作成が成功したことを表示するためのメッセージをセットします。

もし、検証エラーによってpostの保存が成功しなかった場合、コントローラーは修正してもらうためにエラーメッセージ付きで、 ユーザーをnewアクションに戻します。

“Post was successfully created.”メッセージは、Railsのflashハッシュに格納され、 別のアクションに持ち越され、リクエストに対しての情報として提供されます。 新規作成のケースでは、Railsはレコードを保存するとすぐにリダイレクトするため、 ユーザはが実際にpostのcreate処理でレンダリングされるページを見ることはありません。 flashのメッセージはリダレクト先のshowアクションで、ユーザーに“Post was successfully created.”として表示されます。

6.10 個別のPostの詳細表示

indexページにてshowリンクをクリックすると、http://localhost:3000/posts/1のようなURLへ移動します。 RailsはこのURLをshowアクションであると解釈し、1:idパラメータとして渡されます。 下記はshowアクションになります。

def show
  @post = Post.find(params[:id])

  respond_to do |format|
    format.html  # show.html.erb
    format.json  { render :json => @post }
  end
end

showアクションは、idの値からPost.findを使用して、データベース上の目的のレコードを見つけ出します。 レコードを見つけた後、app/views/posts/show.html.erbを使用してその情報を表示します。

<p id="notice"><%= notice %></p>

<p>
  <b>Name:</b>
  <%= @post.name %>
</p>

<p>
  <b>Title:</b>
  <%= @post.title %>
</p>

<p>
  <b>Content:</b>
  <%= @post.content %>
</p>


<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>

6.11 Postの編集

postの新規作成のように、postの編集も2つのプロセスで行います。 まず、特定のpostをedit_post_path(@post)によってリクエストし、 これによりeditアクションがコントール内で呼び出されます。

def edit
  @post = Post.find(params[:id])
end

リクエストされたpostの情報が見つけられた後に、Railsはedit.html.erbビューを使ってこれを表示します。

<h1>Editing post</h1>

<%= render 'form' %>

<%= link_to 'Show', @post %> |
<%= link_to 'Back', posts_path %>

newアクションで使用されていたform部分(partial)をeditアクションで再び使用します。 ただし、今回はformはPostsControllerに対しPUTアクションを行い、submitボタンは"Update Post"と表示されます。

ビューによって生成された送信フォームは、コントローラーのupdateアクションを呼び出します。

def update
  @post = Post.find(params[:id])

  respond_to do |format|
    if @post.update_attributes(params[:post])
      format.html  { redirect_to(@post,
                    :notice => 'Post was successfully updated.') }
      format.json  { head :no_content }
    else
      format.html  { render :action => "edit" }
      format.json  { render :json => @post.errors,
                    :status => :unprocessable_entity }
    end
  end
end

updateアクションでは、Railsはまずeditビューから渡された:idパラメータを使用して、 編集するレコードをDBから取得します。 update_attributesは、リクエストされたpostの値(ハッシュ)を受け取り、このレコードにそれを反映します。 この一連の更新処理が成功すれば、ユーザはpostのshowアクションにリダイレクトされます。 何かしら問題がある場合、修正を促すためeditアクションに戻されます。

6.12 Postの削除

最後に、destroyリンクをクリックすると対象のidがdestroyアクションに送られます。

def destroy
  @post = Post.find(params[:id])
  @post.destroy

  respond_to do |format|
    format.html { redirect_to posts_url }
    format.json { head :no_content }
  end
end

Active Record modelインスタンスのdestroyメソッドは、DBの一致するレコードを削除します。 完了後、表示するレコードが無いため、Railsはユーザーをコントローラーのindexアクションにリダイレクトさせます。

7.2つ目のモデルを追加

ここまでは、スキャフォルードを使うことで1つのモデルに対しての構築を行なってきました。 それでは次は2つ目のモデルをアプリケーションに追加してみましょう。 2つ目のモデルは、ブログ投稿(post)に対するコメント(comment)を扱います。

7.1 モデルの生成

Railsのモデルは唯一の名前を使用し、DBテーブルはそれに関連づいた複数系の名前を使用します。 commentsテーブルに関連付けるため、慣習に従いモデルの名前をCommentとします。 ここで、ジェネレーターを使用してスキャフォールディングを使わずに、モデルとコントローラーだけを生成します。 新しいモデルを作成するために、下記のコマンドをターミナルから実行してください。

$ rails generate model Comment commenter:string body:text post:references

このコマンドは、4つのファイルを生成します。

ファイル 用途
db/migrate/20100207235629_create_comments.rb commentsテーブルをDB内に作成するためのマイグレーションです。 (タイムスタンプ部分は実際とは異なるはずです。)
app/models/comment.rb Commentモデルです。
test/unit/comment_test.rb Commentモデルのためのユニットテストです。
test/fixtures/comments.yml テストで使用するコメントのサンプルデータです。

まずは、comment.rbを見てみましょう。

class Comment < ActiveRecord::Base
  belongs_to :post
end

これは、先程見たpost.rbのモデルに似ています。 異なる点は、belongs_to :postの行で、これはActive Recordの「関連付け」を行います。 次のセクションでは、この「関連付け」について学びます。

DBにcommentsテーブルを追加するマイグレーションファイルは下記の通りです。

class CreateComments < ActiveRecord::Migration
  def change
    create_table :comments do |t|
      t.string :commenter
      t.text :body
      t.references :post

      t.timestamps
    end

    add_index :comments, :post_id
  end
end

t.referencesは、2つのモデルを関連付けるための外部キーをカラムに設定します。 また、add_indexは、この関連付けのカラムにインデックスを適用します。 それhでは、マイグレーションを実行してください。

$ rake db:migrate

Railsは今回追加されたマイグレーションのみ実行し、既にDBに対して実行済みのものは実行しません。 そのため、下記のように追加分のみのメッセージが表示されます。

==  CreateComments: migrating ==================================
-- create_table(:comments)
   -> 0.0008s
-- add_index(:comments, :post_id)
   -> 0.0003s
==  CreateComments: migrated (0.0012s) =========================

7.2 モデルの関連付け

Active Recordの関連付けの仕組みにより、2つのモデルの関連付けを定義します。 このcommentsとpostsのケースでは、次のように関連付けを行います。

  • コメント(comment)は1つのブログ投稿(post)に所属する
  • 1つのブログ投稿(post)は、たくさんのコメント(comments)を持つことが出来る

実際に、これはRailsがこの関連付けを宣言するために使用する(英語の)構文に非常に近いです。 既に上記のCommentモデルで、各commentをpostに所属させるためのコードを確認しているはずです。

class Comment < ActiveRecord::Base
  belongs_to :post
end

post.rbファイルを編集し、もう一方の関連付けを追加する必要があります。

class Post < ActiveRecord::Base
  attr_accessible :content, :name, :title

  validates :name,  :presence => true
  validates :title, :presence => true,
                    :length => { :minimum => 5 }

  has_many :comments
end

これら2つの関連付けの定義により、自動的な便利な振る舞いが有効になります。 例えば、postのデータを持つインスタンス変数@postがあった場合、@post.commentsを使用することで、 それに所属する全てのコメントデータを取得することが出来ます。

Active Recordによる関連付けについての詳細を知りたければ、Active Record Associationsを参照してください。

7.3 コメントの経路を追加

Railsにコメントを参照する場所を知らせるために、経路を追加する必要があります。 再度config/routes.rbファイルを開いてください。 先頭の近くにpostのスキャフォールド時にジェネレーターによってresources :postsが自動的に追加されているはずなので、 次のように編集してください。

resources :posts do
  resources :comments
end

postsのリソース内に入れ子としてcommentsが作成されます。 これは、post(投稿)とcomments(コメント)の間に存在する階層関係を表しています。

経路について更に詳しく知りたければ、Rails Routing from the Outside Inを参照してください。

7.4 コントローラーの作成

モデルの次は、コントローラーの作成に着手します。 再び、次のようにしてジェネレーターでコントローラーを作成します。

$ rails generate controller Comments

6つのファイルと1つの空のディレクトリを作成します。

ファイル/ディレクトリ 用途
app/controllers/comments_controller.rb Commentsコントローラー
app/views/comments/ Commentsコントローラーのビューを格納するフォルダ
test/functional/comments_controller_test.rb コントローラーの機能テスト
app/helpers/comments_helper.rb ビューヘルパー
test/unit/helpers/comments_helper_test.rb ヘルパーのユニットテスト
app/assets/javascripts/comment.js.coffee コントローラーのCoffeeScript
app/assets/stylesheets/comment.css.scss コントローラーのスタイルシート

多くのブログのように、読み終わったpost(投稿)の後にコメントが直接出来るようにし、 コメントを追加する度に今のコメントがリスト表示された状態で投稿ページに戻るようにします。 そのため、CommentsControllerにコメント作成とスパムコメント削除のメソッドを追加します。

まず、Postのshowテンプレート(/app/views/posts/show.html.erb)に コメント投稿欄を追加します。

<p id="notice"><%= notice %></p>

<p>
  <b>Name:</b>
  <%= @post.name %>
</p>

<p>
  <b>Title:</b>
  <%= @post.title %>
</p>

<p>
  <b>Content:</b>
  <%= @post.content %>
</p>

<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
  <div class="field">
    <%= f.label :commenter %><br />
    <%= f.text_field :commenter %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |

Postのshowページに新しいコメントを作成するフォームを追加しました。 これは、CommentsControllerのCreateアクションを呼び出します。

class CommentsController < ApplicationController
  def create
    @post = Post.find(params[:post_id])
    @comment = @post.comments.create(params[:comment])
    redirect_to post_path(@post)
  end
end

設定した入れ子の副作用によって、postでのコントローラーより少し複雑になっています。 コメントに対する各リクエストは、対象の投稿(post)と紐付けないといけないため、 始めにPostモデルのfindメソッドを使って、それを見つけ出します。

また、このコードはモデルの関連付けを行ったことにより、便利な方法でメソッドを使用しています。 createメソッドを@post.commentsから呼び出して、コメントの作成と保存を行なっています。 こうすることで、自動的に指定したpostに所属するコメントになります。

新しいコメントが作成される度、post_path(@post)ヘルパーを使用してユーザーをpost(投稿)ページに戻します。 既に見てきたように、これはPostsControllerのshowアクションを呼び出し、show.html.erbテンプレートをレンダリングします。 コメントを表示させる部分が必要なので、app/views/posts/show.html.erbにそれを追加します。

<p id="notice"><%= notice %></p>

<p>
  <b>Name:</b>
  <%= @post.name %>
</p>

<p>
  <b>Title:</b>
  <%= @post.title %>
</p>

<p>
  <b>Content:</b>
  <%= @post.content %>
</p>

<h2>Comments</h2>
<% @post.comments.each do |comment| %>
  <p>
    <b>Commenter:</b>
    <%= comment.commenter %>
  </p>

  <p>
    <b>Comment:</b>
    <%= comment.body %>
  </p>
<% end %>

<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
  <div class="field">
    <%= f.label :commenter %><br />
    <%= f.text_field :commenter %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

<br />

<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |

これでpost(投稿)とコメントがブログに追加できるようになり、それらが正しい場所に表示されるようになりました。

8.リファクタリング

これでpost(投稿)とコメントが動作するようになりましたが、app/views/posts/show.html.erbのコードが長く不恰好です。 partialを使ってこれを整理しましょう。

8.1 Partialコレクションのレンダリング

まず、コメントのpartialを作成します。 下記のようにapp/views/comments/_comment.html.erbファイルを作成します。

<p>
  <b>Commenter:</b>
  <%= comment.commenter %>
</p>

<p>
  <b>Comment:</b>
  <%= comment.body %>
</p>

この際、app/views/posts/show.html.erbは次のように変更してください。

<p id="notice"><%= notice %></p>

<p>
  <b>Name:</b>
  <%= @post.name %>
</p>

<p>
  <b>Title:</b>
  <%= @post.title %>
</p>

<p>
  <b>Content:</b>
  <%= @post.content %>
</p>

<h2>Comments</h2>
<%= render @post.comments %>

<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
  <div class="field">
    <%= f.label :commenter %><br />
    <%= f.text_field :commenter %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

<br />

<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |

これで、@post.commentsコレクションの各コメント毎に、 app/views/comments/_comment.html.erbがレンダリングされて表示されます。 renderメソッドは、@post.commentsコレクションを繰り返し処理し、 各コメント(commentsコレクションの1つ1つ)は、partialと同じ名前のローカル変数として割り当てられます。

8.2 Partialフォームのレンダリング

コメントの新規作成フォームもpartialに追い出しましょう。 再度、下記のようなapp/views/comments/_form.html.erbを作成します。

<%= form_for([@post, @post.comments.build]) do |f| %>
  <div class="field">
    <%= f.label :commenter %><br />
    <%= f.text_field :commenter %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

次に、app/views/posts/show.html.erbを下記のように変更します。

<p id="notice"><%= notice %></p>

<p>
  <b>Name:</b>
  <%= @post.name %>
</p>

<p>
  <b>Title:</b>
  <%= @post.title %>
</p>

<p>
  <b>Content:</b>
  <%= @post.content %>
</p>

<h2>Comments</h2>
<%= render @post.comments %>

<h2>Add a comment:</h2>
<%= render "comments/form" %>

<br />

<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |

2つ目のrenderには、レンダリングしたいcomments/formのpartialテンプレートが定義されています。 Railsは、文字列内の前方スラッシュを見極め、app/views/commentsディレクトリ内の_form.html.erbがレンダリング対象であると判断します。

@postオブジェクトはインスタンス変数として定義されているため、 ビュー内のレンダリングされる各partialで利用可能です。

9.コメントの削除

もう一つの重要なブログの機能に、スパムコメントの削除があります。 これを行うために、ビュー内にそのためのリンクとCommentsController内に削除アクションを実装する必要があります。

まずは、削除リンクをapp/views/comments/_comment.html.erbのpartialに追加しましょう。

<p>
  <b>Commenter:</b>
  <%= comment.commenter %>
</p>

<p>
  <b>Comment:</b>
  <%= comment.body %>
</p>

<p>
  <%= link_to 'Destroy Comment', [comment.post, comment],
               :confirm => 'Are you sure?',
               :method => :delete %>
</p>

この"Destroy Comment"リンクをクリックすると、DELETE /posts/:id/comments/:idが実行されてCommentsControllerが呼び出されます。 これを使って削除したいコメントを見つることが出来るので、コントローラーにdestroyアクションを追加しましょう。

class CommentsController < ApplicationController

  def create
    @post = Post.find(params[:post_id])
    @comment = @post.comments.create(params[:comment])
    redirect_to post_path(@post)
  end

  def destroy
    @post = Post.find(params[:post_id])
    @comment = @post.comments.find(params[:id])
    @comment.destroy
    redirect_to post_path(@post)
  end

end

destroyアクションはまず対象のpostを見つけ、@post.comments collection内から対象のコメントを探し出します。 次にそのコメントをデータベースから削除し、ユーザーをpostのshowアクションに戻します。

9.1 関連付けられたオブジェクトの削除

postを削除する場合、それに紐付けられているコメントも削除する必要があります。 それをしないとDBにデータが無駄に残ることになります。 Railsは関連付けのdependentを使用することで、これを行えるようにしてくれます。 app/models/post.rbのPostモデルを次のように編集してください。

class Post < ActiveRecord::Base
  attr_accessible :content, :name, :title

  validates :name,  :presence => true
  validates :title, :presence => true,
                    :length => { :minimum => 5 }
  has_many :comments, :dependent => :destroy
end

10.セキュリティ

もし、このままブログを公開してしまうと、誰でもブログ投稿の追加、編集、削除、またはコメントの削除が出来てしまいます。

Railsは、この状況に適した非常にシンプルなHTTP認証システムを提供してくれます。

PostsController内では、認証されていない人のアクセスをブロックする必要があります。 Railsのhttp_basic_authenticate_withを使用して、そのメソッドに許可されたリクエストだけアクセス出来るようにします。

この認証システムを使うために、これをPostsControllerの先頭に指定します。 このケースでは、indexとshowのアクションを除く全てのアクションで認証を適用したいので、次のように書きます。

class PostsController < ApplicationController

  http_basic_authenticate_with :name => "dhh", :password => "secret", :except => [:index, :show]

  # GET /posts
  # GET /posts.json
  def index
    @posts = Post.all
    respond_to do |format|
# snipped for brevity

また、コメントの削除のみ認証が必要なため、CommentsControllerに次のように書き加えます。

class CommentsController < ApplicationController

  http_basic_authenticate_with :name => "dhh", :password => "secret", :only => :destroy

  def create
    @post = Post.find(params[:post_id])
# snipped for brevity

これで新しい投稿(post)をしようとすると、HTTPのBasic認証によって尋ねられるようになりました。

challenge

11.複数モデルのフォームの構築

通常のブログのもう1つの特徴として、タグ投稿の機能があります。 この機能を実装するには、1つのフォームで1つのモデルでは無く複数のモデルと連携する必要があります。 Railsは入れ子のフォーム機能もサポートします。

各投稿(post)の作成時に、フォームの内から複数のタグが付け加えられるようにする機能を追加します。 まず、タグを保持するための新しいモデルを作成します。

$ rails generate model tag name:string post:references

再びマイグレーションを実行して、DBテーブルを作成します。

$ rake db:migrate

次にpost.rbファイルを編集し、もう1つの関連付けを作成してRails(accepts_nested_attributes_forマクロによって)に、 postを通してtagを編集するつもりであることを教えます。

class Post < ActiveRecord::Base
  attr_accessible :content, :name, :title, :tags_attributes

  validates :name,  :presence => true
  validates :title, :presence => true,
                    :length => { :minimum => 5 }

  has_many :comments, :dependent => :destroy
  has_many :tags

  accepts_nested_attributes_for :tags, :allow_destroy => :true,
    :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
end

:allow_destroyオプションは、入れ子状態を通してタグ削除を有効にすることをRailsに伝えます。 ("remove"チェックボックスをビュー上に実装して、これを扱うでしょう(?)) :reject_ifオプションは、入力されていない属性があった場合に新しいタグの保存を防ぎます。

また、:tags_attributesattr_accessibleのリストに追加しなければならないことに注意してください。 これをしないと、postモデルを通してタグを更新しようとした際にMassAssignmentSecurity例外が発生します。

タグ機能を実装するために、views/posts/_form.html.erbを編集します。

<% @post.tags.build %>
<%= form_for(@post) do |post_form| %>
  <% if @post.errors.any? %>
  <div id="errorExplanation">
    <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
    <ul>
    <% @post.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
  <% end %>

  <div class="field">
    <%= post_form.label :name %><br />
    <%= post_form.text_field :name %>
  </div>
  <div class="field">
    <%= post_form.label :title %><br />
    <%= post_form.text_field :title %>
  </div>
  <div class="field">
    <%= post_form.label :content %><br />
    <%= post_form.text_area :content %>
  </div>
  <h2>Tags</h2>
  <%= render :partial => 'tags/form',
             :locals => {:form => post_form} %>
  <div class="actions">
    <%= post_form.submit %>
  </div>
<% end %>

form_for(@post) do |f|内のfpost_formに変更し、 何を表しているのかを理解しやすくしていることに注意してください。

この例では、renderヘルパーにもう一つのオプションを指定していることが確認できると思います。 このケースではローカル変数であるpost_formオブジェクトを渡すことで、partial側でそれを参照することを可能にしています。

また、@post.tags.buildをこのフォームの先頭に追加しています。 これは、新しいタグがユーザーによってその名前が入力される準備があるということを明確にします。 もし、新しいタグの構築をしないと、フォームは新しいタグオブジェクトが作成されるものとして表示されません。

次に、app/views/tagsフォルダにタグ用のフォームを含む_form.html.erbを作成します。

<%= form.fields_for :tags do |tag_form| %>
  <div class="field">
    <%= tag_form.label :name, 'Tag:' %>
    <%= tag_form.text_field :name %>
  </div>
  <% unless tag_form.object.nil? || tag_form.object.new_record? %>
    <div class="field">
      <%= tag_form.label :_destroy, 'Remove:' %>
      <%= tag_form.check_box :_destroy %>
    </div>
  <% end %>
<% end %>

最後に、app/views/posts/show.html.erbテンプレートを編集して、タグを表示するようにします。

<p id="notice"><%= notice %></p>

<p>
  <b>Name:</b>
  <%= @post.name %>
</p>

<p>
  <b>Title:</b>
  <%= @post.title %>
</p>

<p>
  <b>Content:</b>
  <%= @post.content %>
</p>

<p>
  <b>Tags:</b>
  <%= @post.tags.map { |t| t.name }.join(", ") %>
</p>

<h2>Comments</h2>
<%= render @post.comments %>

<h2>Add a comment:</h2>
<%= render "comments/form" %>


<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |

これらの変更により、同じビュー内でpost(投稿)とそのタグの編集が直接出来るようになったことを確認してください。

しかし、@post.tags.map { |t| t.name }.join(", ")が不恰好なので、 ヘルパーメソッドを使ってこれを修正します。

12.ビューヘルパー

ビューヘルパーはapp/helpers内にあり、ビューのために再利用できる小さなスニペットを提供します。 このケースでは、各オブジェクトのname属性の文字列をカンマで連結するメソッドが必要となります。 Postのshowテンプレート用であるため、PostsHelperにこれを用意することにします。

app/helpers/posts_helper.rbを開き、次のように追加します。

module PostsHelper
  def join_tags(post)
    post.tags.map { |t| t.name }.join(", ")
  end
end

app/views/posts/show.html.erbのビューも次のように編集します。

<p id="notice"><%= notice %></p>

<p>
  <b>Name:</b>
  <%= @post.name %>
</p>

<p>
  <b>Title:</b>
  <%= @post.title %>
</p>

<p>
  <b>Content:</b>
  <%= @post.content %>
</p>

<p>
  <b>Tags:</b>
  <%= join_tags(@post) %>
</p>

<h2>Comments</h2>
<%= render @post.comments %>

<h2>Add a comment:</h2>
<%= render "comments/form" %>


<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |

13.次はどうする?

ここまでで、はじめてのRailsアプリケーションについて見てきましたが、これを自身で自由に更新して試してみるべきです。 しかし、何の助けも無しにすれをする必要はありません。 Railsを始めて動かすにあたり、下記の資料も参考にしてみてください。

Railsはまた、rakeコマンドを使用することで組み込みのヘルプを作成することが出来ます。

  • rake doc:guidesを実行すると、アプリケーションのdoc/guidesフォルダにRails Guidesの完全なコピーが作成されます。 doc/guides/index.htmlをブラウザで開いてみてください。
  • rake doc:railsを実行すると、アプリケーションのdoc/apiにRailsのAPIドキュメントの完全なコピーが作成されます。 doc/api/index.htmlをブラウザで開いてみてください。

14.設定の落とし穴

外部データが全てUTF-8になっていれば、Railsで作業する際に非常に楽になります。 そうでなくて、もしRubyライブラリとRailsで手持ちのデータをUTF-8に変換することが出来ますが、 常に動く保証がないので、出来れば外部データは全てUTF-8にしておくことをお勧めします。

文字コード処理でミスがあると、よくあるケースではブラウザに疑問符の付いた黒いダイヤモンドマークが表示されてしまいます。 または、“ü”の代わりに“ü”が表示されてしまうこともあるでしょう。 Railsはいくつかの内部処理を経て、これらの問題を自動的に検出・修正して和らげようとします。 しかし、UTF-8では無い外部データを扱っている場合、時折Railsによる自動的な検出・修正が出来ず、この類の問題が発生してしまうかもしれません。

下記は、UTF-8データを取り扱うための一般的な対策・注意点です。

  • 多くのテキストエディタ(Textmateのような)は、デフォルトでUTF-8でファイルを保存します。 もし、使用しているテキストエディタがそうでなければ、テンプレートに(éのような)が入ると、ブラウザで 疑問符付きの黒いダイヤモンドが表示されてしまうかもしれません。 これは、I18n翻訳ファイルにも適用されます。 デフォルトがUTF-8ではないエディタ(あるバージョンのDreamweaverなど)は、大抵デフォルトの文字列を変更する機能がありますので、 UTF-8に変更してください。
  • RailsはデフォルトでDBのデータをUTF-8に変換します。 しかし、DBがUTF-8を内部で使用していない場合、ユーザーが入力した文字を格納できないかもしれません。 例えば、DBが内部でLatin-1を使用していた場合、ユーザーがロシア語、ヘブライ語、日本語の文字列を入力すると、 このデータはDBに入る度に永遠に失われてしまいます。 可能な限り、DB内部はUTF-8を使用するようにしてください。

 Back to top

© 2010 - 2017 STUDIO KINGDOM