Rails入門

このエントリーをはてなブックマークに追加

Ruby on Rails Guidesの内容を翻訳して作成したガイドです。 誤訳や誤記があると思いますのでその点についてはご了承ください。 もし、誤訳などの間違いを見つけましたら、 @tomof まで教えていただければ幸いです。

訳注:Rails4の「Getting Start」からは、scaffold(スキャフォールド)の説明がとり除かれたようです。 これは、おそらくscaffoldを使用することで、かえって初心者の理解を妨げる要因になると考えられてのことだと思われます。 scaffoldの事が知りたければ、Rails入門(Rails 3)を参照してください。

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

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

1. ガイドの前提

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

  • Rubyはバージョン1.9.3、またはそれ以降です。
  • RubyGemsパッケージシステム

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

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

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は無数の設定ファイルの細かい指定をさせるより、やりたい事・それをする方法を決めつけてしまいます。

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

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

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

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

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

3.1 Railsのインストール

コマンドラインプロンプトを開いてください。 Mac OS XであればTerminal.appを、Windowであればスタートメニューから"実行"を選び、"cmd.exe"を打ち込んでください。 $から始まるコマンドは、コマンドライン上で実行されるべきものになります。 では、インストールされているRubyの現在のバージョンを確認してみてください。

$ ruby -v
ruby 1.9.3p385

Railsをインストールするために、RubyGemsによって提供されるgem installコマンドを使用します。

$ gem install rails

RubyとRailsをシステム上へ素早くインストールする手助けをしてくれるツールが存在します。 WindowsユーザーであればRails Installerを、 Mac OS X ユーザーにはRails One Clickを使用することが出来ます。

下記を実行することで、正しくインストールされている事を確かめる事が出来ます。

$ rails --version

"Rails 4.0.0"のように表示されたのであれば、次に進む準備が整ったことになります。

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

Railsには、特定のタスクを始める際に必須となるものを全て生成してしまうことで、 開発作業を効率化してくれるジェネレーターと呼ばれるスクリプトが存在します。 これらの1つに新しいアプリケーションのジェネレーターがあり、 自分自身で書く必要なく、新しいRailsアプリケーション基盤を提供してくれます。

このジェネレーターを使用するにはターミナルを開き、 ファイルを作成したいディレクトリに移動して次のように打ち込みます。

$ rails new blog

これは、blogというディレクトリ内にBlogという名のRailsアプリケーションを生成し、 bundle installを使用してGemfile内に既に書かれている依存関係にあるgemのインストールを行います。

rails new -hを実行することで、 Railsアプリケーションを生成する際に使用できる全てのコマンドラインのオプションを確認することが出来ます。

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

$ cd blog

上記で実行したrails new blogコマンドは、blogフォルダ内に下記のRails構造のフォルダ群を生成します。 blogディレクトリは、Railsアプリケーションを構成する自動生成されたファイルとフォルダを含みます。 チュートリアルのほとんどは、app/フォルダ内で行われますが、 Railsがデフォルトで生成する各ファイルとフォルダについて、ここで説明しておきます。

ファイル/フォルダ 用途
app/ アプリケーションのコントローラー(Controllers)、モデル(Models)、ビュー(Views)、アセット(assets)を含みます。 ガイドはこの後、これに焦点を絞って進めていくことになります。
bin/ アプリケーション開発を始めるrailsスクリプトや、その他の開発やアプリケーション実行に使用するスクリプトが含まれます。
config/ アプリケーションの実行ルール、経路、データベース、その他の設定を行います。 設定の詳細については、Railsの設定を参照してください。
config.ru アプリケーションを起動するのに使用するRackベースサーバのRack設定ファイルです。
db/ データベースマイグレーションや、現在のデータベースのスキーマが含まれます。
Gemfile
Gemfile.lock
これらのファイルは、Railsアプリケーションに必要とされるgem依存が何かを指定することを可能にしてくれます。 これらのファイルはBundler gemによって使用されるファイルです。 Bundlerについてのより詳細な情報については、 Bundlerのサイトを参照してください。
lib/ 拡張モジュールを格納します。
log/ アプリケーションのログファイルを格納します。
public/ 唯一の公開フォルダです。 静的なファイルとコンパイルされたアセットファイルが含まれます。
Rakefile このファイルは、コマンドラインから実行されるタスクを見つけてロードします。 タスク定義はRailsのコンポーネントを通じて定義されています。 Rakefileを変更するのではなく、アプリケーションのlib/tasksフォルダにファイルを追加することで、 独自のタスクを追加すべきです。
README.rdoc これは、アプリケーションの簡潔な取り扱いのマニュアルです。 このファイルを編集して、他の人にアプリケーションがどんなものなのか、どのようにセットアップするかなどを教えられるようにすべきです。
script/ アプリケーション構築のためのrailsを含むスクリプトが含まれ、デプロイやアプリケーションの実行などで使用されます。
test/ Unitテスト、fixtures、または他のテスト構造を含みます。 詳細は、Railsアプリケーションのテスト を参照してください。
temp/ 一時的なファイル(キャシュ、pidとセッションファイル等)を格納するフォルダです。
vendor/ 全てのサードパーティ製のコードを格納します。 典型的なRailsアプリケーションでは、ここには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アプリケーションのrootディレクトリ内で、次のコマンドを実行してサーバを起動してください。

$ rails server

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

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

rails_welcome

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

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

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

Railsに"Hello"と出力させるために、最低限のコントローラーとビューを作成する必要があります。

コントローラーの目的はアプリケーションから特定のリクエストを受け取る事です。 ルーティングはどのコントローラーがどのリクエストを受け取るのかを決定します。 各コントローラーに1つ以上のルート、異なるアクションによる異なるルートの提供といった事がよくあります。 各アクションの目的は、情報を集めてそのためのビューを提供することにあります。

ビューの目的は人が読みやすいフォーマットで、情報を表示することにあります。 作る上の重要になることは、コントローラーでは無くビューに情報を集めさせるということです。 ビューは情報を表示するだけのものにすべきです。 デフォルトではビューのテンプレートは、Rails内のリクエストサイクル内でユーザーに送信される前に変換される、 ERB(Embedded Ruby)と呼ばれる言語で書かれます。

必要とする"index"という名前のアクションを持つ"welcome"という名前の新しいコントローラーを作成するために、 下記のようにして"コントローラー"(controller)ジェネレーターを実行する必要があります。

$ rails generate controller welcome index

Railsはあなたのために、ファイルとルートを作成してくれます。

