気になった用語等をまとめる
第1, 2章_環境準備等
学んだこと
学んだこと
- Procオブジェクトはブロックをオブジェクト化したもの
proc = Proc.new {puts "hoge"}
proc.call
=> hoge
# Procオブジェクトで渡したブロックはcallメソッドで実行できる
- yieldはメソッド呼び出しで渡されたブロックをメソッドの中で実行する
def yield_test
yield(1, 2)
end
puts yield_test { |a, b| a + b}
=> 3
# ブロックをメソッド内の任意のタイミングで実行できる
- メソッド内ではアクセサがなくてもインスタンス変数の読み書きが可能
- クラスメソッドはメソッド名にselfを付ける
- クラス変数は変数名に@@を付ける
- メソッド内ではselfがなくてもインスタンスメソッドの呼び出しが可能
- 同じクラスのメソッド内ではプライベートメソッドの呼び出しが可能(selfを付けることは不可)
- モジュールは色々な呼び出し方がある
- モジュールは複数のクラスから呼び出したい共通のメソッドをまとめたりする(コードを綺麗にしたい時に有効)
- 例外で再度beginから始める場合はretryを使う
num=0
begin
p 10 / num
resucue ZeroDivisionError => e
p e
num = 2
retry
end
puts "終了"
# 無限ループ注意
- 例外で明示的に例外を発生させる場合はraiseを使う
begin
raise ZeroDivisionError
resucue => e
p e
end
第4章_環境構築(Docker)
学んだこと
第5章_環境構築(Rails7)
学んだこと
# ①Dockerfileを元にビルド
docker compose build
# ②コンテナ起動
docker compose up
# ③DB作成
# docker compose exec webでwebサービスのコンテナ上を指定
# execコマンドは対象のコンテナが実行中であることが前提
docker compose exec web (bundle exec) rails db:create
第6章_環境構築(Rails5)
学んだこと
# Dockerfile上で
# ①「rails s時にuninitialized constant Nokogiri::HTML4 (NameError)」にはruby:2.4.5→2.5.0で解消
# ②「docker build時にapt-get updateでPackages Not Found」にはecho~を二つ記載することで解消
FROM ruby:2.5.0
RUN echo "deb http://archive.debian.org/debian/ stretch main" > /etc/apt/sources.list \
&& echo "deb http://archive.debian.org/debian-security stretch/updates main" >> /etc/apt/sources.list \
&& apt-get update -qq && apt-get install -y build-essential nodejs
...
学んだこと
- 基本理念
- MVCについて
- ①ルーティングによりURLとCが紐付けられる
- ②CはデータをM(DB)から取得する
- ③CはデータをVに渡してHTMLを生成する
- M, Cはクラス
- Vはerbファイル
学んだこと
- BootstrapはWebのUIを作成するためのフレームワーク
- ①GemfileでBootstrapを入れる(その後「docker compose build」でイメージの再作成)
- ②cssをscssに変更する
- ③scss内のrequire~は消し「@import "bootstrap";」を記載
- ④javascriptのファイルを関連づける「docker compose run web rails importmap:install」を実行
- ⑤config/importmap.rb内にjsファイルを参照する所定のコードを記載
- ⑥config/assets.rb内にjsファイルをブラウザに配信するための所定のコードを記載
- ⑦app/javascript/application.js内にjsファイルを記載する
- app/views/layouts/application.html.erbで全体のレイアウトを設定
- ※続きは9章のRails5をカスタマイズしながら進める
学んだこと
- ※Rails7にカスタマイズしながら進める
- docker-compose upでサーバ起動後にCtrl+cで停めるとログが残る時があり次回起動できなくなるため以下で削除
rm tmp/pids/server.pid
もしくは
docker-composeファイルに「rm -f tmp/pids/server.pid」を記載
- gを使わずに一つ一つファイルを作成していくと理解が深まる
- マイグレーションファイルでDBの設定
docker-compose run web (bundle exec) rails g model board name:string title:string body:text
# docker-compose run web→webサービスのコンテナ内でコマンド実行
# bundle exec rails g model→RailsPJにインストールされたrailsコマンド実行+マイグレーションファイルとモデルファイルの作成
# name:string title:string body:text →boardsテーブルのカラム作成
# モデルは単数、テーブルは複数
# その後テーブルの作成
docker-compose run web (bundle exec) rake db:migrate
- O/Rマッパーとはテーブルのレコードをプログラムのオブジェクトとして扱う機能
- Railsにおけるリソースは主にテーブルデータのこと
- HTTPメソッドとパスの組み合わせでリクエスト
- ルーティングによってコントローラーのアクションに転送される
- 掲示板新規作成ページの作成(ルーティングの書き方)
get "boards", to: "boards#index"
- ヘルパーメソッドはビューの中で使用できるメソッド(簡潔に記述できる)
<%= form_for @board do |f| %>
<div class="form-group">
<%= f.label :name, "名前" %>
<%= f.text_field :name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :title, "タイトル" %>
<%= f.text_field :title, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :body, "本文" %>
<%= f.text_area :body, class: "form-control", rows: 10 %>
</div>
<%= f.submit "保存", class: "btn btn-primary" %>
<% end %>
- コントローラー内で定義したインスタンス変数はビューで参照可能
def new
@board = Board.new
end
- デバッグツールpry-byebugを使用するにはgemでdevelopmentにインストール後attachする必要がある
docker ps # コンテナ名の確認
docker attach コンテナ名
# Ctrl + p + qで抜ける
- postはコントローラー上paramsで受け取る
- paramsの中で必要なデータのみ取れるようフィルタ(ストロングパラメーター)をかける
def board_params
# 必要なデータのみparamsから取り出し
params.require(:board).permit(:name, :title, :body)
end
def create
# DBへ保存
Board.create(board_params)
end
# C:取得
def index
@boards = Board.all
end
# V:表示
<tbody>
<% @boards.each do |board| %>
<tr>
<th><%= board.id %></th>
<td><%= board.title %></td>
<td><%= board.name %></td>
<td><%= board.created_at %></td>
<td><%= board.updated_at %></td>
</tr>
<% end %>
</tbody>
# UTC表記を日本時間に直す
# config/application.rbに追記
config.time_zone = "Tokyo"
# 所定のフォーマットは使い回すためにまとめる
# config/initialize/time_formats.rbを作成
Time::DATE_FORMATS[:datetime_jp] = "%Y年 %m月 %d日 %H時 %M分"
# Vに追記
<td><%= board.created_at.to_s(:datetime_jp) %></td>
<p class="card-text"><%= simple_format(@board.body) %></p>
http://localhost:3000/rails/info/routes
# Helperはpath(URL)を返す
- リソースベースルーティングによりルーティングの設定を一行で済ませることができる
# ルーティングに追記
resources :boards, only: [:index, :new, :create, :show]
# Vに追記(編集する際はpathだけでなく@でオブジェクトを渡すのがポイント)
<%= link_to "編集", edit_board_path(@board), class: "btn btn-outline-dark" %>
# Cに追記
redirect_to board(=path)
# Vに_form.html.erbを作成(必ず_から始める)
<%= render partial: "form", locals: { board: @board } %>
# Cに追記
def destroy
board = Board.find(params[:id])
board.delete
redirect_to boards_path
end
# Vに追記
# ※Rails7からdeleteの仕様が変わったためjavascript等の設定変更が必要(参考文献②も参照)
<td><%= link_to "削除", board, class: "btn btn-outline-dark", data: { turbo_method: :delete } %></td>
- コントローラーのフィルタ機能(アクションの実行前後で動作を付ける)
# Cに追記
before_action :set_target_board, only: %i[show edit update destroy]
def set_target_board
@board = Board.find(params[:id])
end
- ページネーションとは1ページに全て表示するのでなく複数のページに分割して表示する機能のこと
- ①db/seeds.rbにサンプルデータを入れて確認する
- デフォルトでRails.envにはdevelopmentが入っている
- ②gem "kaminari"をインストールする
- ③kaminariの設定ファイルを生成する
docker compose exec web bundle exec rails g kaminari:config
docker compose exec web bundle exec rails g kaminari:views bootstrap4
def index
@boards = Board.page(params[:page])
end
# デフォルトで1ページ辺り25件のデータを取得
# Vのindexに追記
<%= paginate @boards %>
- ⑦ページリンクを日本語にする(任意:application.rb等を修正)
- ⑧ページリンクを中央寄せにする(任意:Vの_pafinator.html.erbを修正)
- ⑨ページ数を変更(任意:kaminari_config.rbを修正)
- フラッシュを使用したメッセージの表示
# Cに追記
def create
# DBへ保存
board = Board.create(board_params)
flash[:notice] = "「#{board.title}」の掲示板を作成しました" # 一度だけのメッセージ
redirect_to board
end
# Vに追記(※createでなくshowとなるがflashは異なるVに値を渡せる)
<% if flash[:notice] %>
<div class="alert alert-primary"><%= flash[:notice] %></div>
<% end %>
# ※redirectへの設定も可能
def destroy
@board.delete
redirect_to boards_path, flash: { notice: "「#{@board.title}」の掲示板が削除されました"}
end
# Mに追記
class Board < ApplicationRecord
validates :name, presence: true, length: {maximun:10}
validates :title, presence: true, length: {maximun:30}
validates :body, presence: true, length: {maximun:1000}
end
# Cに追記
def create
# DBへ保存
board = Board.new(board_params)
if board.save
flash[:notice] = "「#{board.title}」の掲示板を作成しました"
redirect_to board
else
redirect_to new_board_path, flash: {
board: board,
error_messages: board.errors.full_messages
}
end
end
# Vに追記(new)
<% if flash[:error_messages] %>
<div class="alert alert-danger">
<ul>
<% flash[:error_messages].each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
- モデルのアソシエーションとは複数のモデルを関連付ける機能のこと
# まずは便利なgemのインストール→モデルファイルでテーブル構成が分かるようになる(参考文献③も参照)
gem "annotate"
docker-compose exec web bundle exec rails g annotate:install
docker compose exec web bundle exec annotate
# board:reference はboardモデルへの外部キーの作成(borad_id)
docker compose run web bundle exec rails g model comment board:references name:string comment:text
# Mに追記(board)
# has_manyで1:多を設定
class Board < ApplicationRecord
has_many :comments
# commentsコントローラーの作成
# --skip-template-engineでVの作成をスキップ
docker compose exec web bundle exec rails g controller comments create destroy --skip-template-engine
# routeの追加
resources :comments, only: %i[create destroy]
# Cに追記(boards)
# 掲示板に紐付けたコメントを作成
def show
@comment = @board.comments.new
end
# Vに追記(show)
<%= render partial: "comment_form", locals: { commnet: @comment } %>
# _comment_form.html.erbの作成
所定の内容をコピー
# comments.scssの設定
所定の内容をコピー
# gem "rails-flog", require: "flog"をインストール(binding_parameterを整形する)
# Cに追記
def create
comment = Comment.new(comment_params)
if comment.save
flash[:notice] = "コメントを投稿しました"
redirect_to comment.board
else
flash[:comment] = comment
flash[:error_messages] = comment.errors.full_messages
redirect_back fallback_location: comment.board
end
end
private
def comment_params
params.require(:comment).permit(:board_id, :name, :comment)
end
# Cを修正(board)
def show
@comment = Comment.new(board_id: @board.id)
end
# Vに追記(show)
<div class="p-comment-list">
<div class="p-comment_listTitle">コメント</div>
<%= render @board.comments %>
</div>
<%= render partial: "comments/form", locals: { comment: @comment } %>
# _comments.html.erb, comment.scss, _form.html.erbに所定の内容をコピー
- コメントモデルへのバリデーションとエラーメッセージの設定
# commentモデルにバリデーションを書き、Vにsharedフォルを設け_error_messages.html.erbを作成
<% if flash[:error_messages] %>
<div class="alert alert-danger">
<ul>
<% flash[:error_messages].each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
# Cに追記
def destroy
comment = Comment.find(params[:id])
comment.delete
redirect_to comment.board, flash: { notice: "コメントが削除されました"}
end
# Vに追記(_comment.html.erb)
<span><%= link_to "削除", comment, data: { turbo_method: :delete, turbo_confirm: "削除しても宜しいですか?"} %></span>
# Mに追記(tag)
# tagとboardsをboard_tag_relationsを経由して関連付ける
class Tag < ApplicationRecord
has_many :board_tag_relations
has_many :boards, through: :board_tag_relations
end
- 掲示板削除でコメントやタグの関連付けも一緒に削除するにはdependent: :delete_allを追記
# Mに追記(board, tag)
class Board < ApplicationRecord
has_many :comments, dependent: :delete_all
has_many :board_tag_relations, dependent: :delete_all
has_many :tags, through: :board_tag_relations
# Cに追記(boards)
# deleteをdestroyメソッドにする必要がある
def destroy
@board.destroy
①db/seeds.rbに必要なタグを記載
②docker compose exec web rails db:seed(※rake db:seedでなくても可)
③_form.html.erb, _board.html.erbを修正
# Vに追記(index)
<div class="ms-auto boards__linkBox">
<%= form_tag boards_path, method: :get, class: 'boards__searchForm' do %>
<%= select_tag :tag_id,
options_from_collection_for_select(Tag.all, :id, :name, params[:tag_id]),
{
prompt: 'タグで絞り込み',
class: 'form-control boards__select',
onchange: 'submit(this.form);'
}
%>
<% end %>
#Cに追記
def index
@boards = params[:tag_id].present? ? Tag.find(params[:tag_id]).boards : Board.all
@boards = @boards.page(params[:page])
end
# Vに作成(_header.html.erb)
# 全ページ共通のためapp/helpers/application_helper.rbにヘルパーメソッドを定義
module ApplicationHelper
def header_link_item(name, path)
class_name = 'nav-item'
class_name << ' active' if current_page?(path)
content_tag :li, class: class_name do
link_to name, path, class: 'nav-link'
end
end
end
# rootに追記
root "home#index"
# Vに追記(_header.html.erb)
所定の内容をコピー
# Vに追記(全ページ共通application.html.erb)
<body>
<%= render "header" %>
<div class="container">
<%= yield %>
</div>
</body>
第10章_ユーザー認証の実装(Rails5)
学んだこと
- ※Rails7にカスタマイズしながら進める
- 1回目のアクセスでは認証情報からユーザーを識別しセッションIDをクッキーに乗せて発行
- 2回目のアクセスではセッションIDからユーザーを識別する
- M, V, Cの作成
- ①gemでbcryptのインストール
- ②rails gでユーザーモデルの作成
- ③db:migrateでテーブルの作成
- ④rails gでセッションコントローラーの作成
- rails g controller sessions create destroy --skip-template-engine
- Vは不要なため--skip-template-engineを付ける
- ⑤rails gでホームコントローラーの作成
- ⑥rails gでユーザーコントローラーの作成
- ルーティングの設定
- post "login", to: "sessions#create"
- delete "logout", to: "sessions#destroy"
- ユーザーモデルの認証機能設定
- ①has_secure_passwordでパスワードの設定
- ②validatesの設定
- ③ja.ymlで日本語化
- 登録フォームの作成
- ①_header.html.erbでドロップリストの作成
- ※機能しなかったためbootstrapのサイトの例示を参考に書き換えた(参考文献⑤参照)
- ②ユーザーコントローラーで@userを新規作成
- ③_form.html.erbで画面の作成
- ユーザー登録
- ①リクエストパラメーターの設定
- ②createアクションでsession変数の設定
- ※redirect_to :backはrails7では使用不可(参考文献④参照)
- ログイン・ログアウト
- ①home/index.html.erbにrender partialを記載し_login_form.html/erbを読み込む
- ②sessionsコントローラーにログイン・ログアウト機能を記載
class SessionsController < ApplicationController
def create
user = User.find_by(name: params[:session][:name])
if user && user.authenticate(params[:session][:password])
session[:user_id] = user.id
redirect_to mypage_path
else
render "home/index"
end
def destroy
session.delete(:user_id)
redirect_to root_path
end
end
- ログインユーザーの取得とマイページの作成
- ①applicationコントローラーに現在のユーザーの取得
# before_actionにより全てのアクションより前に呼び出される
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :current_user
private
def current_user
return unless session[:user_id]
@current_user = User.find_by(id: session[:user_id])
end
end
<h1>マイページ</h1>
<%= @current_user.name %>
第11章_Railsのバージョンアップ
学んだこと
- ①Gemfileのrailsのバージョンを変更
- ②以下により関連するgemも含めてアップデートされる
docker-compose run web bundle update rails
- ③イメージの再作成
- ④エラー箇所は設定ファイルを更新
config/initializers/new_framework_defaults.rb
# ※機能の廃止などでエラーとなるためバージョンアップは慎重に行う
学んだこと
- form_withヘルパーとはform_for, form_tagヘルパーを置き換えるもの
# 修正前
<%= form_for user do |f| %>
# 修正後
<%= form_with model: user do |f| %>
# 修正前
<%= form_for(:session, method: :post, url: login_path) do |f| %>
# 修正後(デフォルトでpostのためpostは省略可)
<%= form_with scope: :session, url: login_path do |f| %>
# 修正前
<%= form_tag boards_path, method: :get, class: 'boards__searchForm' do %>
# 修正後
<%= form_wiht url: boards_path, method: :get, class: 'boards__searchForm' do %>
第13章_RSpecによるテスト
学んだこと
RSpec.describe User, type: :model do
describe "#age" do
before "#age" do
allow(Time.zone).to receive(:now).and_return(Time.zone.parse("2018/04/01"))
end
context "20年前の生年月日の場合" do
let(:user) { User.new(birthday: Time.zone.now - 20.years) }
it "年齢が20歳であること" do
expect(user.age).to eq 20
end
end
- 境界値のテストとモック
- モックはテスト用にダミーオブジェクトやそれに対してメソッドを呼んだ場合に決まった値を返すもの
- コントローラーのテスト
- アクションによって、どういったレスポンスコードが返るのか、どういったテンプレート(V)をレンダリングするのか、どういった変数がテンプレート(V)に渡されるべきなのか、のテスト
- user_controller_spec.rbにテストコードを記載
describe "GET #new" do
before {get :new}
it "レスポンスコードが200であること" do
expect(response).to have_http_status(:ok)
end
it "newテンプレートをレンダリングすること" do
expect(response).to render_template :new
end
it "新しいuserオブジェクトがビューに渡されること" do
expect(assigns(:user)).to be_a_new User
end
end
end
- createアクションの正常系のテスト
- createアクションの異常系のテスト
- テストはリファラー(前のページの情報)がないので明示的に設定が必要
- docker compose exec web bundle exec rspec -f d ./spec/controllers/users_controller_spec.rb :45 (45行目以降をテスト)
その他
学んだこと
- コンテナとイメージを全て削除し始めからやり直す場合
- ①Dockerfileを元にビルド
- ②コンテナ起動
- ③DB作成
- docker compose exec webでwebサービスのコンテナ上を指定
- execコマンドは対象のコンテナが実行中であることが前提
- docker compose exec web (bundle exec) rails db:create
- ④マイグレーションファイルでDBの設定(--skip-collision-checkでエラー回避)
- docker-compose run web (bundle exec) rails g model board name:string title:string body:text --skip-collision-check
⑤その後テーブルの作成
- docker-compose run web (bundle exec) rake db:migrate
Gemfileにgemを追加した時の対応
- ①インストール
- ②ビルド
参考文献
Kazuya Kojima, 2023/10/10取得, フルスタックエンジニアが教える 即戦力Railsエンジニア養成講座
①@teriyakisan(Hiroki Tanaka), 2023/10/10取得, docker build時にapt-get updateでPackages Not Found
②アカリバ株式会社, 2023/10/10取得, rails7でlink_toのmethod: :deleteが動作しない場合の対処
③@gen_signup(げん), 2023/10/10取得, Dockerでannotateを起動させる手順
④ひつじもん (id:Hitsuji_mon), 2023/10/10取得, Rails 日記App制作 ~Rails5.2以上でのredirect_backの使い方 | Git~
⑤Bootstrap, 2023/10/10取得, ドロップダウン
⑥@dmrt, 2023/10/10取得, 【解決済】Ruby on Rails 6のrspec-rails実行で「raise WrongScopeError, name
is not available from within an example (e.g. an it
block) ...」