Rails学習記録入門

Railsのアプリ開発メモ 入門編
プログラミングスクールで学んでいることをスクラップに整理していく

開発環境
docker compose build
:イメージのビルド
docker compose up
:Railsサーバの起動
docker compose exec web bash
:コンテナに入る。webはDockerのサービス名
bundle install
:Gemのインストール。コンテナ内から実行
yarn install
:node_modulesのインストール。コンテナ内から実行
docker compose exec web bin/rails db:create
:データベースの作成
bin/dev
:CSS, JS用のサーバー起動

Git
git switch -c ブランチ名
:ブランチを切り替える。新規の場合は作成する
git branch
:現在のブランチ名を確認する
git status
:変更のあったファイルを確認する
git add ファイル名
:リモートリポジトリに反映したいファイルをステージに上げる
git commit -m "メッセージ"
:ステージにあげた変更をリポジトリに保存する
git push origin ブランチ名
:変更をリモートリポジトリにアップロード
git checkout master
:masterブランチに切り替える
git pull origin master
:リモートリポジトリの内容をローカルにコピーする

タスク登録機能を実装する例
モデルの作成
docker compose run web bin/rails generate Task title:string description:text
- モデル名Taskは単数。作成するカラムとその型を記述
db/migrate
のmigrationファイルを確認してdocker compose run web bin/rails db:migrate
models/task.rb
がモデルファイル。テーブル名は単数系
class Task < ApplicationRecord
validates :title, presence: true #バリデーションを記述。これはnull禁止
end
バリデーションの例
参考:https://railsguides.jp/active_record_validations.html
- validates :terms_of_service, acceptance: true #チェックボックスがオンであるおこと
- validates :email, confirmation: true #二つのフィールドの内容が同じであること
- validates :end_date, comparison: { greater_than: :start_date } #比較対象よりも大きいこと
- validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,message: "英文字のみが使えます" } #正規表現のようなフォーマットに従っていること
- validates :size, inclusion: { in: %w(small medium large),message: "%{value} のサイズは無効です" } #指定するものを含んでいること
- validates :points, numericality: true #数値であること
- validates :games_played, numericality: { only_integer: true } #数値の中でも整数である条件を付与
- validates :name, :login, :email, presence: true #空白値ではないこと
- validates :email, uniqueness: true #一意であること
ルーティング
参考:https://railsguides.jp/routing.html
config/routes.rb
がルーティングファイル
Rails.application.routes.draw do
resources :tasks #コントローラへのルーティング。複数形。
# resources :tasks, only: :index # indexアクションだけに有効にする場合only: :アクション
end
resourcesを用いることでindex、show、new、create、edit、update、destroyアクションが作成される
- GET /tasks tasks#index すべてのタスクの一覧を表示
- GET /tasks/new tasks#new タスクを1つ作成するためのHTMLフォームを返す
- POST /tasks tasks#create タスクを1つ作成する
- GET /tasks/:id tasks#show 特定のタスクを表示する
- GET /tasks/:id/edit tasks#edit タスク編集用のHTMLフォームを1つ返す
- PATCH/PUT /tasks/:id tasks#update 特定のタスクを更新する
- DELETE /tasks/:id tasks#destroy 特定のタスクを削除する
パスとURL用ヘルパー
resourcesでルーティングした場合、一意のURLパスが同時に生成され、リンク時に活用できる
- tasks_urlは/tasks #複数
- new_task_urlは/tasks/new #単数
- edit_task_url(:id)は/tasks/:id/edit (edit_photo_url(10)であれば/photos/10/edit) #単数
- task_url(:id)は/photos/:id (photo_url(10)であれば/photos/10) #単数
コントローラの設定
参考 https://railsguides.jp/action_controller_overview.html
app/controllers/tasks_controller.rb
ファイルを作成する
class TasksController < ApplicationController
before_action :set_task, only: %i[ show edit update destroy ] #[:show, :edit, :update, :destroy]と同意
before_action :set_index_title, only: :index #set_index_titleをindexアクションの最初に実行
before_action :set_show_title, only: :show
# GET /tasks or /tasks.json
def index #一覧表示
@tasks = Task.all
end
# GET /tasks/1 or /tasks/1.json
def show #一件表示
@task = Task.find_by(id: params[:id])
end
# GET /tasks/new
def new #新規作成用のフォーム表示
@task = Task.new
end
# GET /tasks/1/edit
def edit #一件編集
@task = Task.find_by(id: params[:id])
end
# POST /tasks or /users.json
def create #新規作成(DB更新)
@task = Task.new(task_params) #task_paramsは事前に指定されたparamのみを許可
respond_to do |format| #サイトリクエストに対してHTML、APIリクエストに対してJSONを返す
if @task.save #保存を試みる
format.html { redirect_to task_url(@task), notice: "タスクの新規登録に成功しました" } #task_url(@task)にリダイレクト @taskのidを内部的に取得している
format.json { render :show, status: :created, location: @task }
else
format.html { render :new, status: :unprocessable_entity } #ステータスコードに422
format.json { render json: @task.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /tasks/1 or /users/1.json
def update
respond_to do |format|
if @task.update(task_params)
format.html { redirect_to task_url(@task), notice: "タスクの更新に成功しました" }
format.json { render :show, status: :ok, location: @task}
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @task.errors, status: :unprocessable_entity }
end
end
end
# DELETE /tasks/1 or /users/1.json
def destroy
@task.destroy
respond_to do |format|
format.html { redirect_to tasks_url, notice: "タスクの削除に成功しました" }
format.json { head :no_content }
end
end
private #以下、クラス内部からのみアクセス可能
# Use callbacks to share common setup or constraints between actions.
def set_task
@task = Task.find(params[:id])
end
# Only allow a list of trusted parameters through.
def task_params #許容するパラメータbefore_actionでは実行しない
params.require(:task).permit(:title, :description)
end
def set_index_title
@index_title = "タスク一覧"
end
def set_show_title
@show_title = "タスク詳細"
end
end
ビューの作成
参考 https://railsguides.jp/action_view_overview.html
app/views
にtasksフォルダを作成する
共通ページapplication.html.erb
<!-- ヘッダー割愛 -->
<body>
<header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#">RUNTEQ Rails入門STEPアプリ</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="col-md-3 text-end">
<% if current_user.present? %> <!-- ユーザーログイン関連は次項で -->
<span class="btn btn-primary"><%= current_user.name %></span>
<%= link_to 'ログアウト',logout_path %>
<% else %>
<%= link_to 'ログイン', new_login_path, class: 'btn btn-outline-primary me-2' %>
<% end %>
</div>
</header>
<div class="container-fluid">
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="position-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item">
<%= link_to 'トップ', root_path, class: 'nav-link active', 'aria-current': 'page' %><!-- root_pathでトップに -->
<%= link_to 'ユーザー', users_path, class: 'nav-link active', 'aria-current': 'page' %><!-- users_pathでusers#indexに -->
<%= link_to 'タスク', tasks_path, class: 'nav-link active', 'aria-current': 'page' %><!-- users_pathでtasks#indexに -->
</li>
</ul>
</div>
</nav>
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<%= yield %><!-- この部分に各個別のビューを表示 -->
</main>
</div>
</div>
</body>
一覧ページindex
<p style="color: green"><%= notice %></p>
<h1><%= @index_title %></h1><!-- @index_titleは コントローラのbefore_actionで定義 -->
<div id="users">
<% @tasks.each do |task| %><!-- @tasksを順に -->
<%= render task %><!-- パーシャルファイル _task.html.erbに記述しここで読み込む -->
<p>
<%= link_to "詳細", task %><!-- task#showにリンク -->
</p>
<% end %>
</div>
<%= link_to "新規作成", new_task_path %><!-- task#newにリンク -->
パーシャルページ
<div id="<%= dom_id task %>">
<p>
<strong>タイトル:</strong>
<%= task.title %>
</p>
<p>
<strong>内容:</strong>
<%= task.description %>
</p>
</div>
パーシャルページ 新規登録フォーム
<%= form_with(model: task) do |form| %><!-- form_withはRailsでフォームを作成するためのヘルパーメソッド -->
<% if task.errors.any? %>
<div style="color: red">
<h2><%= pluralize(task.errors.count, "error") %> prohibited this user from being saved:</h2><!-- エラーの数と、そのエラーがユーザーの保存を妨げたことを示すメッセージを表示 -->
<ul>
<% task.errors.each do |error| %><!-- 各エラーをリスト表示-->
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= form.label :title, 'タイトル', style: "display: block" %>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :description, '内容', style: "display: block" %>
<%= form.text_area :description %>
</div>
<div>
<%= form.submit '登録' %>
</div>
<% end %>