create  app/controllers/welcome_controller.rb
 route  get "welcome/index"
invoke  erb
create    app/views/welcome
create    app/views/welcome/index.html.erb
invoke  test_unit
create    test/controllers/welcome_controller_test.rb
invoke  helper
create    app/helpers/welcome_helper.rb
invoke    test_unit
create      test/helpers/welcome_helper_test.rb
invoke  assets
invoke    coffee
create      app/assets/javascripts/welcome.js.coffee
invoke    scss
create      app/assets/stylesheets/welcome.css.scss

ここで最も重要なものは、もちろんapp/controllers/welcome_controller.rbのコントローラーと、 app/views/welcome/index.html.erbのビューになります。

app/views/welcome/index.html.erbファイルを自分が使用しているエディタで開いて下さい。 ファイル内の全てのコードを削除し、それを下記の1行のコードに置き換えてください。

<h1>Hello, Rails!</h1>

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

コントローラーとビューを作成したので、Railsに"Hello Rails!"が表示させるように指示する必要があります。 このケースでは、サイトのルートURLであるhttp://localhost:3000)に、 これを表示させたいと思います。 現時点では、その場所には"Welcome Aboard"が表示されるはずです。

次にRailsに"Home"ページが何処にあるのかを伝える必要があります。

エディタでconfig/routes.rbファイルを開いて下さい。

Blog::Application.routes.draw do
  get "welcome/index"

  # The priority is based upon order of creation:
  # first created -> highest priority.
  # ...
  # You can have the root of your site routed with "root"
  # root to: "welcome#index"

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

root "welcome#index"

root "welcome#index"はRailsに、アプリケーションのルートへのリクエストを、 welcomeコントローラーのindexアクションにマッピングする事を伝え、 get "welcome/index"はRailsに、 http://localhost:3000/welcome/indexへのリクエストを、 welcomeコントローラーのindexアクションにマッピングする事を伝えます。 これはコントローラージェネレーターを実行した際にすぐに作成されます。 (rails generate controller welcome index)

ブラウザでhttp://localhost:3000にアクセスすると、 app/views/welcome/index.html.erbの"Hello, Rails!"メッセージが確認できるはずです。 実際にこれは新しいルートであるWelcomeControllerのindexアクションを通り、正しくそのビューを描画しています。

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

5. 起動と実行

ここまでで、コントローラー、アクション、ビューの作成方法を確認してきたので、 もう少しコンテンツを作成してみましょう。

ブログアプリケーションなので、ここで新しい"resource"(リソース)を作成していきます。 "resource"(リソース)とは、投稿(posts)、人々(people)、動物(animals)といった同様のオブジェクトの集合に対して使用される用語です。 あなたはリソースの項目に対して、create、read、update、destoryを作成し、 これらの操作をCRUDオペレーションとして参照する事が可能です。

Railsは標準のRESTリソースを宣言するのに使用される、resourcesメソッドを提供します。 下記は、config/routes.rbは書き方の一例になります。

Blog::Application.routes.draw do

  resources :posts

  root to: "welcome#index"
end

rake routesを実行すると、標準のRESTfulアクションのための全てのルートを参照する事が出来ます。

$ rake routes
    posts GET    /posts(.:format)          posts#index
          POST   /posts(.:format)          posts#create
 new_post GET    /posts/new(.:format)      posts#new
edit_post GET    /posts/:id/edit(.:format) posts#edit
     post GET    /posts/:id(.:format)      posts#show
          PATCH  /posts/:id(.:format)      posts#update
          PUT    /posts/:id(.:format)      posts#update
          DELETE /posts/:id(.:format)      posts#destroy
     root        /                         welcome#index

次のセクションでは、アプリケーションに新しい投稿を作成する機能とそれを参照するビューを追加します。 これはCRUDで言うところの"C"(create)と"R"(read)、作成と読込になります。 これを行うフォームは下記のようになります。

ここでは基本的な最小構成のものしか表示されませんが、これでOKです。 この後、スタイルを整えていきます。

5.1 下地の作成

まず始めに必要になる事は、アプリケーション内で新しい投稿をcreate出来るようにすることです。 これを行う場所は、/posts/newになります。 ルートは既に定義されていて、リクエストは/posts/newは到達可能ですが、 http://localhost:3000/posts/newにアクセスすると、 ルーティングエラーが発生しているはずです。

routing_error_no_controller

このエラーは、サーバーへのリクエストのためのコントローラー定義をルートが必要とするため、発生します。 この問題の解決は至って単純で、PostsControllerという名前のコントローラーを作成することです。 下記のコマンドを実行することで、これを行います。

$ rails g controller posts

新規に作成されたapp/controllers/posts_controller.rbを開くと、 空のコントローラーであることが確認出来ます。

class PostsController < ApplicationController
end

コントローラーはApplicationControllerの継承が定義されたシンプルなクラスです。 このクラスの中で、このコントローラーの中のアクションとなるメソッドを定義することになります。 これらのアクションは、投稿(posts)のCRUDオペレーションとして実行されます。

Rubyにはpublicprivateprotectedメソッドが存在します。 (詳細は、Programming Rubyを参照してください。) ただし、publicメソッドだけが、コントローラーのためのアクションになることが出来ます。

ここでhttp://localhost:3000/posts/newを再読み込みすると、 下記の異なる新しいエラーが発生します。

unknown_action_new_for_posts

このエラーはRailsがnewアクションを、作成したばかりのPostsController内から、 見つける事が出来ない事を指し示しています。 これはRailsにコントローラーが作成される際に、ジェネレーターの生成プロセスでRailsに必要なアクションを伝えないと、 デフォルトでコントローラーの中身が空になるためです。

コントローラー内にアクションを手動で定義するために必要なことは、 コントローラー内にnewメソッドを定義する事です。 app/controllers/posts_controller.rbを開き、PostsControllerクラス内に、 次のようにnewメソッドを定義します。

def new
end

PostsController内にnewメソッドが定義されたら、 http://localhost:3000/posts/newを再読み込みしてください。 また別のエラーが表示されるはずです。

template_is_missing_posts_new

Railsはこのような素のアクションであれば、そのアクションに関連付くビューを使用して、 情報を表示しようとするため、このエラーが発生しました。 利用可能なビューが存在しなかったので、エラーが出力されてしまったのです。

上記の画像では、下部が切り取られています。 何が書かれていたか、下記に記しておきます。

Missing template posts/new, application/new with {locale:[:en], formats:[:html], handlers:[:erb, :builder, :coffee]}. Searched in: * "/path/to/blog/app/views"

