Rails入門
Railsの入門ガイドです。 このガイドを読むことで、次の事が学べるはずです。
- Railsのインストールし、Railsアプリケーションのプロジェクト生成、データベース接続
- Railsアプリケーションの一般的なレイアウトについて
- MVC(Model, View, Controller)とRESTの基本的な仕組み
- Railsアプリケーションの効率的な開発
- 1. ガイドの前提
- 2. Railsとは何なのか?
- 3. 新しいRailsプロジェクトの作成
- 4. Hello, Rails!
- 5. 起動と実行
- 6. 2つ目のモデルを追加
- 7. リファクタリング
- 8. コメントの削除
- 9. セキュリティ
- 10. 次はどうする?
- 11. 設定の落とし穴
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の哲学にはいくつかの指針が含まれす。
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つの異なる環境のために区切られたセクションを含みます。
手動でデータベース構成を更新する必要はありません。
もし、アプリケーションジェネレーターのオプションを見ているのであれば、
その中に-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は、新しいアプリのためにtherubyracer
gemをGemfileにコメントアウトされた状態で追加するので、
必要ならコメントを外してください。
therubyrhino
は、JRubyユーザーに対して推奨されているランタイムで、
JRuby下で生成されたアプリケーションではデフォルトで追加されています。
ExecJSを参照して、
サポートされているすべてのランタイムについて調べることができます。
デフォルトでRuby組み込みのWEBrickというWebサーバーを起動します。 (もちろんRailsは、他のWebサーバ上でも動かすことが出来ます) アプリケーションの確認のために、 ブラウザでhttp://localhost:3000を開いてみてください。 Railsのデフォルトのインフォメーションページが表示されるはずです。
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にアクセスすると、
ルーティングエラーが発生しているはずです。
このエラーは、サーバーへのリクエストのためのコントローラー定義をルートが必要とするため、発生します。
この問題の解決は至って単純で、PostsController
という名前のコントローラーを作成することです。
下記のコマンドを実行することで、これを行います。
$ rails g controller posts
新規に作成されたapp/controllers/posts_controller.rb
を開くと、
空のコントローラーであることが確認出来ます。
class PostsController < ApplicationController
end
コントローラーはApplicationController
の継承が定義されたシンプルなクラスです。
このクラスの中で、このコントローラーの中のアクションとなるメソッドを定義することになります。
これらのアクションは、投稿(posts)のCRUDオペレーションとして実行されます。
Rubyにはpublic
、private
、protected
メソッドが存在します。
(詳細は、Programming Rubyを参照してください。)
ただし、public
メソッドだけが、コントローラーのためのアクションになることが出来ます。
ここでhttp://localhost:3000/posts/newを再読み込みすると、 下記の異なる新しいエラーが発生します。
このエラーはRailsがnew
アクションを、作成したばかりのPostsController
内から、
見つける事が出来ない事を指し示しています。
これはRailsにコントローラーが作成される際に、ジェネレーターの生成プロセスでRailsに必要なアクションを伝えないと、
デフォルトでコントローラーの中身が空になるためです。
コントローラー内にアクションを手動で定義するために必要なことは、
コントローラー内にnew
メソッドを定義する事です。
app/controllers/posts_controller.rb
を開き、PostsController
クラス内に、
次のようにnew
メソッドを定義します。
def new
end
PostsController
内にnew
メソッドが定義されたら、
http://localhost:3000/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"
非常に多くのテキストが出力されていますね! 各パーツ毎にどんな内容なのかを理解していきましょう。
この場合であれば、app/views/posts/new.html.erb
に置かれるテンプレートが最もシンプルなケースになります。
このファイル名の拡張子は、1つ目の拡張子がテンプレートのフォーマットの、
2つ目の拡張子が使用されるハンドラーの、それぞれがキーになっています。
Railsはapp/views
内のposts/new
テンプレートを見つけようと試みます。
このテンプレートのフォーマットとなり得るのはhtml
だけで、
ハンドラーはerb
、builder
、coffee
の内の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が行うことは、
現在のコントローラーであるPostsController
のcreate
アクションをフォームの送信対象とし、
そのルートに対してPOSTリクエストが送信されるようにします。
get
メソッドよりもpost
メソッドが使用される事から、
RailsはPOSTメソッドのみを受け付けるルートを定義します。
POSTメソッドは、Web全体でフォームに使用される典型的なメソッドです。
フォームとそれに関連付けられたルートの定義を使用して、 フォームを入力し、送信(submit)ボタンを押すことで新しい投稿が作成されるプロセスを始めるように出来るため、 これを行っていきましょう。 ここでフォームの送信をすると、次のようなエラーが確認出来るはずです。
これを動作させるためには、PostsController
内にcreate
アクションを作成する必要があります。
5.3 投稿(posts)の新規作成(create)
"Unknown action"を取り除くために、
app/controllers/posts_controller.rb
のPostsController
内の
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
はフォームから送られてきたパラメーター(フィールド)を表します。
prams
はActiveSupport::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の新規作成が出来るでしょうか。 試してみてください。下記のようなエラーが表示されるはずです。
Railsは、セキュアなアプリケーションを書くための手助けをしてくれる機能を持っており、
ここではその1つが実行されたと認識してください。
その1つとは、strong_parameters
と呼ばれるもので、
我々がコントローラーで受け取りたいとする必要なパラーメーターであると、Railsに伝えるものです。
このケースでは、必要とするものはtitle
とtext
パラメーターであるため、
コントローラーの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
というメソッドが確認できますか?
これは、このアクション内でのtitle
とtext
の受け取りを許可するものです。
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を開いて、試してみてください!
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
内のnew
とcreate
アクションを、
下記のようにします。
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
を追加している理由は、
そうしなければビュー上で@post
がnil
になってしまい、
@post.errors.any?
の呼び出しでエラーがスローされてしまうためです。
Railsはエラーが含まれるフィールド(input要素等)を、
自動的にfield_with_errors
クラス付きのdivで囲います。
CSSを定義することで、その箇所を目立つようにすることが可能です。
これで新規作成(new)のフォーム(http://localhost:3000/posts/new)から、 titleの無いpostを送信しようとすると、保存の際に適切なエラーメッセージが表示されるようになります。
5.12 Postの更新
これまでに、CRUDの"CR"部分までを説明してきました。 次は投稿(post)の更新、"U"部分を見ていきましょう。
最初のステップは、posts_controller
へedit
アクションを追加することです。
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) %>
すると、次のように表示されるはずです。
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
は正しいaction
とmethod
の属性を把握することが出来るため、
その状況に合ったフォーム内容を記述します。
ここで、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ファイルを通して行われます。
このファイルが無いと、確認ダイアログは表示されません。
おめでとうございます! これで投稿の新規作成(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ページ上に、CommentsController
のcreate
アクションを呼び出すことで、
新しいコメントを作成するフォームを追加しています。
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)
ヘルパーを使用して元の投稿へ、ユーザーを戻します。
既に見てきたように、これはPostsController
のshow
アクションを呼び出して、
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 %>
これで投稿とコメントがブログに追加出来るようになり、正しい場所にそれが表示されるようになりました。
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つに、スパムコメントの削除があります。
これを行うために幾つかのビューにリンクを、CommentsController
にDELETE
アクションを実装する必要があります。
まずは、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
の先頭にそれを指定します。
このケースでは、index
とshow
を除く各アクションでユーザーに認証を求めたいので、
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のベーシック認証を求められるようになります。
10. 次はどうする?
"初めてのRailsアプリケーション"の説明はここまでになります。 後は自由に改良し、自身で様々な事を試してみてください。 ただし、何の助けも無しにそれをすることは出来ないでしょう。 問題の解決やRailsの実行するための助けが必要であれば、 下記のリンク先などから探してみると良いでしょう。
- Ruby on Rails ガイド
- Ruby on Rails チュートリアル
- Ruby on Rails メーリングリスト
- irc.freenode.netの#rubyonrailsチャンネル
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つの最もありがちな原因:
© 2010 - 2017 STUDIO KINGDOM