下記の環境を想定しています。
OS:Ubuntu 20.04 LTS Ruby: 2.7 Rails: 7.0.1
コンテナで試す場合は下記の様にコンテナを起動します。
# docker run -d -it --name rails -h rails -p 3000:3000 -v `pwd`/mnt:/mnt ubuntu:20.04 # docker exec -it rails /bin/bash
必要なモジュールをインストールします。途中でタイムゾーンを聞かれたら Asia/Tokyo を選択してください。コンテナに root でログインする場合は sudo は不要です。時刻がずれているとインストールに失敗することがあります。
$ sudo apt update # 必要に応じてアップデート $ sudo apt -y upgrade # 必要に応じてアップグレード $ sudo apt -y install gcc make git $ sudo apt -y install ruby sqlite3 nodejs yarn $ sudo apt -y install ruby-dev libsqlite3-dev $ gem install rails
rails new
でプロジェクトを作成します。試しに myapp
という名前のプロジェクトを作成してみます。
$ rails new myapp $ cd myapp
./bin/rails server
でテスト用のサーバを起動します。-b 0.0.0.0 を指定すると外部ホストからの接続を受け付けることができます。別コンソールで起動しておけば起動しなおす必要はありません。
$ bin/rails server -b 0.0.0.0 -p 3000
http://{SERVER_ADDR}:3000/
に接続して赤い日の丸のような RAILSアイコンが表示されれば成功です。必要に応じて ufw
や firewall-cmd
などでファイアウォールの穴あけを行ってください。
Rails では MVC(Model, View, Controller) モデルで開発を行います。Model はデータベースなどでデータを管理します。View は HTML でデータを表示したり利用者からの画面入力を受け付けます。Controller は Model からデータを読み出して成形して View に引き渡したり、View からデータを受け取って成形して Model に格納したりします。
下記を実行して index
というメソッドを持つ、Home
という名前のコントローラを作成します。
$ bin/rails generate controller Home index
コントローラと、コントローラが持つメソッドに対応するビューも作成されます。
$ cat app/controllers/home_controller.rb class HomeController < ApplicationController def index end end $ $ cat app/views/home/index.html.erb <h1>Home#index</h1> <p>Find me in app/views/home/index.html.erb</p>
app/views/home/index.html.erb
ファイルを次のように変更してみましょう。
<h1>My Application</h1>
テストサーバを起動し、http://{SERVER_ADDR}:3000/home/index
にアクセスし、My Application
と表示されれば成功です。
コントローラで設定したパラメータをビューで表示してみます。app/controllers/home_controller.rb
に下記を追記します。
class HomeController < ApplicationController def index @message = "This is a test site of Ruby on Rails." end end
app/views/home/index.html.erb
に下記を追記します。<% ~ %>
は Ruby コードを実行します。<%= ~ %>
は Ruby コードの結果を表示します。
<h1>My Application</h1> <%= @message %>
http://{SERVER_ADDR}:3000/home/index
にアクセスし、上記メッセージが表示されれば成功です。
リストを表示してみます。app/controllers/home_controller.rb
に下記を追記します。
class HomeController < ApplicationController def index @message = "This is a test site of Ruby on Rails" @links = [ "users", "books", "help" ] end end
app/views/home/index.html.erb
に下記を追記します。
<h1>My Application</h1> <%= @message %> <ul> <% @links.each do |link| %> <li><a href="/<%= link %>"><%= link %></a></li> <% end %> </ul>
http://{SERVER_ADDR}:3000/home/index
にアクセスし、users 等のリンクが表示されれば成功です。
Home
コントローラに help
メソッドとビューを追加してみます。app/controllers/home_controller.rb
に下記を追記します。
class HomeController < ApplicationController def index @message = "This is a test site of Ruby on Rails." @links = [ "users", "books", "help" ] end def help end end
app/views/home/help.html.erb
ファイルを作成します。
<h1>Help</h1> <a href="/">Return</a> <p>This is help page...</p>
config/routes.rb
に下記のルーティングを追加します。/help の URL に GET メソッドでアクセスしたら Home コントローラの help メソッドを呼び出すという命令です。get 'home/index'
は get "/home/index", to: "home#index"
の省略形です。
Rails.application.routes.draw do get 'home/index' get '/help', to: 'home#help' end
help リンクをクリックしてヘルプ画面が表示されれば成功です。
config/routes.rb
に下記を追記すると、ルート(/
)へのアクセスでも Home
コントローラの index
メソッドを呼び出すようになります。
Rails.application.routes.draw do root 'home#index' get 'home/index' get '/help', to: 'home#help' end
http://{SERVER_ADDR}:3000/
にアクセスしても My Application のページが表示されれば成功です。
app/assets/stylesheets/common.css
ファイルを作成します。デフォルトでは、app/assets/stylesheets
配下にあるすべての .css ファイルが読み込まれます。
h1 { background-color: black; color: white; padding: .8rem; } a { color: #339; } input { height: 1.2rem; width: 25rem; margin-bottom: .5rem; } textarea { height: 3rem; width: 25rem; } button, input[type=submit] { height: 1.4rem; width: 10rem; margin-bottom: .5rem; }
ページを表示してタイトルの背景が黒くなれば成功です。
scaffold は「足場」という意味です。開発の足場となるサンプルのようなコントローラを作成します。
$ bin/rails generate scaffold User name:string age:integer
DBも作成するので、DBのマイグレーションも行います。詳細は後述。
$ bin/rails db:migrate
users
リンクをクリックすると、ユーザ管理アプリケーションに遷移し、ユーザ情報の作成、一覧/詳細、編集、削除を行うことができます。この一連の機能群を CRUD(Create, Read, Update, Delete) とも呼びます。HTTPメソッドと URL、機能の関係は次のようになります。
GET http://{SERVER_ADDR}:3000/users # 一覧画面(表示) GET http://{SERVER_ADDR}:3000/users/1 # 詳細画面(表示) GET http://{SERVER_ADDR}:3000/users/new # 作成画面(入力) POST http://{SERVER_ADDR}:3000/users # 作成(実行) GET http://{SERVER_ADDR}:3000/users/1/edit # 編集画面(入力) PATCH http://{SERVER_ADDR}:3000/users/1 # 編集(実行) PUT http://{SERVER_ADDR}:3000/users/1 # 編集(実行) ... PATCHと同じ DELETE http://{SERVER_ADDR}:3000/users/1 # 削除(実行)
ルート画面に戻るリンクをつけておきましょう。app/views/users/index.html.erb
に下記を追記します。
... <h1>Users</h1> <a href="/">Return</a>
ユーザ管理アプリと同様なブック管理アプリを手作業で開発していきます。この章で作成するアプリの最終形を サンプル に掲載します。
title
と author
カラムを持つ Book
モデルを作成します。
$ bin/rails generate model Book title:string author:string
モデルを作成・変更すると、前回からの差分ファイルが db/migrate
フォルダに作成されます。下記を実行すると、未適用のマイグレーションファイルがあれば、それをデータベースに反映します。
$ bin/rails db:migrate
下記の様にコンソールからモデルを参照・変更することができます。
$ bin/rails console irb> book = Book.new(title: "吾輩は猫である", author: "夏目漱石") irb> book.save # 保存 irb> Book.all # 全件表示 irb> Book.find(1) # 検索表示 irb> Ctrl-D # コンソールモード終了
コントローラを作成します。
$ bin/rails generate controller Books
app/controllers/books_controller.rb
に下記を追記します。
class BooksController < ApplicationController def index @books = Book.all end end
app/views/books/index.html.erb
を作成します。
<h1>Books</h1> <a href="/">Return</a> <ul> <% @books.each do |book| %> <li><a href="/books/<%= book.id %>"><%= book.title %></a></li> <% end %> </ul>
config/routes.rb
にルーティングを追加します。
Rails.application.routes.draw do ... get '/help', to: 'home#help' get '/books', to: 'books#index' end
books
リンクをクリックして一覧画面が表示されれば成功です。
app/controllers/books_controller.rb
に下記を追記します。
class BooksController < ApplicationController def index @books = Book.all end def show @book = Book.find(params[:id]) end end
app/views/books/show.html.erb
を作成します。
<h1>Books</h1> <a href="/books">Return</a> <div>Title: <%= @book.title %></div> <div>Author: <%= @book.author %></div>
config/routes.rb
にルーティングを追加します。
Rails.application.routes.draw do ... get '/books', to: 'books#index' get '/books/:id', to: 'books#show', as: :book end
ブックタイトルをクリックして詳細画面が表示されれば成功です。
app/controllers/books_controller.rb
に下記を追記します。
class BooksController < ApplicationController ... def show @book = Book.find(params[:id]) end def new @book = Book.new end def create @book = Book.new(book_params) if @book.save redirect_to @book else render :new, status: :unprocessable_entity end end private def book_params params.require(:book).permit(:title, :author) end end
app/views/books/new.html.erb
を作成します。
<h1>Books</h1> <a href="/books">Return</a> <%= form_with model: @book do |form| %> <div> <div><%= form.label :title %></div> <div><%= form.text_field :title %></div> </div> <div> <div><%= form.label :author %></div> <div><%= form.text_field :author %></div> </div> <div> <%= form.submit %> </div> <% end %>
config/routes.rb
にルーティングを追加します。/books/new
が /books/:id
にマッチしないように /books/:id
よりも先に書く必要があります。
Rails.application.routes.draw do ... get '/books', to: 'books#index' get '/books/new', to: 'books#new', as: :new_book post '/books', to: 'books#create' get '/books/:id', to: 'books#show', as: :book end
一覧画面 app/views/books/index.html.erb
に作成画面へのリンクを追記します。link_to の第一引数はリンクテキスト、第二引数には config/routes.rb
の as:
で指定した名前に _path
をつけたものを指定できます。
<h1>Books</h1> <a href="/">Return</a> | <%= link_to "Add", new_book_path %> ...
Add
リンクをクリックしてブックを新規登録することができれば成功です。
app/controllers/books_controller.rb
に下記を追記します。
class BooksController < ApplicationController ... def create ... end def edit @book = Book.find(params[:id]) end def update @book = Book.find(params[:id]) if @book.update(book_params) redirect_to @book else render :new, status: :unprocessable_entity end end private ... end
app/views/books/edit.html.erb
を作成します。
<h1>Books</h1> <a href="/books">Return</a> <%= form_with model: @book do |form| %> <div> <div><%= form.label :title %></div> <div><%= form.text_field :title %></div> </div> <div> <div><%= form.label :author %></div> <div><%= form.text_field :author %></div> </div> <div> <%= form.submit %> </div> <% end %>
config/routes.rb
にルーティングを追加します。
Rails.application.routes.draw do ... get '/books/:id', to: 'books#show', as: :book get 'books/:id/edit', to: 'books#edit', as: :edit_book patch '/books/:id', to: 'books#update' end
詳細画面 app/views/books/show.html.erb
に Edit
ボタンを追加します。
<h1>Books</h1> <a href="/books">Return</a> <div>Title: <%= @book.title %></div> <div>Author: <%= @book.author %></div> <%= button_to "Edit", edit_book_path, method: :get %>
Edit
ボタンからブックを編集することができれば成功です。
app/controllers/books_controller.rb
に下記を追記します。
class BooksController < ApplicationController ... def update ... end def destroy @book = Book.find(params[:id]) @book.destroy redirect_to books_path end private ...
config/routes.rb
にルーティングを追加します。
Rails.application.routes.draw do ... patch '/books/:id', to: 'books#update' delete '/books/:id', to: 'books#destroy' end
詳細画面 app/views/books/show.html.erb
に Delete
ボタンを追加します。下記の様に指定すると /books/:id
パスに対して DELETE
メソッドが発行されます。
<h1>Books</h1> ... <%= button_to "Edit", edit_book_path, method: :get %> <%= button_to "Delete", @book, method: :delete %>
Delete
ボタンでブックを削除できれば成功です。
app/views/home/help.html.erb
, app/views/users/index.html.erb
, app/views/books/index.html.erb
の中のルート画面へのパスを相対パスに修正します。
修正前:<a href="/">Return</a> 修正後:<%= link_to "Return", root_path %>
app/views/books/new.html.erb
, app/views/books/edit.html.erb
, app/views/books/show.html.erb
の中にあるパスも相対パスに修正します。
修正前:<a href="/books">Return</a> 修正後:<%= link_to "Return", books_path %>
app/views/books/index.html.erb
の詳細画面へのリンクも相対パスに修正します。book がブックオブジェクトを示している場合、book オブジェクト自体を指定すると、そのオブジェクトの book_path に遷移します。
修正前:<li><a href="/books/<%= book.id %>"><%= book.title %></a></li> 修正後:<li><%= link_to book.title, book %></li>
トップページのメニューを、ラベルを指定可能に、パスを絶対パスから _path を用いた相対パスに書き換えます。app/controllers/home_controller.rb
を次のように修正します。
class HomeController < ApplicationController def index @message = "This is a test site of Ruby on Rails." @menus = [ { :label => "User", :path => users_path }, { :label => "Book", :path => books_path }, { :label => "Help", :path => help_path } ] end ...
app/views/home/index.html.erb
を次のように修正します。
<h1>My Application</h1> <%= @message %> <ul> <% @menus.each do |menu| %> <li><%= link_to menu[:label], menu[:path] %></li> <% end %> </ul>
config/routes.rb
で book に関するルーティングを削除し、代わりに resources
を使用すると、Books コントローラに対する /books/new
や /books/:id/edit
などのルーティング設定をまとめて一括指定することができます。
Rails.application.routes.draw do resources :users root 'home#index' get 'home/index' get '/help', to: 'home#help' resources :books end
下記を実行すると定義されているルーティング情報の一覧が表示されます。Prefix
に _path を加えたものが link_to
などで使用できます。
$ bin/rails routes Prefix Verb URI Pattern Controller#Action users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroy
app/views/books/new.html.erb
と app/views/books/edit.html.erb
の内容はほぼ同じですので、共通部をパーシャルとして切り出します。まず、app/views/books/_form.html.erb
ファイルを作成します。パーシャルのファイル名はアンダーバー(_)で始める必要があります。@book が book に変わることに注意してください。
<%= form_with model: book do |form| %> <div> <div><%= form.label :title %></div> <div><%= form.text_field :title %></div> </div> <div> <div><%= form.label :author %></div> <div><%= form.text_field :author %></div> </div> <div> <%= form.submit %> </div> <% end %>
app/views/books/new.html.erb
と app/views/books/edit.html.erb
を次のように修正します。
<h1>Books</h1> <%= link_to "Return", books_path %> <%= render "form", book: @book %>
作成や編集フォームにバリデーションを追加するには次のようにします。app/models/book.rb
に下記を追記します。
class Book < ApplicationRecord validates :title, presence: true, length: { maximum: 20 } validates :author, presence: true, length: { maximum: 20 } end
app/views/books/_form.html.erb
に下記を追記します。
<%= form_with model: book do |form| %> <div> <div><%= form.label :title %></div> <div><%= form.text_field :title %></div> <% @book.errors.full_messages_for(:title).each do |message| %> <div><%= message %></div> <% end %> </div> <div> <div><%= form.label :author %></div> <div><%= form.text_field :author %></div> <% @book.errors.full_messages_for(:author).each do |message| %> <div><%= message %></div> <% end %> </div> <div> <%= form.submit %> </div> <% end %>
Title
や Author
に20文字以上の文字を入力して登録・変更しようとするとエラーメッセージが表示されれば成功です。
ブックに対してコメントを追記できるようにします。親子関係をもつテーブルを扱う練習でもあります。下記の様にコントローラと、Bookに関係づいたモデルを追加してマイグレーションします。
$ bin/rails generate controller Comments $ bin/rails generate model Comment commenter:string body:text book:references $ bin/rails db:migrate
app/models/book.rb
に下記を追記します。
class Book < ApplicationRecord has_many :comments validates :title, presence: true, length: { maximum: 20 } validates :author, presence: true, length: { maximum: 20 } end
config/routes.rb
に下記を追記します。/books/:book_id/comments/:id
などでのルーティングが追加されます。
Rails.application.routes.draw do ... get '/help', to: 'home#help' resources :books do resources :comments end end
app/controllers/comments_controller.rb
に下記を追記します。
class CommentsController < ApplicationController def create @book = Book.find(params[:book_id]) @comment = @book.comments.create(comment_params) redirect_to book_path(@book) end private def comment_params params.require(:comment).permit(:commenter, :body) end end
app/views/books/show.html.erb
に下記を追記します。上半分がコメント表示欄、下半分がコメント追記欄です。
<h1>Books</h1> <%= link_to "Return", books_path %> <div>Title: <%= @book.title %></div> <div>Author: <%= @book.author %></div> <%= button_to "Edit", edit_book_path, method: :get %> <%= button_to "Delete", @book, method: :delete %> <h2>Comments</h2> <% @book.comments.each do |comment| %> <div> <strong><%= comment.commenter %></strong> <%= comment.body %> </div> <% end %> <%= form_with model: [ @book, @book.comments.build ] do |form| %> <div> <div><%= form.label :commenter %></div> <div><%= form.text_field :commenter %></div> </div> <div> <div><%= form.label :body %></div> <div><%= form.text_area :body %></div> </div> <div><%= form.submit %></div> <% end %>
ブックの詳細画面からコメントを追加できるようになったら成功です。
必要に応じて app/views/layouts/application.html.erb
を編集します。
<!DOCTYPE html> <html lang="ja"> <head> <title>Myapp</title> ...
修正したはずなのに修正がうまく反映されないなどの場合、キャッシュ情報が残っている場合があります。サーバーを再起動してみたり、下記のコマンドでキャッシュ情報をクリアしてみてください。
$ bin/rails tmp:clear
Rails では単語の単数形と複数形を使用します。スキャフォールド で指定する名前は単数形ですが、自動生成されるコントローラ名は複数形、モデルは単数形、ビューのディレクトリ名は複数形になります。これらは Rails が単数形と複数形の変換アルゴリズムと辞書を持っていて自動変換しています。複数形化メソッド pluralize
と単数形化メソッド singularize
もサポートしています。
puts "person".pluralize # => "people" puts "people".singularize # => "person"
単数形・複数形が期待通りに変換されない場合は config/initializers/inflections.rb
に単数形・複数形の単語を追加してください。
ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.irregular "love", "loves" end