非常に多くのテキストが出力されていますね! 各パーツ毎にどんな内容なのかを理解していきましょう。

1つ目のパーツ
Missing template posts/new, application/new with …

最初のパーツは、見つからないテンプレートが何なのかを示しています。 このケースでは、posts/newテンプレートがその対象になります。 Railsはまず始めにこのテンプレートを探します。 もし見つからなければ、application/newからテンプレートの読み込みを試みます。 PostsControllerApplicationControllerを継承しているため、この場所を探します。

2つ目のパーツ
 … {locale:[:en], formats:[:html], handlers:[:erb, :builder, :coffee]}. …

次はハッシュを含むメッセージのパーツです。 このハッシュの:localeキーは単に、何の言語テンプレートを取得すべきなのかを示しています。 デフォルトでは、英語-または"en"-テンプレートになります。 次のキーは:formatsで、レスポンスで提供されるフォーマットを示します。 デフォルトのフォーマットは:htmlで、そのためRailsはHTMLのテンプレートを探します。 最後のキーは:handlersで、何のテンプレートハンドラが、テンプレートの描画に使用されるのかを教えてくれます。 :erbは最も一般的なHTMLテンプレートで、:builderはXMLテンプレートで使用され、 :coffeeはCoffeeScriptを使用してJavaScriptテンプレートを構築します。

最後のパーツ
… Searched in: * "/path/to/blog/app/views"

最後のパーツのメッセージは、Railsが何処のテンプレートを探していたのかを教えてくれます。 このような基本的なRailsアプリケーションに含まれるテンプレートは、1つの場所にありますが、 より複雑なアプリケーションでは異なる複数のパスになるかもしれません。

この場合であれば、app/views/posts/new.html.erbに置かれるテンプレートが最もシンプルなケースになります。 このファイル名の拡張子は、1つ目の拡張子がテンプレートのフォーマットの、 2つ目の拡張子が使用されるハンドラーの、それぞれがキーになっています。 Railsはapp/views内のposts/newテンプレートを見つけようと試みます。 このテンプレートのフォーマットとなり得るのはhtmlだけで、 ハンドラーはerbbuildercoffeeの内の1つでなければいけません。 新しいHTMLフォームを作成したいため、ERBを使用することになります。 そのため、ファイルはposts/new.html.erbにすべきで、 アプリケーションのapp/views内に置かなければいけません。

それでは新しいファイルであるapp/views/posts/new.html.erbを作成し、 この内容を書いてみましょう。

<h1>New Post</h1>

http://localhost:3000/posts/newを再読み込みすると、 このタイトルがページ上で確認できるはずです。 ルート、コントローラー、アクション、ビューが上手く調和して動作していますね! それでは、投稿(post)を新規登録するためのフォームを作成していきましょう。

5.2 最初のフォーム

このテンプレート内にフォームを作成するために、フォームビルダー(form builder)を使用します。 Railsのフォームビルダーは、form_forと呼ばれるヘルパーによって提供されます。 このメソッドを使用するために、下記のコードをapp/views/posts/new.html.erb内に追加します。

<%= form_for :post do |f| %>
  <p>
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  </p>

  <p>
    <%= f.label :text %><br>
    <%= f.text_area :text %>
  </p>

  <p>
    <%= f.submit %>
  </p>
<% end %>

ページを再読込すると、先ほどの図の例と同じフォームが表示されるはずです。 このようにRailsでフォームを構築するのは、とても簡単です!

form_forを呼び出す際に、このフォームを識別するためのオブジェクトを渡します。 このケースでは、:postシンボルがそれに該当します。 これは、form_forヘルパーにこのフォームが何であるかを伝えます。 このヘルパーメソッドのブロック内、fとして表されているFormBuilderオブジェクトは、 それぞれタイトルと投稿のテキスト入力欄のための、2つのラベルと2つのテキストフィールドを構築するのに使用されています。 最後に、fオブジェクト上でsubmitが呼び出され、 このフォームのsubmitボタンが作成されます。

ただし、このフォームには1つ問題があります。 生成されたHTMLをページのソースビューで解析すると、 フォームのaction属性が/posts/newになっている事が確認できるはずです。 何故問題なのかというと、それは今まさにあなたが開いているページであり、 このルートは新しい投稿のためのフォームを表示するためだけに使用されるべきだからです。

このフォームは別の場所を指定するための、異なるURLを使用する必要があります。 これはform_for:urlオプションを使用することで、 簡単に行う事が可能です。 一般的にRailsでは、このような新規作成のフォーム送信で使用されるアクションの事を"create"と名付け、 フォームはそのアクションを対象とすべきです。

app/views/posts/new.html.erb内のform_forの行を次のように編集します。

<%= form_for :post, url: posts_path do |f| %>

この例では、posts_pathヘルパーには:urlオプションが渡されています。 ここでRailsが行うことは、 現在のコントローラーであるPostsControllercreateアクションをフォームの送信対象とし、 そのルートに対してPOSTリクエストが送信されるようにします。

getメソッドよりもpostメソッドが使用される事から、 RailsはPOSTメソッドのみを受け付けるルートを定義します。 POSTメソッドは、Web全体でフォームに使用される典型的なメソッドです。

フォームとそれに関連付けられたルートの定義を使用して、 フォームを入力し、送信(submit)ボタンを押すことで新しい投稿が作成されるプロセスを始めるように出来るため、 これを行っていきましょう。 ここでフォームの送信をすると、次のようなエラーが確認出来るはずです。

unknown_action_create_for_posts

これを動作させるためには、PostsController内にcreateアクションを作成する必要があります。

5.3 投稿(posts)の新規作成(create)

"Unknown action"を取り除くために、 app/controllers/posts_controller.rbPostsController内の newアクションのすぐ下にcreateアクションを定義します。

class PostsController < ApplicationController
  def new
  end

  def create
  end
end

もし、ここで再度フォームの送信を行うと、 "a template is missing"といったテンプレートが見つからないという内容の別のエラーが表示されるはずです。 現時点では、これで問題ありません。 今のところは、無視してください。 createアクションがすべき事は、、データベースに新しい投稿を保存することです。

フォームが送信された際に、フォームのフィールドはRailsのパラメーターとして送信されます。 これらのパラメーターは、その後コントローラーのアクション内で参照され、 特定のタスクに使用されます。 これらのパラメーターを確認するために、次のようにcreateアクションを変更してください。

def create
  render text: params[:post].inspect
end

