今まで Java / Python / Javascript を使って開発することが多かったのですが、Ruby on Rails を使っているとの話を耳にすることが多いので、試しにチュートリアルをやってみました。
*Ruby on Rails とは
Rubyで書かれたWebアプリケーションフレームワークです。少ないコード量で簡単にプログラミングできるように設計されています。MVCアーキテクチャを採用しているため、開発効率をより高めることができます。
その他に特徴としては、オブジェクト指向でクラスの継承もできる、SQLを使わなくてもデータベースの操作ができる、テスト自動化の仕組みが元から入っている、日本語のドキュメントが多いといった特徴があります。ブログサイトやECサイトなどで使われることが多いのですが、機械学習には向いていないようです。
*参考
*環境
- MacOS
- Ruby 2.3.3
- rbenv 1.1.2
- Rails 5.2.3
*環境構築
MacにはRubyが元から入っているので、複数のRubyのバージョンを環境で切り替えれるよう、rbenv
を使います。# Rubyのバージョン確認
$ ruby -v
ruby 2.3.3p222 (2016-11-21 revision 56859) [universal.x86_64-darwin17]
# rbenvのインストール
$ brew update
$ brew install rbenv ruby-build
# rbenvを使えるよう設定
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile
$ rbenv -v
rbenv 1.1.2
rbenv を使って指定バージョンをインストールします。
# 指定バージョンをインストール
$ rbenv install 2.3.3
# バージョン確認(まだローカルのまま)
$ rbenv versions
* system
2.3.3
# バージョン切り替え
$ rbenv rehash
$ rbenv global 2.3.3
# バージョン確認(指定バージョン)
$ rbenv versions
system
* 2.3.3
$ ruby -v
ruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-darwin17]
フレームワークとして使う Rails をインストールします。
$ gem install rails
$ rails --version
Rails 5.2.3
*アプリケーション作成
ターミナルでrails new {アプリ名}
を実行すると、指定した名前のアプリケーションが作成されます。(作成された時点で Git も入っています)$ rails new blog
$ cd blog/
/blog (master #)$
作成されたディレクトリは下記構成になっています。
blog
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile ---- コマンドラインから実行するタスクの定義
├── app ---- コントローラー、モデル、ビューなど開発で中心に使う
│ ├── assets
│ ├── channels
│ ├── controllers
│ ├── helpers
│ ├── jobs
│ ├── mailers
│ ├── models
│ └── views
├── bin ---- 起動やデプロイ用のスクリプトファイル
│ ├── bundle
│ ├── rails
│ ├── rake
│ ├── setup
│ ├── spring
│ ├── update
│ └── yarn
├── config ---- DBやルーティングなどの設定ファイル
│ ├── application.rb
│ ├── boot.rb
│ ├── cable.yml
│ ├── credentials.yml.enc
│ ├── database.yml
│ ├── environment.rb
│ ├── environments
│ ├── initializers
│ ├── locales
│ ├── master.key
│ ├── puma.rb
│ ├── routes.rb
│ ├── spring.rb
│ └── storage.yml
├── config.ru ---- 起動時に必要になるRack設定ファイル
├── db ---- データベーススキーマとマイグレーションファイル
│ └── seeds.rb
├── lib ---- 拡張モジュール
│ ├── assets
│ └── tasks
├── log ---- ログファイル
├── package.json ---- npm依存関係の指定
├── public ---- 静的ファイルやコンパイル済みアセットなど
│ ├── 404.html
│ ├── 422.html
│ ├── 500.html
│ ├── apple-touch-icon-precomposed.png
│ ├── apple-touch-icon.png
│ ├── favicon.ico
│ └── robots.txt
├── storage
├── test
│ ├── application_system_test_case.rb
│ ├── controllers
│ ├── fixtures
│ ├── helpers
│ ├── integration
│ ├── mailers
│ ├── models
│ ├── system
│ └── test_helper.rb
├── tmp ---- キャッシュや一時ファイル
│ ├── cache
│ └── storage
└── vendor ---- サードパーティーによって書かれたコード
*サーバーの起動
下記コマンドを実行するとサーバーが起動します。$ rails server
下記にアクセスすると、サンプル画面が表示されます。
http://localhost:3000/
*Hello画面を作成
ジェネレータを使ってコントローラーを作成します。コマンドは
rails generate controller {コントローラー名} {アクション}
として使います。Rails ではコントローラーで定義するメソッド名をアクションと呼びます。$ rails generate controller Welcome index
コマンドを実行後、
{コントローラー名}_controller.rb
のファイルが生成されます。<app/controllers/welcome_controller.rb>
class WelcomeController < ApplicationController
def index
end
end
views ディレクトリにはコントローラー名のディレクトリ配下に、
{アクション名}.html.erb
のファイルが生成されます。<app/views/welcome/index.html.erb>
<h1>Welcome#index</h1>
<p>Find me in app/views/welcome/index.html.erb</p>
このファイルを下記に修正し、
Hello, Rails!
が画面に表示されるようにします。<h1>Hello, Rails!</h1>
ルーティングを修正します。
root
を指定することで、welcome
コントローラーのindex
アクションを実行するようにします。<config/routes.rb>
Rails.application.routes.draw do
get 'welcome/index'
# ---- ↓追加 ----
root 'welcome#index'
end
起動すると「Hello, Rails!」が表示されます。
$ rails server
*ブログアプリケーションの実装
ひとまとまりの単位をリソースと呼び、この単位ごとに CRUD の処理を実行します。ルーティングの設定にリソースを追加する必要があるので、今回は「記事」のリソースを追加しました。<config/routes.rb>
Rails.application.routes.draw do
get 'welcome/index'
# ---- ↓追加 ----
resources :articles
root 'welcome#index'
end
Rails では標準的なルーティングが自動で定義されており、下記コマンドを実行して確認することができます。
$ rails routes
Prefix Verb URI Pattern Controller#Action
welcome_index GET /welcome/index(.:format) welcome#index
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
root GET / welcome#index
rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
先ほどの
Hello
のときと同様に、ジェネレータを使ってArticles
のコントローラーを作成します。コントローラー名は複数形、頭文字を大文字にするという規則があります。
$ rails generate controller Articles
アクションを指定しなかったので、生成されたコントローラーには何も定義されていない状態になりますが、このままだとエラーになるので
new
アクションを追加します。<app/controllers/articles_controller.rb>
class ArticlesController < ApplicationController
def new
end
end
このままだと
view
ファイルがなくてエラーになるので、new.html.erb
を新規作成します。コントローラー作成時にアクションを指定すると自動生成されるので、本来であれば
rails generate controller Articles new
としてアクションを指定したほうが良さそうです。<app/views/articles/new.html.erb>
<h1>New Article</h1>
ブラウザに下記が表示されます。
*入力フォームの作成
記事を投稿するためのフォームを作成します。先ほど作成した
view
ファイルに下記を追加します。<% ~ %>
または<%= ~ %>
の中に記述されたコードは Ruby のコードとして実行されます。<%= ~ %>
は実行された結果を評価し、文字列として出力します。いっぽう<% ~ %>
は、結果を出力しない計算や繰り返し処理を記述するときに使います。-
form_with ヘルパー
Viewファイルでform_with
というヘルパーを使うことで、フォームを簡単に作成することができます。オプションを指定することができ、今回はscope:
とurl:
、local:
を指定しています。scope:
にはルーティングで名前空間を指定したときのプレフィックスを、url:
にはフォームに入力されたデータを送信するURLを、local:
にはリモートフォームを使わない設定を指定します。
-
path ヘルパー
rails route
コマンドを実行すると表示されるプレフィックス名
_path と定義することで、URL用のパスを生成してくれます。articles_path
とすると、/articles
を返します。
<h1>New Article</h1>
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
POSTリクエストとしてルーティングされるので、コントローラーに
create
アクションを作成する必要があります。現時点では試しにパラメーターが正常に渡っているかブラウザに表示して確認をします。inspect
を使うとPOSTの値を確認することができます。- render
plain:
オプションを指定することで、平文をマークアップせずにブラウザに送信することができます。
# ---- ↓追加 ----
def create
render plain: params[:article].inspect
end
ブラウザにPOSTの値が表示されます。
下記になっていれば正常に渡されています。
<ActionController::Parameters {"title"=>"test", "text"=>"aaa"} permitted: false>
*モデルの作成
ジェネレータを使ってモデルを作成します。rails generate model {モデル名} {カラム名:型}
として実行します。モデル名は単数形で頭文字を大文字するという命名規則があります。$ rails generate model Article title:string text:text
下記ファイルが生成されます。
<app/models/article.rb>
class Article < ApplicationRecord
end
<db/migrate/20190731111353_create_articles.rb>
class CreateArticles < ActiveRecord::Migration[5.2]
def change
create_table :articles do |t|
t.string :title
t.text :text
t.timestamps
end
end
end
マイグレーションを実行します。
Articles
テーブルがデータベース上に生成されます。$ rails db:migrate
== 20190731111353 CreateArticles: migrating ===================================
-- create_table(:articles)
-> 0.0023s
== 20190731111353 CreateArticles: migrated (0.0023s) ==========================
*投稿データの保存
コントローラーにデータを保存するためのcreate
アクションと、保存されたデータを表示するためのshow
アクションを追加します。-
@
@article
はインスタンス変数です。コントローラーのインスタンス変数はViewファイルから参照することができます。
-
find()
指定したモデルのインスタンスを返します。@article.title
とすることで、データの値を使うことができます。指定したIDがわかっている場合はfind()
を使いますが、特定カラムの値から取得したい場合はfind_by()
を使います。
-
new()
属性を渡すとモデルオブジェクトを生成することができます。作成したインスタンスでsave
を実行するとデータを保存することができます。
-
params
URLから送られてきた値やフォームの入力値を受け渡しするためのメソッドです。params[:パラメータ名]
として使います。
class ArticlesController < ApplicationController
# ---- ↓追加 ----
def show
@article = Article.find(params[:id])
end
def new
end
# ---- ↓追加 ----
def create
@article = Article.new(article_params)
@article.save
redirect_to @article
end
# ---- ↓追加 ----
private
def article_params
params.require(:article).permit(:title, :text)
end
end
表示用のViewファイルを新規作成します。
コントローラーのインスタンス変数からタイトルとテキストを表示しています。
<app/views/articles/show.html.erb>
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
*記事一覧を表示
既に定義されているルーティングを使い、GETするためにindex
アクションを追加します。all
を使うことでモデルの中身全てを取得することができます。<app/controllers/articles_controller.rb>
class ArticlesController < ApplicationController
# ---- ↓追加 ----
def index
@articles = Article.all
end
def show
@article = Article.find(params[:id])
end
def new
end
def create
@article = Article.new(article_params)
@article.save
redirect_to @article
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
コントローラーに対応するViewファイルを作成します。
@articles
のインスタンス変数を使って表示します。<app/views/articles/index.html.erb>
<h1>Listing articles</h1>
<table>
<tr>
<th>Title</th>
<th>Text</th>
<th colspan="3"></th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
</tr>
<% end %>
</table>
*リンクの追加
最初の Hello のページからブログ一覧画面へ遷移するためのリンクを作成します。- link_to
Viewファイルでリンクを表示するためのヘルパーメソッドです。link_to 'リンク表示の文字列', リンク先のURL
として使います。
<h1>Hello, Rails!</h1>
# ---- ↓追加 ----
<%= link_to 'My Blog', controller: 'articles' %>
http://localhost:3000一覧画面から投稿新規作成画面に遷移するリンクを作成します。ここでも
_path
ヘルパーを使っています。- path ヘルパー
rails route
コマンドを実行すると表示されるプレフィックス名
_path と定義することで、URL用のパスを生成してくれます。new_article_path
とすると、/articles/new
を返します。
<h1>Listing articles</h1>
# ---- ↓追加 ----
<%= link_to 'New article', new_article_path %>
<table>
<tr>
<th>Title</th>
<th>Text</th>
<th colspan="3"></th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
</tr>
<% end %>
</table>
投稿作成画面から一覧画面に戻るリンクを作成します。
<app/views/articles/new.html.erb>
<h1>New Article</h1>
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
# ---- ↓追加 ----
<%= link_to 'Back', articles_path %>
詳細表示画面から一覧画面に戻るリンクを作成します。
<app/views/articles/show.html.erb>
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
# ---- ↓追加 ----
<%= link_to 'Back', articles_path %>
*バリデーションの追加
モデルファイルにvalidates
を設定するとバリデーションを追加することができます。presence: true
とすることで、値が空でないことを確認しています。<app/models/article.rb>
class Article < ApplicationRecord
validates :title, presence: true, length: { minimum: 5 }
end
バリデーションエラーになると
save
をしたときにfalse
を返すようになるので、コントローラーのcreate
アクションに条件を追加します。バリデーションエラー時はrender 'new'
で Viewファイルのnew
テンプレートを呼び出し、新規作成画面を再表示します。保存に成功した場合は、一覧画面に遷移します。- redirect_to
インスタンス変数を指定すると、インスタンス変数名のshow
アクションを呼び出します。
class ArticlesController < ApplicationController
def index
@articles = Article.all
end
def show
@article = Article.find(params[:id])
end
def new
@article = Article.new
end
# ---- ↓追加 ----
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
バリデーションエラーが発生した場合は、原因がわかるように画面に内容を表示するようにします。
-
errors.any?
@article.errors.any?
とすることでエラーが発生しているかの判定をすることができます。
-
pluralize
数値を受け取って、それに応じて単数形/複数形に対応した名詞に変換します。
<h1>New Article</h1>
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
# ---- ↓修正 start ----
<% if @article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@article.errors.count, "error") %>
prohibited this article from being saved:
</h2>
<ul>
<% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
# ---- ↑修正 end ----
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
*更新機能の追加
更新するためにはedit
アクションを追加します。更新後のフォームを送信するためには
update
アクションを追加する必要があります。<app/controllers/articles_controller.rb>
class ArticlesController < ApplicationController
...
# ---- ↓追加 ----
def edit
@article = Article.find(params[:id])
end
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article
else
render 'edit'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
edit
の View ファイルは新規作成の入力フォームと同じなので、共通化します。View ファイルに_form.html.erb
を新規作成し、入力フォームの部分を移植します。<app/views/articles/_form.html.erb>
<%= form_with(model: @article, local: true) do |form| %>
<% if @article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@article.errors.count, "error") %>
prohibited this article from being saved:
</h2>
<ul>
<% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
new.html.erb
を下記に修正します。render
で表示するビューを指定します。<app/views/articles/new.html.erb>
<h1>New Article</h1>
<%= render 'form' %>
<%= link_to 'Back', articles_path %>
edit.html.erb
を新規作成します。<app/views/articles/edit.html.erb>
<h1>Edit article</h1>
<%= render 'form' %>
<%= link_to 'Back', articles_path %>
一覧画面に、記事を更新するための編集画面へのリンクを追加します。
<app/views/articles/index.html.erb>
<h1>Listing articles</h1>
<%= link_to 'New article', new_article_path %>
<table>
<tr>
<th>Title</th>
<th>Text</th>
<th colspan="2"></th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
# ---- ↓追加 ----
<td><%= link_to 'Edit', edit_article_path(article) %></td>
</tr>
<% end %>
</table>
詳細表示画面にも、編集画面へのリンクを追加します。
edit_article_path(@article)
とすることで、View からコントローラーに値を渡すことができます。<app/views/articles/show.html.erb>
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<%= link_to 'Back', articles_path %>
<%= link_to 'Edit', edit_article_path(@article) %>
*削除機能の追加
削除するためにはコントローラーにdestroy
アクションを追加します。削除後は一覧画面を再表示します。- destroy
インスタンス変数に対してdestroy
をすると、データを1件削除することができます。関連データも削除します。
class ArticlesController < ApplicationController
def index
@articles = Article.all
end
def show
@article = Article.find(params[:id])
end
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
def edit
@article = Article.find(params[:id])
end
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article
else
render 'edit'
end
end
# ---- ↓追加 ----
def destroy
@article = Article.find(params[:id])
@article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
一覧画面に削除するためのリンクを追加します。
data-confirm
を使って本当に削除して良いかの確認をするようにしています。- data-confirm
data: {confirm: "{メッセージ}"}
を指定することで、指定したメッセージをブラウザのダイアログで表示させることができます。
<h1>Listing articles</h1>
<%= link_to 'New article', new_article_path %>
<table>
<tr>
<th>Title</th>
<th>Text</th>
<th colspan="3"></th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
# ---- ↓追加 ----
<td><%= link_to 'Destroy', article_path(article),
method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
*コメント機能の追加
記事に対してコメントを追加できるようにします。記事のモデル作成時と同様に、ジェネレータを使ってコメントモデルを作成します。記事モデルと関連付ける必要があるため、
references
で外部キーを指定しています。$ rails generate model Comment commenter:string body:text article:references
Running via Spring preloader in process 58630
invoke active_record
create db/migrate/20190803104946_create_comments.rb
create app/models/comment.rb
invoke test_unit
create test/models/comment_test.rb
create test/fixtures/comments.yml
マイグレーションを実行します。
$ rails db:migrate
== 20190803104946 CreateComments: migrating ===================================
-- create_table(:comments)
-> 0.0065s
== 20190803104946 CreateComments: migrated (0.0068s) ==========================
記事モデルにもコメントモデルとの関連付けを指定するため、既存の
article.rb
にhas_many :comments
を追加します。<app/models/article.rb>
class Article < ApplicationRecord
has_many :comments
validates :title, presence: true, length: { minimum: 5 }
end
articles
のネストされたリソースとして、comments
のルーティングを追加します。do ... end
はブロック処理です。<config/routes.rb>
Rails.application.routes.draw do
get 'welcome/index'
resources :articles do
resources :comments
end
root 'welcome#index'
end
ジェネレータを使って
Comments
のコントローラーを生成します。$ rails generate controller Comments
Running via Spring preloader in process 58861
create app/controllers/comments_controller.rb
invoke erb
create app/views/comments
invoke test_unit
create test/controllers/comments_controller_test.rb
invoke helper
create app/helpers/comments_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/comments.coffee
invoke scss
create app/assets/stylesheets/comments.scss
詳細表示画面に、コメントの入力フォームを追加します。
- build
親モデルに対する外部参照キーを自動でセットすることができます。
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
# ---- ↓追加 start ----
<h2>Comments</h2>
<% @article.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_with(model: [ @article, @article.comments.build ], local: true) do |form| %>
<p>
<%= form.label :commenter %><br>
<%= form.text_field :commenter %>
</p>
<p>
<%= form.label :body %><br>
<%= form.text_area :body %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
# ---- ↑追加 end ----
<%= link_to 'Back', articles_path %>
<%= link_to 'Edit', edit_article_path(@article) %>
コントローラーにコメントを登録するための
create
アクションを追加します。<app/controllers/comments_controller.rb>
class CommentsController < ApplicationController
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.create(comment_params)
redirect_to article_path(@article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
コメント表示部分を共通化します。
View ファイルに
_comment.html.erb
を新規作成します。ファイル名と同じ
comment
をローカル変数として使うことができます。<app/views/comments/_comment.html.erb>
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
詳細表示画面でコメントを表示させるように修正します。
render
で@article.comments
を指定することで、_comment.html.erb
に値を渡すことができます。<app/views/articles/show.html.erb>
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<h2>Comments</h2>
# ---- ↓修正 ----
<%= render @article.comments %>
<h2>Add a comment:</h2>
<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>
<p>
<%= form.label :commenter %><br>
<%= form.text_field :commenter %>
</p>
<p>
<%= form.label :body %><br>
<%= form.text_area :body %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
<%= link_to 'Edit', edit_article_path(@article) %>
コメントの入力フォーム部分も共通化します。
_form.html.erb
を新規作成します。<app/views/comments/_form.html.erb>
<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>
<p>
<%= form.label :commenter %><br>
<%= form.text_field :commenter %>
</p>
<p>
<%= form.label :body %><br>
<%= form.text_area :body %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
詳細表示画面で、
_form.html.erb
を呼び出すよう修正します。<app/views/articles/show.html.erb>
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<h2>Comments</h2>
<%= render @article.comments %>
<h2>Add a comment:</h2>
# ---- ↓修正 ----
<%= render "comments/form" %>
<%= link_to 'Back', articles_path %>
<%= link_to 'Edit', edit_article_path(@article) %>
*コメント削除機能の追加
コメント表示部分に削除するためのリンクを追加します。<app/views/comments/_comment.html.erb>
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
# ---- ↓追加 ----
<p>
<%= link_to 'Destroy Comment', [comment.article, comment],
method: :delete, data: { confirm: 'Are you sure?' } %>
</p>
コントローラーに
destroy
アクションを追加します。<app/controllers/comments_controller.rb>
class CommentsController < ApplicationController
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.create(comment_params)
redirect_to article_path(@article)
end
# ---- ↓追加 ----
def destroy
@article = Article.find(params[:article_id])
@comment = @article.comments.find(params[:id])
@comment.destroy
redirect_to article_path(@article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
関連データを削除する必要があるため、
article.rb
にdependent: :destroy
を追加します。<app/models/article.rb>
class Article < ApplicationRecord
has_many :comments, dependent: :destroy
validates :title, presence: true, length: { minimum: 5 }
end
*Basic認証の追加
http_basic_authenticate_with
を使うことで、簡単にHTTP認証を実装することができます。except
を指定することで、認証しないアクションを指定することができます。コントローラーに追加し、表示以外は認証するようにします。
<app/controllers/articles_controller.rb>
class ArticlesController < ApplicationController
# ---- ↓追加 ----
http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]
def index
@articles = Article.all
end
def show
@article = Article.find(params[:id])
end
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
def edit
@article = Article.find(params[:id])
end
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article
else
render 'edit'
end
end
def destroy
@article = Article.find(params[:id])
@article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
コメントは削除のときのみに認証をつけます。
<app/controllers/comments_controller.rb>
class CommentsController < ApplicationController
# ---- ↓追加 ----
http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.create(comment_params)
redirect_to article_path(@article)
end
def destroy
@article = Article.find(params[:article_id])
@comment = @article.comments.find(params[:id])
@comment.destroy
redirect_to article_path(@article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
*所感
Java や Python を経験した私にとって、Rails はこう書けばこう動くといったお作法が非常に多くて理解するのが大変でした。チュートリアルだけだとわからない部分が多く、記法などを調べながらやることで理解できました。覚えれば速く開発できそうですが、きちんと理解しないと影響範囲がわかりづらく修正漏れが発生しそうです。初心者向きとの噂も聞いたことがありますが、きちんと理解して使わないと危ない言語だと思いました。
Rails は黒魔術感がすごいとは聞いていましたが、実際に書いてみて納得しました。
Sign up here with your email
ConversionConversion EmoticonEmoticon