気になった用語等をまとめる
第1, 2章_環境準備等
学んだこと
- N/A
第3章_Rubyを学ぶ
学んだこと
- 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)
学んだこと
- Dockerを使う場合は要参照
第5章_環境構築(Rails7)
学んだこと
- コンテナ内でrailsコマンドを実行する点に注意
# ①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 ...
第7章_Railsの基礎
学んだこと
- 基本理念
- 同じことは繰り返すな
- 設定より規約が優先される
- MVCについて
- ①ルーティングによりURLとCが紐付けられる
- ②CはデータをM(DB)から取得する
- ③CはデータをVに渡してHTMLを生成する
- M, Cはクラス
- Vはerbファイル
第8章_初めてのRailsアプリ開発(Rails7)
学んだこと
- 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をカスタマイズしながら進める
第9章_初めてのRailsアプリ開発(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
- その後DBに保存
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>
- 改行するにはsimple_formatを使う
<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
- ④kaminariのビューファイルを生成する
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の作成
- ルーティングの設定
- post "login", to: "sessions#create"
- delete "logout", to: "sessions#destroy"
- ユーザーモデルの認証機能設定
- ①has_secure_passwordでパスワードの設定
- ②validatesの設定
- ③ja.ymlで日本語化
- 登録フォームの作成
- ①_header.html.erbでドロップリストの作成
- ※機能しなかったためbootstrapのサイトの例示を参考に書き換えた(参考文献⑤参照)
- ②ユーザーコントローラーで@userを新規作成
- ③_form.html.erbで画面の作成
- ①_header.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
- ②meビューでマイページの作成
<h1>マイページ</h1> <%= @current_user.name %>
第11章_Railsのバージョンアップ
学んだこと
- ①Gemfileのrailsのバージョンを変更
- ②以下により関連するgemも含めてアップデートされる
docker-compose run web bundle update rails
- ③イメージの再作成
- ④エラー箇所は設定ファイルを更新
config/initializers/new_framework_defaults.rb # ※機能の廃止などでエラーとなるためバージョンアップは慎重に行う
第12章_form_withヘルパーによるフォームの作成
学んだこと
- 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によるテスト
学んだこと
- ①gem "rspec-rails", "~> 4.0.1" ※4.0.1以上を指定(参考文献⑥参照)
- ②docker compose run web bundle
- ③docker compose run web bundle exec rails g rspec:install(rspecの設定ファイルの作成)
- ④docker compose exec web bundle exec rails g rspec:model User(Userモデルのテストファイル)
- ⑤docker compose exec web bundle exec rails g rspec:controller Users
- ユニットテストは特定のオブジェクトやメソッドのテスト
- テストはテスト用のDBが自動で使われる
- モデルのテスト
- docker compose exec web bundle exec rspec ./spec/models/(テストコードの実行)
- docker compose exec web bundle exec rails g migration AddbirthdayToUser birthday:date
- docker compose exec web bundle exec rails db:migrate
- user_spec.rbにテストコードを記載
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を元にビルド
- docker compose build
- ②コンテナ起動
- docker compose up
- ③DB作成
- ④マイグレーションファイルで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を追加した時の対応
- ①インストール
- docker-compose run web bundle
- ②ビルド
- docker compose build
参考文献
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) ...」