ここでのrenderメソッドは、 非常にシンプルでparams[:post].inspect(paramsハッシュの:postキーの値)の内容をtextで描画します。 paramsはフォームから送られてきたパラメーター(フィールド)を表します。 pramsActiveSupport::HashWithIndifferentAccessオブジェクトを返し、 これは文字列またはシンボルのどちらを使用してもハッシュのキーとしてアクセスすることを可能にしてくれます。 ここで重要になるのは、フォームから送信されてきたパラメーターになります。

再度フォームから送信をすると、テンプレートが存在しないといった内容のエラーはもう表示されないはずです。 代わりに、次のような内容が表示されるはずです。

{"title"=>"First post!", "text"=>"This is my first post."}

このアクションは現時点では、その時フォームから送られたPOSTのパラメーターを表示するようになっています。 ただし、これでは全く現実的ではありません。 パラメーターを確認することは出来ましたが、これを使って特に何かをしているわけではありません。

5.4 Postモデルの新規作成

Railsのモデル(Model)は単数形の名称を使用し、その対となるデータベースのテーブル名には複数形の名称を使用します。 Railsはモデルを生成するためのジェネレーターを提供し、多くのRails開発者は新しいモデルを作成する際にこれを使用します。 新しいモデルを作成するために、ターミナルからこのコマンドを実行してみてください。

$ rails generate model Post title:string text:text

このコマンドはRailsに、文字列型のtitle属性とテキスト型のtext属性付きの、 Postモデルが欲しいという事を伝えます。 これらの属性は自動的にデータベース内のpostsテーブル追加され、 Postモデルにマッピングされます。

Railsはファイルの束を作成することで、これに応答します。 ただし、ここではapp/models/post.rbと、 db/migrate/20120419084633_create_posts.rb(実際にあなたが作成するファイル名とは多少異なる部分があります)に注目します。 後者はデータベース構造の作成を請け負うもので、次はこのファイルについて確認していきます。

Activeレコードのカラム名とモデル属性の自動マッピングは十分に優れており、 これはActiveレコードによって自動的に行われることとして、Railsモデル内に開発者が属性を宣言する必要が無い事を意味しています。

5.5 マイグレーションの実行

先程確認した通り、rails generate modelはデータベースのマイグレーションファイルを、 db/migrateディレクトリ内に作成しました。 マイグレーションは、データベーステーブルをシンプルに作成・更新するために設計されたRubyのクラスです。 Railsはrakeコマンドを使用してマイグレーションを実行し、またデータベースに適用した後でも、 元へ戻す(undo)事を可能としています。 マイグレーションのファイル名は、作成された順に処理が行われるように、タイムスタンプを含めます。

db/migrate/20120419084633_create_posts.rbのファイル(実際のあなたのファイル名とは、少し異なる事を覚えておいてください)の中身は、 次のようになっているはずです。

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

      t.timestamps
    end
  end
end

上記のマイグレーションは、このマイグレーションを実行した際に呼び出されるchangeという名前のメソッドを作成します。 このメソッド内で定義されている処理はリバーシブル(reversible/元に戻せる)でもあり、 これは後で元に戻したい場合に、このマイグレーションで元に戻す方法をRailsが把握しているという事を意味します。 このマイグレーションを実行すると、文字列カラムとテキストカラムを持つpostsテーブルが作成されます。 また、Railsに投稿が作成・更新された時間を記録させるようにする2つのタイムスタンプのフィールドも作成します。

マイグレーションについてより詳細な情報が知りたいければ、 RailsのDBマイグレーションのガイドを参照してください。

ここで、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のようにして、明示的に環境を指定したコマンドを実行しなければいけません。

5.6 コントローラー内でのデータベース(DB)へのデータ保存

posts_controllerに戻り、作成したPostモデルを使用してDB内にデータを保存するために、 createアクションを変更する必要があります。 app/controllers/posts_controller.rbを開き、次のようにcreateアクションを変更します。

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

ここで行われている事を順に説明していきます。 各Railsのモデルはそれぞれのデータベースカラムに自動的にマッピングされたそれぞれの属性を使用して、 初期化する事が可能です。 最初の行では、まさにこれを行っています。(params[:post]には、送信された各属性の値が含まれている事を思い出してください。) 次に@post.saveは、モデルのデータベース内への保存を請け負います。 最後に、ユーザーを後ほど定義をするshowアクションへリダイレクトしています。

後ほど確認しますが、@post.saveはモデルが保存されたか、されなかったかを示す真偽値を返します。

今なら、http://localhost:3000/posts/newを開いて、 postの新規作成が出来るでしょうか。 試してみてください。下記のようなエラーが表示されるはずです。

forbidden_attributes_for_new_post

Railsは、セキュアなアプリケーションを書くための手助けをしてくれる機能を持っており、 ここではその1つが実行されたと認識してください。 その1つとは、strong_parametersと呼ばれるもので、 我々がコントローラーで受け取りたいとする必要なパラーメーターであると、Railsに伝えるものです。 このケースでは、必要とするものはtitletextパラメーターであるため、 コントローラーのcreateアクションを次のように変更します。

def create
  @post = Post.new(post_params)

  @post.save
  redirect_to @post
end

private
  def post_params
    params.require(:post).permit(:title, :text)
  end

permitというメソッドが確認できますか? これは、このアクション内でのtitletextの受け取りを許可するものです。

def post_paramsはプライベートであることに注意してください。 これは、攻撃者からのモデルの属性を不正に設定するための手動によるモデルへのハッシュ渡しを防ぐために行います。 詳しい情報については、 Railsの公式ブログ:Strong Parametersを参照してください。

5.7 Post(投稿)の表示

もし、再びフォームの送信を行うと、Railsはshowアクションが見つからないという内容のエラーを表示します。 これでは不便なので、先にshowアクションを追加しておきましょう。

rake routesの出力を確認すると、 showアクションのルートは次のようになっています。

post GET    /posts/:id(.:format)      posts#show

特別な文法である:idは、 このルートは投稿(post)のidとなる:idパラメーターが期待されるという事をRailsに伝えます。

以前と同じように、showアクションをapp/controllers/posts_controller.rb内に、 またそのビューを追加する必要があります。

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

注目すべき点が2つあります。 Post.findを使用して、対象の投稿(post)を見つけます。 また、postオブジェクトの参照を保持するためにインスタンス変数(@が頭に付く)も使用します。 Railsは全てのインスタンス変数をビューに渡してくれるため、これを行います。

では、app/views/posts/show.html.erbに下記の内容の新しいファイルを作成します。

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

<p>
  <strong>Text:</strong>
  <%= @post.text %>
</p>

この変更により、最終的に新しい投稿(post)の新規作成が出来るようになるはずです。 http://localhost:3000/posts/newを開いて、試してみてください!

show_action_for_posts

5.8 全ての投稿(post)一覧

まだ、全ての投稿(post)の一覧を表示する機能が無いので、この実装をしてみましょう。 config/routes.rbの特定のルートと、

posts GET    /posts(.:format)          posts#index

このルートのための、app/controllers/posts_controller.rbファイルのPostsController内のアクションを使用します。

def index
  @posts = Post.all
end

最後にこのアクションのためのビューは、app/views/posts/index.html.erbになります。

<h1>Listing posts</h1>

<table>
  <tr>
    <th>Title</th>
    <th>Text</th>
  </tr>

  <% @posts.each do |post| %>
    <tr>
      <td><%= post.title %></td>
      <td><%= post.text %></td>
    </tr>
  <% end %>
</table>

ここでhttp://localhost:3000/postsを開くと、 作成した全ての投稿(post)が一覧で表示されているはずです。

5.9 リンクの追加

これで、create、show、listの機能が揃いました。 では、一連のページを通してナビゲーション出来るように、リンクを追加してみましょう。

app/views/welcome/index.html.erbを開いて、次のように変更してください。

<h1>Hello, Rails!</h1>
<%= link_to "My Blog", controller: "posts" %>

link_toメソッドは、Rails組み込みのビューヘルパーの1つです。 これはテキストベースのハイパーリンクを作成します。 このケースでは、postsへのパスになります。

同様に他のビューに対してもリンクを追加します。 まず、app/views/posts/index.html.erb内の<table>タグの上に"New Post"を配置してみましょう。

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

このリンクは、投稿(post)の新規作成フォームに導いてくれます。 また、app/views/posts/new.html.erbのテンプレートに、 indexアクションに戻るためのリンクも追加するべきです。 テンプレートのフォームの下に、そのリンクを追加してみましょう。

<%= form_for :post do |f| %>
  ...
<% end %>

<%= link_to 'Back', posts_path %>

最後に、app/views/posts/show.html.erbテンプレートに同様にindexアクションに戻る別のリンクを追加して、 1つの投稿(post)の内容を閲覧している人が、戻って再び一覧を閲覧できるようにしましょう。

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

<p>
  <strong>Text:</strong>
  <%= @post.text %>
</p>
<%= link_to 'Back', posts_path %>

もし同じコントローラー内のアクションへリンクしたい場合、 :controllerオプションを指定する必要はありません。 Railsはデフォルトで、現在のコントローラーを使用します。

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

5.10 フィールドの更新

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

class Post < ActiveRecord::Base
end

このファイルを見ると何も機能が無いのではとお思いかもしれませんが、 このPostクラスはActiveRecord::Baseを継承していることに注目してください。 Activeレコードは、基本的なデータベースのCRUD(Create、Read、Update、Destroy)操作、データの検証はもちろん、 洗練された検索のサポートと複数のモデルを別のモデルに関連付ける機能を含む、Railsモデルを自由に扱う優れた機能を提供します。

5.11 検証の追加

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

class Post < ActiveRecord::Base
  validates :title, presence: true,
                    length: { minimum: 5 }
end

これらの変更は、全ての投稿(post)は必ずtitleを持ち、またそれが少なくとも5文字以上であることを保証するようにします。 Railsはモデルの様々な状況による検証を行う事が可能で、 それにはカラムのpresence(存在(必須))、uniqueness(一意)、format(フォーマット・形式)、オブジェクトの関連付けの有無などが含まれます。 検証の詳細については、 Activeレコードの検証 のガイドを参照してください。

ここでの検証により、不正なpost上で@post.saveを呼び出すと、falseが返されます。 再びapp/controllers/posts_controller.rbを開いて確認してみると、 createアクション内の@post.save呼び出しの結果のチェックをしていない事に気づくでしょう。 この状況で@post.saveが失敗した場合、ユーザーをフォームへ戻す必要があります。 これを行うために、app/controllers/posts_controller.rb内のnewcreateアクションを、 下記のようにします。

def new
  @post = Post.new
end

def create
  @post = Post.new(params[:post].permit(:title, :text))

  if @post.save
    redirect_to @post
  else
    render 'new'
  end
end

newアクションは@postと呼ばれる新しいインスタンス変数を作成していますが、 それが何故なのかはすぐに理解出来るはずです。

createアクション内に、saveがfalseを返した際にredirect_toの代わりに、 renderを使用している点に注目してください。 renderメソッドが使用されるのは、 描画の際に@postオブジェクトをnewテンプレートに返すためです。 この描画はフォーム送信されたリクエストと同じものとして処理されるのに対し、 redirect_toは別のリクエストを発行するようにブラウザに伝えます。

http://localhost:3000/posts/newを再読み込みし、 タイトルを入力しないでpostの保存をしようとすると、Railsはフォームへ戻しますが、これだけでは有用とは言えません。 ユーザーに何か間違いがあるという事を伝える必要があります。 これを行うために、エラーメッセージが確認できるようにapp/views/posts/new.html.erbを修正します。

<%= form_for :post, url: posts_path do |f| %>
  <% if @post.errors.any? %>
  <div id="error_explanation">
    <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 %>
  <p>
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  </p>

  <p>
    <%= f.label :text %><br>
    <%= f.text_area :text %>
  </p>

  <p>
    <%= f.submit %>
  </p>
<% end %>

<%= link_to 'Back', posts_path %>

いくつか変更が行われました。 @post.errors.any?を使用してエラーが無いか確認を行い、 このケースでは@post.errors.full_messagesを使用して全てのエラーのリストを表示しています。

pluralizeはその引数として、数値と文字列を受け取るRailsヘルパーです。 引数の数値が1より大きければ、引数の文字列が自動的に複数形にされます。

posts_controller内に@post = Post.newを追加している理由は、 そうしなければビュー上で@postnilになってしまい、 @post.errors.any?の呼び出しでエラーがスローされてしまうためです。

Railsはエラーが含まれるフィールド(input要素等)を、 自動的にfield_with_errorsクラス付きのdivで囲います。 CSSを定義することで、その箇所を目立つようにすることが可能です。

これで新規作成(new)のフォーム(http://localhost:3000/posts/new)から、 titleの無いpostを送信しようとすると、保存の際に適切なエラーメッセージが表示されるようになります。

form_with_errors

5.12 Postの更新

これまでに、CRUDの"CR"部分までを説明してきました。 次は投稿(post)の更新、"U"部分を見ていきましょう。

最初のステップは、posts_controllereditアクションを追加することです。

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

ビューにはpostの新規作成時に使用したものと同じフォームを含めます。 app/views/posts/edit.html.erbというファイルを、 下記のように作成します。

<h1>Editing post</h1>

<%= form_for :post, url: post_path(@post), method: :patch do |f| %>
  <% if @post.errors.any? %>
  <div id="error_explanation">
    <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 %>
  <p>
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  </p>

  <p>
    <%= f.label :text %><br>
    <%= f.text_area :text %>
  </p>

  <p>
    <%= f.submit %>
  </p>
<% end %>

<%= link_to 'Back', posts_path %>

ここでは、updateアクションのためのフォームを配置します。 updateアクションはまだ定義されていないので、 すぐにそれを行いましょう。

method: :patchオプションはRailsに対し、 このフォームはRESTプロトコルに沿ってupdateリソースのために使用される事を期待された、 PATCHのHTTPメソッドを通したsubmitを行いたい事を伝えます。

デフォルトでは、form_forヘルパーはPOST送信を行うフォームを構築します。

次はapp/controllers/posts_controller.rb内に、 updateアクションを作成する必要があります。

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

  if @post.update(params[:post].permit(:title, :text))
    redirect_to @post
  else
    render 'edit'
  end
end

新しく登場したupdateメソッドは、既に存在するレコードを更新したい際に使用され、 更新したい属性を含むハッシュを受け取ります。 また、投稿(post)の更新内容にエラーがある場合には、ユーザーをフォームに戻す必要があります。

更新のために全ての属性を渡す必要はありません。 例えば、@post.update(title: 'A new title')を呼び出したとすると、title属性だけが更新され、 その他の属性については何も行われません。

最後に、全ての投稿(post)の一覧内に、editアクションへのリンクを表示したいので、 app/views/posts/index.html.erbの"Show"リンクの隣に追加してみましょう。

<table>
  <tr>
    <th>Title</th>
    <th>Text</th>
    <th></th>
    <th></th>
  </tr>

<% @posts.each do |post| %>
  <tr>
    <td><%= post.title %></td>
    <td><%= post.text %></td>
    <td><%= link_to 'Show', post %></td>
    <td><%= link_to 'Edit', edit_post_path(post) %></td>
  </tr>
<% end %>
</table>

app/views/posts/show.html.erbテンプレートにも同じように、 "Edit"リンクを追加します。 これをテンプレートの下部に追加します。

...

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

すると、次のように表示されるはずです。

index_action_with_edit_link

5.13 ビューの重複部分の除去

editページはnewページと非常に似ており、 実際にそれらはお互いにフォームを表示するための同じコードを共有しています。 ビューの重複部分を削除しましょう。 慣習に従い、部分(partial)ファイルは接頭辞としてアンダースコアを付けます。

部品(partial)の詳細については、 Railsによるレイアウトと描画を参照してください。

下記のコードを含む、新しいapp/views/posts/_form.html.erbファイルを作成してください。

<%= form_for @post do |f| %>
  <% if @post.errors.any? %>
  <div id="error_explanation">
    <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 %>
  <p>
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  </p>

  <p>
    <%= f.label :text %><br>
    <%= f.text_area :text %>
  </p>

  <p>
    <%= f.submit %>
  </p>
<% end %>

form_forの宣言を除き、全て同じ内容です。 フォーム構築時に、form_forは正しいactionmethodの属性を把握することが出来るため、 その状況に合ったフォーム内容を記述します。 ここで、app/views/posts/new.html.erbビューをこの新しい部品(partial)を使用して、 完全に書き換えてみましょう。

<h1>New post</h1>

%>= render 'form' <%

%>= link_to 'Back', posts_path <%

app/views/posts/edit.html.erbビューにも同じ事を行います。

<h1>Edit post</h1>

<%= render 'form' %>

<%= link_to 'Back', posts_path %>

5.14 Postの削除

データベースから投稿(post)を削除するCRUDの"D"部分の実装をしていきましょう。 RESTの慣習に従い、config/routes.rb内のpost削除のルート(route)を次のようにします。

DELETE /posts/:id(.:format)      posts#destroy

deleteルーティングのメソッドは、destroyリソースのルートとして使用されるべきです。 これが一般的なgetのルートのままの場合、 下記のような悪意のあるURLが作られ、拡散されてしまう可能性があります。

destroyリソースのために、このルートをdestroyアクションにマッピングして、 deleteメソッドを使用出来るようにします。 まだ存在しないdestroyアクションをapp/controllers/posts_controller.rb内に、 下記のように追加してください。

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

  redirect_to posts_path
end

データベースからレコードの削除をしたい際には、 Activeレコードオブジェクト上のdestroyを呼び出すことが可能です。 indexアクションにリダイレクトさせるため、このアクションのためのビューの追加は不要であることに注意してください。

最後に、'destroy'リンクをindexアクションのテンプレート(app/views/posts/index.html.erb)に追加します。

<h1>Listing Posts</h1>
<%= link_to 'New post', new_post_path %>
<table>
  <tr>
    <th>Title</th>
    <th>Text</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

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

ここでlink_toが、普段と異なる使い方をされています。 1つ目の引数にルート名を、その他の引数の最後には2つのキーが指定されています。 :method:'data-confirm'オプションはHTML5の属性として使用され、 リンクがクリックされた際にRailsは始めに確認ダイアログをユーザーに対して表示し、次にdeleteメソッドのリンクの送信を行います。 これはアプリケーション生成時に自動的にアプリケーションのレイアウト(app/views/layouts/application.html.erb)に含まれる、 jquery_ujsのJavaScriptファイルを通して行われます。 このファイルが無いと、確認ダイアログは表示されません。

confirm_dialog

おめでとうございます! これで投稿の新規作成(create)、表示(show)、一覧(list)、更新(update)、削除(destroy)の一通りの機能が揃いました。

通常Railsは、ルートを手動で宣言した箇所でのリソースオブジェクトの使用を推奨しています。 ルーティングの詳細については、Railsのルーティングを参照してください。

6. 2つ目のモデルを追加

次はアプリケーションに2つ目のモデルを追加してみましょう。 2つ目のモデルは、投稿(post)のコメント(comment)になります。

6.1 モデルの生成

以前Postモデルを作成した際に使用したものと同じジェネレーターを使用します。 投稿のコメントが参照出来るようにCommentモデルを作成してみましょう。 ターミナル上で下記のコマンドを実行して下さい。

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

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

ファイル 用途
db/migrate/20100207235629_create_comments.rb データベース内にcommentsテーブルを作成するためのマイグレーション (実際のファイル名はタイムスタンプ部分が異なります。)
app/models/comment.rb Commentモデルです。
test/models/comment_test.rb CommentモデルのためのTesting harnessです。
test/fixtures/comments.yml テストで使用されるコメントのサンプルです。

まずは、app/models/comment.rbを確認します。

class Comment < ActiveRecord::Base
  belongs_to :post
end

以前見たpost.rbのモデルと非常に似ていますが、 Activeレコードの"関連付け"を設定するbelongs_to :postの行が異なります。 次のセクションで、"関連付け"について少し説明をします。

モデルに加え、Railsはそれに対応するデータベーステーブルを作成するためのマイグレーションも生成してくれます。

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行は、このカラムのためのインデックス(index)を設定します。 それでは、このマイグレーションを実行してみましょう。

$ rake db:migrate

Railsは現在のデータベースに対して、まだ実行されていないマイグレーションだけを実行してくれます。 そのため、このケースでは次の用に表示されます。

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

6.2 モデルの関連付け

Activeレコードの関連付けは、2つのモデルの間の関係を簡単に宣言出来るようにしてくれます。 このコメント(comments)と投稿(posts)のケースでは、次のように関係を書き出すことが出来ます。

  • 各コメントは(comment)は、1つの投稿(post)に属し(belong)ます。
  • 1つの投稿(post)は、多くのコメント(comments)を持つ(have,has)事が出来ます。

実際にこれは、Railsが使用するこの関連付けの宣言の文法に、非常に近いものになっています。 既にCommentモデル(app/models/comment.rb)のコード内の行で確認したように、 各コメント(comment)が投稿(Post)に属するように指定されています。

class Comment < ActiveRecord::Base
  belongs_to :post
end

もう片方の関連付けを、app/models/post.rbを編集して追加する必要があります。

class Post < ActiveRecord::Base
  has_many :comments
  validates :title, presence: true,
                    length: { minimum: 5 }
  [...]
end

これら2つの宣言は、関連付けの便利な自動的な振る舞いを有効にしてくれます。 例えば、もし投稿の内容を含む@postのインスタンス変数を持つ場合、 @post.commentsを使用して、その投稿に属する全てのコメントを配列で取得することが出来ます。

Activeレコード関連付けの詳細については、 Activeレコードの関連付けを参照してください。

6.3 コメント(Comments)をルートに追加

コメントを見るためのナビゲーションをRailsに認識させるために、ルートに追加を行う必要があります。 再びconfig/routes.rbを開いて、下記のように編集して下さい。

resources :posts do
  resources :comments
end

これは投稿(posts)内に入れ子のリソース(resource)としてコメント(comments)を作成します。 投稿(posts)とコメント(comments)の間にある関係の階層をあわら素、もう一つの側面とも言えます。

ルーティングの詳細については、 Railsのルーティングのガイドを参照してください。

6.4 コントローラーの生成

直に手を動かしてモデル内の編集を行うと、それにマッチするコントローラーの作成に意識を向ける事が出来ます。 以前使用したジェネレーターを、再度使用してみましょう。

$ rails generate controller Comments

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

ファイル/ディレクトリ 用途
app/controllers/comments_controller.rb コメント(Comments)コントローラーです。
app/views/comments/ コントローラーのビューは、ここに格納されます。
test/controllers/comments_controller_test.rb コントローラーのためのテストです。
app/helpers/comments_helper.rb ビューのヘルパーファイルです。
test/helpers/comments_helper_test.rb ヘルパーのためのテストです。
app/assets/javascripts/comment.js.coffee コントローラーのためのCoffeeScriptです。
app/assets/stylesheets/comment.css.scss コントローラーのためのCSSです。

多くのブログがそうであるように、投稿を読む人はその投稿を読み終わった後に直接コメントを付け、 コメントが追加されると、それらのコメントが一覧として表示されているその投稿のページに戻します。 こういった傾向があるため、CommentsControllerも、そこでコメントを作成し、 またスパムのコメントがあった場合にはそれを削除する機能を提供します。

始めに投稿のshowテンプレート(app/views/posts/show.html.erb)を、 新しいコメントが作成できるようにコードを追加します。

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

<p>
  <strong>Text:</strong>
  <%= @post.text %>
</p>

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

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

これは投稿のshowページ上に、CommentsControllercreateアクションを呼び出すことで、 新しいコメントを作成するフォームを追加しています。 form_forは、ここでは配列を使用した呼び出しを行い、 /posts/1/commentsのような入れ子のルートを構築します。

app/controllers/comments_controller.rb内に、 createを作成しましょう。

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

投稿(posts)のコントローラーで行ってきたことより、多少複雑になっている事が確認できます。 これは、先程設定した入れ子の副作用と言えます。 各コメントへのリクエストは、コメント(comment)が付けられる投稿(post)への参照を保持しなければいけないため、 最初にPostモデルのfindメソッドを呼び出して、対象となる投稿(post)を取得しています。

加えてこのコードは、関連付けに利用出来る幾つかのメソッドの恩恵を受けることが出来ます。 @post.comments上でcreateメソッドを使用して、コメントの作成と保存を行います。 コメントは特定の投稿(post)へ属していることから、コメント(comment)は自動的にその投稿へ繋げられます。

新しいコメントが作成されると、post_path(@post)ヘルパーを使用して元の投稿へ、ユーザーを戻します。 既に見てきたように、これはPostsControllershowアクションを呼び出して、 show.html.erbテンプレートを描画します。 ここにコメントを表示させたいので、それをapp/views/posts/show.html.erbに追加しましょう。

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

<p>
  <strong>Text:</strong>
  <%= @post.text %>
</p>

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

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

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

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

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

post_with_comments

7. リファクタリング

ここで、投稿とコメントが動作するapp/views/posts/show.html.erbテンプレートを見てみましょう。 長くて冗長であることが確認出来ます。 これを部品・部分(partial)化して、整理することが出来ます。

7.1 コレクション部分の描画

まず、投稿への全てのコメント表示の箇所を抜き取るために、コメント(comment)を部分化します。 app/views/comments/_comment.html.erbファイルを作成し、下記のコードを書き込んで下さい。

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

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

すると、app/views/posts/show.html.erbを次のように変更することが出来ます。

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

<p>
  <strong>Text:</strong>
  <%= @post.text %>
</p>

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

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

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

これでapp/views/comments/_comment.html.erb内の@post.commentsコレクションに含まれる各コメントごとに、 部品化したテンプレートが描画されるようになります。 renderメソッドは@post.commentsコレクションを繰り返し、 各コメント毎の部品(partial)に対して、同じ名前のローカル変数を割り当てます。 このケースでは、部品(partial)テンプレートである_comment.html.erbの、 commentが割り当てられた変数として利用可能になります。

7.2 部品(partial)フォームの描画

新しく投稿するコメント部分についても、それ独自の部品(partial)として切り出してみましょう。 下記の含むapp/views/comments/_form.html.erbファイルを作成してください。

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

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

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

<p>
  <strong>Text:</strong>
  <%= @post.text %>
</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 %>

2つ目のrenderは、描画したいcomments/formの部品テンプレートを定義しているに過ぎません。 Railsは文字列内のスラッシュ(/)前方を検知し、開発者が描画して欲しい_form.html.erbファイルが、 app/views/comments内にあることを認識します。

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

8. コメントの削除

他にブログで重要になる機能の1つに、スパムコメントの削除があります。 これを行うために幾つかのビューにリンクを、CommentsControllerDELETEアクションを実装する必要があります。

まずは、app/views/comments/_comment.html.erbの部品(partial)テンプレートにdeleteリンクを追加しましょう。

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

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

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

この新しい"Destroy Comment"リンクをクリックすると、 CommentsControllerへのDELETE /posts/:post_id/comments/:idが実行され、 削除したいコメントを見つけるためにその情報が使用されます。 destroyアクションをコントローラー(app/controllers/comments_controller.rb)に追加してみましょう。

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)を探し(find)、 更に@post.commentsコレクション内から対象のコメントを探し(find)ます。 次にそれをデータベースから削除し、showアクションに戻します。

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

投稿(post)を削除した際に、それに関連付くコメント(comments)もまた削除される必要があります。 そうしなければ、残されたコメントがデータベースの容量を占有することになります。 これを行うために、Railsは関連付けのdependent(依存)オプションを提供してくれます。 Postモデルのファイルapp/models/post.rbを下記のように修正してください。

class Post < ActiveRecord::Base
  has_many :comments, dependent: :destroy
  validates :title, presence: true,
                    length: { minimum: 5 }
  [...]
end

9. セキュリティ

ブログをオンライン上に公開すると、誰でも投稿の追加、編集、削除が、またはコメントを削除出来るようになってしまいます。

Railsは、この状況で上手く動作するとてもシンプルなHTTP認証システムを提供してくれます。

PostsController内で、認証されていない人からの様々なアクションをブロックする必要があります。 認証された人からのリクエストのみを許可するために、Railsのhttp_basic_authenticate_withメソッドを使用することが可能です。

認証システムのために、PostsControllerの先頭にそれを指定します。 このケースでは、indexshowを除く各アクションでユーザーに認証を求めたいので、 app/controllers/posts_controller.rbにそれを指定します。

class PostsController < ApplicationController

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

  def index
    @posts = Post.all
  end

  # 簡潔化のため、省略

また、コメント(comments)の削除も認証されたユーザーにだけ許可したいので、 CommentsController(app/controllers/comments_controller.rb)にも、 下記のように書き込みます。

class CommentsController < ApplicationController

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

  def create
    @post = Post.find(params[:post_id])
    ...
  end
  # 簡潔化のため、省略

これで、新しい投稿を作成しようとすると、HTTPのベーシック認証を求められるようになります。

challenge

10. 次はどうする?

"初めてのRailsアプリケーション"の説明はここまでになります。 後は自由に改良し、自身で様々な事を試してみてください。 ただし、何の助けも無しにそれをすることは出来ないでしょう。 問題の解決やRailsの実行するための助けが必要であれば、 下記のリンク先などから探してみると良いでしょう。

Railsには組み込みのヘルプも存在し、rakeのコマンドを使用して作成することが出来ます。

  • rake doc:guidesを実行すると、アプリケーションのdoc/guides内にRailsガイドの完全なコピーが出力されます。 Webブラウザでdoc/guides/index.htmlを開き、そのガイドを調べてみてください。
  • rake doc:railsを実行すると、アプリケーションのdoc/api内にRailsのAPIドキュメントの完全なコピーが出力されます。 Webブラウザでdoc/api/index.htmlを開き、そのAPIドキュメントを調べてみてください。

Railsガイドをdoc:guidesのrakeタスクを使用して生成出来るようにするには、RedClothのgemをインストールする必要があります。 これをGemfileに追加し、bundle installを実行してください。

11. 設定の落とし穴

Railsでの作業を楽にする最良な方法は、全ての外部データをUTF-8として格納することです。 これを行わないと、RubyライブラリとRailsは元のデータをUTF-8に変換することがあり、 これは常に正常に処理されるとは限らないため、全てのデータをUTF-8にすることが望ましいと言えます。

もしこの箇所で間違いがあると、よくある症状に黒いダイヤモンドがクエッションマークと共に、ブラウザ上に表示されるというものがあります。 また別の症状には、"ü"の代わりに"ü"のような文字が表示されるといったものがあります。 Railsはこれらの問題の共通の原因を和らげるための複数の内部手順を踏むため、 自動的なの検知、修復が可能です。 しかし、もし外部データがUTF-8として格納されていなければ、 この種の問題は自動的にRailsに検知されず、修復されないことがあります。

UTF-8では無いデータが生まれる2つの最もありがちな原因:

テキストエディタ:
多くのテキストエディタ(TextMateのような)は、 デフォルトではUTF-8として保存します。 もしそれを行わないテキストエディタであれば、 特殊な文字(éのような)がテンプレートに入力されると、 クエッションマーク付きのダイモンドがブラウザ内に表示されます。 これはi18nの通訳ファイルにも適用されます。 デフォルトがUTF-8では無い多くのエディタ(Dreamweaverのあるバージョンのような)の場合は、 デフォルトでUTF-8に変更するように設定を変更してください。
データベース:
Railsはデフォルトで、データベースからのデータを両者の境界上でUTF-8へ変換します。 ただし、もしデータベースが内部でUTF-8を使用していない場合、 ユーザーが入力した文字全てを格納することが出来ないかもしれません。 例えば、もしあなたのデータベースが内部でLatin-1を使用しており、 ユーザーがロシア語、ヘブライ語、日本語等の文字を入力すると、 データベース入力時に永遠に失われてしまいます。 可能な限り、データベースの内部ストレージにはUTF-8を使用してください。

 Back to top

© 2010 - 2017 STUDIO KINGDOM