system spec

gemの導入

group :development, :test do
  gem 'byebug', platforms: %i[mri mingw x64_mingw]
  gem 'factory_bot_rails'
  gem 'rspec-rails', '~> 5.0.0'
  gem 'webdrivers'
  gem 'capybara'
end
  

rspecでcapybaraを使うために必要な機能を読み込む

require 'capybara/rspec'

system specのファイルを作成

$ rails g rspec:system tasks
Running via Spring preloader in process 2824
      create  spec/system/tasks_spec.rb

ドライバの設定

ドライバとは、capybaraにおいて、ブラウザの機能を利用するためのもの
spec/support/capybara.rbを作成

$ mkdir spec/support
$ touch spec/support/capybara.rb

ブラウザにheadless chromeを使用し、selenium_chrome_headlessをドライバとして指定。

  RSpec.configure do |config|
    config.before(:each, type: :system)do
      driven_by(:selenium_chrome_headless)
    end
end

モジュールの作成

login用のメソッドをmoduleとして共通化する。

  module LoginModule
    def login(user)
      visit login_path
      fill_in 'Email', with: user.email
      fill_in 'Password', with: 'password'
      click_button 'Login'
    end
end

includeする。

  RSpec.configure do |config|
  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
  config.fixture_path = "#{::Rails.root}/spec/fixtures"
  config.include FactoryBot::Syntax::Methods
  config.include LoginModule

spec/support以下を読み込ませる。

  Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }

systemspecの設定

require 'rails_helper'

RSpec.describe "Users", type: :system do
  let(:user) { create(:user) }
  let(:other_user) { create(:user) }

  describe 'ログイン前' do
    describe 'ユーザー新規登録' do
      context 'フォームの入力値が正常' do
        it 'ユーザーの新規作成が成功する' 
      end

      context 'メールアドレスが未入力' do
        it 'ユーザーの新規作成が失敗する' 
      end

      context '登録済のメールアドレスを使用' do
        it 'ユーザーの新規作成が失敗する' do
          existed_user = create(:user)
          visit sign_up_path
          fill_in 'Email', with: existed_user.email
          fill_in 'Password', with: 'password'
          fill_in 'Password confirmation', with: 'password'
          click_button 'SignUp'
          expect(page).to have_content '1 error prohibited this user from being saved'
          expect(current_path).to eq users_path
          expect(page).to have_content("Email has already been taken")
          expect(page).to have_field 'Email', with: existed_user.email
        end
      end
    end
  end

  describe 'ログイン後' do
    before { login(user) }

      describe 'ユーザー編集' do
        context 'フォームの入力値が正常' do
        it 'ユーザーの編集が成功する' 
      end
        context 'メールアドレスが未入力' do
          it 'ユーザーの編集が失敗する' 
        end

        context '登録済みのメールアドレスを使用' do
          it 'ユーザーの編集が失敗する' do
            visit edit_user_path(user)
            fill_in 'Email', with: other_user.email
            fill_in 'Password', with: 'password'
            fill_in 'Password confirmation', with: 'password'
            click_button 'Update'
            expect(page).to have_content('1 error prohibited this user from being saved')
            expect(page).to have_content("Email has already been taken")
            expect(current_path).to eq user_path(user)
          end
        end
        
        context '他ユーザーの編集ページにアクセス' do
          it '編集ページへのアクセスが失敗する' do
            visit edit_user_path(other_user)
            expect(current_path).to eq user_path(user)
            expect(page).to have_content("Forbidden access.")
          end
        end
      end

      describe 'マイページ' do
        context 'タスクを作成' do
          it '新規作成したタスクが表示される' do
            create(:task, title:'test', status: :doing, user: user)
            visit user_path(user)
            expect(page).to have_content('You have 1 task.')
            expect(page).to have_content('test')
            expect(page).to have_content('doing')
            expect(page).to have_link('Show')
            expect(page).to have_link('Edit')
            expect(page).to have_link('Destroy')
          end
        end
      end
    end
end
  require 'rails_helper'

RSpec.describe 'Tasks', type: :system do
  let(:user) { create(:user) }
  let(:task) { create(:task) }

  describe 'ログイン前' do
    describe 'ページ遷移確認' do
      context 'タスクの新規登録ページにアクセス' do
        it '新規登録ページへのアクセスが失敗する' do
          visit new_task_path
          expect(page).to have_content('Login required')
          expect(current_path).to eq login_path
        end
      end

      context 'タスクの編集ページにアクセス' do
        it '編集ページへのアクセスが失敗する' do
          visit edit_task_path(task)
          expect(page).to have_content('Login required')
          expect(current_path).to eq login_path
        end
      end

      context 'タスクの詳細ページにアクセス' do
        it 'タスクの詳細情報が表示される' do
          visit task_path(task)
          expect(page).to have_content task.title
          expect(current_path).to eq task_path(task)
        end
      end

      context 'タスクの一覧ページにアクセス' do
        it 'すべてのユーザーのタスク情報が表示される' do
          task_list = create_list(:task, 3)
          visit tasks_path
          expect(page).to have_content task_list[0].title
          expect(page).to have_content task_list[1].title
          expect(page).to have_content task_list[2].title
          expect(current_path).to eq tasks_path
        end
      end
    end
  end

  describe 'ログイン後' do
    before { login(user) }

    describe 'タスク新規登録' do
      context 'フォームの入力値が正常' do
        it 'タスクの新規作成が成功する' do
          visit new_task_path
          fill_in 'Title', with: 'test_title'
          fill_in 'Content', with: 'test_content'
          select 'doing', from: 'Status'
          fill_in 'Deadline', with: DateTime.new(2020, 6, 1, 10, 30)
          click_button 'Create Task'
          expect(page).to have_content 'Title: test_title'
          expect(page).to have_content 'Content: test_content'
          expect(page).to have_content 'Status: doing'
          expect(page).to have_content 'Deadline: 2020/6/1 10:30'
          expect(current_path).to eq '/tasks/1'
        end
      end

      context 'タイトルが未入力' do
        it 'タスクの新規作成が失敗する' do
          visit new_task_path
          fill_in 'Title', with: ''
          fill_in 'Content', with: 'test_content'
          click_button 'Create Task'
          expect(page).to have_content '1 error prohibited this task from being saved:'
          expect(page).to have_content "Title can't be blank"
          expect(current_path).to eq tasks_path
        end
      end

      context '登録済のタイトルを入力' do
        it 'タスクの新規作成が失敗する' do
          visit new_task_path
          other_task = create(:task)
          fill_in 'Title', with: other_task.title
          fill_in 'Content', with: 'test_content'
          click_button 'Create Task'
          expect(page).to have_content '1 error prohibited this task from being saved'
          expect(page).to have_content 'Title has already been taken'
          expect(current_path).to eq tasks_path
        end
      end
    end

    describe 'タスク編集' do
      let!(:task) { create(:task, user: user) }
      let(:other_task) { create(:task, user: user) }
      before { visit edit_task_path(task) }

      context 'フォームの入力値が正常' do
        it 'タスクの編集が成功する' do
          fill_in 'Title', with: 'updated_title'
          select :done, from: 'Status'
          click_button 'Update Task'
          expect(page).to have_content 'Title: updated_title'
          expect(page).to have_content 'Status: done'
          expect(page).to have_content 'Task was successfully updated.'
          expect(current_path).to eq task_path(task)
        end
      end

      context 'タイトルが未入力' do
        it 'タスクの編集が失敗する' do
          fill_in 'Title', with: nil
          select :todo, from: 'Status'
          click_button 'Update Task'
          expect(page).to have_content '1 error prohibited this task from being saved'
          expect(page).to have_content "Title can't be blank"
          expect(current_path).to eq task_path(task)
        end
      end

      context '登録済のタイトルを入力' do
        it 'タスクの編集が失敗する' do
          fill_in 'Title', with: other_task.title
          select :todo, from: 'Status'
          click_button 'Update Task'
          expect(page).to have_content '1 error prohibited this task from being saved'
          expect(page).to have_content "Title has already been taken"
          expect(current_path).to eq task_path(task)
        end
      end
    end

    describe 'タスク削除' do
      let!(:task) { create(:task, user: user) }

      it 'タスクの削除が成功する' do
        visit tasks_path
        click_link 'Destroy'
        expect(page.accept_confirm).to eq 'Are you sure?'
        expect(page).to have_content 'Task was successfully destroyed'
        expect(current_path).to eq tasks_path
        expect(page).not_to have_content task.title
      end
    end
  end
end

Model Spec

概要

taskモデルのバリデーションに関するテストを作成する。
FactoryBotを使用しテストデータを作成する。

バリデーションエラーの発生と、エラーメッセージの内容を表示させる。
各バリデーションを削除した際、テストが失敗することを確認する。

FactoryBotの設定

FactoryBot.の省略
以下の設定を追加すると、factory_botメソッドの前にFactoryBot.を付ける必要がなくなる。
FactoryBot.create(:user)create(:user)と省略して書ける。

RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end

UserとTaskモデルのテストデータを作成

  FactoryBot.define do
  factory :task do
    sequence(:title, "title_1")
    content { 'Content' }
    status { 'todo' }
    deadline { Date.current.tomorrow }
    association :user
  end
end
  FactoryBot.define do
    factory :user do
      sequence(:email) { |n| "tester#{n}@example.com" }
      password { 'password' }
      password_confirmation { 'password' }
    end
  end

今回のUserとTaskのアソシエーションは1対多(belongs_to と has_many)である。
ユニーク制約のついているカラムではsequenceメソッドを使用する。
association :userでuserとの関連を定義。

sequenceメソッド

sequenceの第二引数で定義した文字にはループの度に.nextが実行される。

> "title_1".next
 => "title_2"

sequence(:title){|n| "title_#{n}"}sequence(:title, "title_1")のように書ける。

TaskのModel Spec

taskモデルのvalidationに関するテストを記述していく。

  • 全部の属性を登録したとき有効
  • titleがないときに無効
  • titleが重複したとき無効
require 'rails_helper'

RSpec.describe Task, type: :model do
  describe 'validation' do
    it "is valid with all attributes" do
      task = build(:task)
      expect(task).to be_valid
      expect(task.errors).to be_empty
    end

    it "is invalid without a title" do
      task_without_title = build(:task, title: "")
      expect(task_without_title).to be_invalid
      expect(task_without_title.errors[:title]).to eq ["can't be blank"]
    end

    it "is invalid with a duplicate title" do
      task = create(:task)
      task_with_duplicated_title = build(:task, title: task.title)
      expect(task_with_duplicated_title).to be_invalid
      expect(task_with_duplicated_title.errors[:title]).to eq ["has already been taken"]
    end
  end
end

describe' 'do...endでテスト内容の宣言。
今回はvalidationに関するテストを宣言している。

it " " do...endには、実行するテスト内容を記述する。

be_○○○マッチャ

Rspecでは、empty? のようにメソッド名が?で終わり、戻り値が true / false になるメソッドを be_empty のような形式で検証できる。
expect(task).to be_validで記述するのがexpect(期待する)で、ここでは
taskがto be_validすることを期待するというテストとなる。
be_validはマッチャと呼ばれる機能でexpectの引数にしたインスタンスにバリデーションエラーが発生しないことを検証する。(task.valid? が true になればパスする)

expect(task.errors).to be_emptyでtask.errorsがemptyであることを期待する。

省略

task = FactoryBot.build(:task, title: "", user: user)task = build(:task)と書ける。
FactoryBot.buildでTaskのFactoryにuserオブジェクトを指定。
設定ファイルへ追記しているので、FactoryBot.の部分は省略できる。
taskのFactoryにassociationを定義しているのでuserの記述を省略できる。

buildとcreateの違い

buildはテストデータをメモリ上に保存するが、createはデータベース上に保存する。
データベースへのアクセスが必要なテストの場合はcreateを使用し、必要でない場合はbuildを使えば良い
以下、idや作成日時を見るとbuildではnilとなっていて、createではデータベース上に保存されているので値が表示されている。
f:id:meo2:20210731174744p:plain

rspecセットアップ

概要

GitHubからサンプルアプリを fork, clone 後、ローカルでのrspec実行環境をセットアップする。

ローカルへのコピー

GitHubからサンプルアプリをforkし、ターミナルからcloneする。
$ git clone URL
新たなブランチの作成
$ git checkout -b ブランチ名

セットアップ

gem rspecを参考にインストール。
gem 'factory_bot_rails'
gem 'rspec-rails', '~> 5.0.0'の導入。
group :development, :test へ追記しbundleする。
(開発環境でもrails console等で動作確認ができる。)

group :development, :test do
  gem 'byebug', platforms: %i[mri mingw x64_mingw]
  gem 'factory_bot_rails'
  gem 'rspec-rails', '~> 5.0.0'
end  

ジェネレーターを使ってrspecに必要な設定ファイルをインストールする。

$ rails generate rspec:install
      create .rspec
      create spec
      create spec / spec_helper.rb
      create spec / rails_helper.rb

ジェネレーターを使ってtaskモデルのspecの雛形を作成。

$ rails generate rspec:model task
      create spec/models/task_spec.rb
      invoke factory_bot
      create spec / factories/tasks.rb

rspecコマンドでテストが実行できるか確認。

$ bundle exec rspec            
*

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) Task add some examples to (or delete) /Users/owner/rspec1/sample_app_for_rspec/spec/models/task_spec.rb
     # Not yet implemented
     # ./spec/models/task_spec.rb:4


Finished in 0.00168 seconds (files took 0.61562 seconds to load)
1 example, 0 failures, 1 pending

Git,GitHub基礎

Gitとは

ファイルのバージョン管理をするツールのこと。
バージョン管理とは、ソースコードなどのファイルの追加や変更履歴を管理することで、過去の変更を確認したり、特定の場所にファイルを戻したりできること。

バージョン管理を使えば、いつ、どこで、誰が、どのようなファイル変更をしたのか記録でき、いつでも以前の状態に戻せる。

バージョン管理には分散型と集中型の2種類があり、Gitは分散型のバージョン管理システムである。
分散型バージョン管理システムであるGitは開発者自身のPCにローカルリポジトリを持ち、ここに修正や追加したソースコードをcommitすることができる。

ローカルリポジトリへcommitされたソースコードはリモートリポジトリへpushすることでローカルとリモートのリポジトリ内容を合わせることができる。

GitHubとは

ソースコードの管理サービスのことで、主にリモートリポジトリとして活用される。
Gitで管理しているソースコードをチームで簡単に共有できるwebサービス

リポジトリ

バージョン管理によって管理されたファイルやディレクトリの状態や変更履歴を保管するところをリポジトリと呼ぶ。

リポジトリの種類

f:id:meo2:20210723203727p:plain

開発過程をリモートリポジトリへ登録する流れ

  • ステージング(add): ワークツリー(実際に作業してるディレクトリ)で変更があるファイルをステージングエリアへ追加する
  • コミット(commit): ステージングエリアからローカルリポジトリへ登録
  • プッシュ(push): ローカルリポジトリに登録されたファイルをリモートリポジトリへ追加

    コマンド

    $ git status(ステージングにファイルが存在しているか確認)
    $ git add -A( 変更があったすべてのファイルがaddされる) この状態をステージングという。
    $ git commit -m '(適切な内容のコミットメッセージ)'
    $ git push origin(リモートへ変更を反映させる)
    $ git rm -r cached ファイル名(リモートからファイルを削除する。)
    cachedオプションをつけないとローカルからも削除される。
    .gitignoreへ削除したいディレクトリを指定して実行。
    .gitignoreとは、Gitの管理対象に含めないファイルを指定するファイル。

    pullとfetch

  • pull: リモートリポジトリの変更履歴をローカルへ取得。
    リモートのmasterブランチからorigin/masterを介してローカルのmasterまで一気に変更する。
    $ git checkout master
    masterブランチへ移動
    $ git pull origin master
    origin(複製元リモートリポジトリ)の変更をローカルへ取り込む

  • fetch: リモートリポジトリの変更履歴をローカルへ取得。
    $ git fetch origin master
    pullとは違い、origin/masterに変更が取り込まれる。
    $ git merge origin master
    マージしてorigin/masterからmasterへ変更を取り込む。

リポジトリのcloneとfork

既にリモートリポジトリが存在する場合、リモートリポジトリをローカルリポジトリへcloneする。
cloneとは、リモートリポジトリからローカルリポジトリへ複製すること。
cloneすると複製元のリモートリポジトリにはoriginという名前がつく。
$ git clone URL
URLはGitHubから確認する。
f:id:meo2:20210724043336p:plain:w300:h220
forkとは、他人のリモートリポジトリを自分のアカウントのリモートリポジトリへ複製すること。
forkするにはGitHubで右上にあるforkボタンを押すだけ
f:id:meo2:20210724044845p:plain:w300:h50
forkしたら複製した自分のリモートリポジトリをcloneして開発を進めていく。

ブランチ

Gitにはブランチ(branch)という機能があり、
masterから変更履歴を分岐させ、新しい作業場所を作ることをブランチを切ると言う。
f:id:meo2:20210724162956p:plain
ブランチの作成
$ git checkout -b ブランチ名
現在のブランチの確認
$ git branch

GitHub Flow

GitHubではmasterからブランチを切って開発を進め、リモートにpushした後、プルリクエストと呼ばれる機能でコードレビューを行いマージをする。(この開発の流れをGitHub Flowという)
f:id:meo2:20210724195935p:plain

実践

GitHubリポジトリを作成し、cloneする。
$ git clone URL
f:id:meo2:20210724201303p:plain
ローカルでプルリクエスト用のブランチを切る。
$ git checkout -b pullreq1
リモートへpushする。
$ git add -A
$ git commit -m ''
$ git push
GitHub上でプルリクエストを作成する。
GitHub上のリポジトリページからPull requestsを選択し、
マージ先のブランチとプルリクエスト対象のブランチを選択する。
f:id:meo2:20210725052931p:plain

コミット履歴やファイル差分を確認して、Create pull requestする。

f:id:meo2:20210725053231p:plain

プルリクエストを受け取ったユーザーはFiles changedからソースコードを確認し、修正がある場合は commentから修正を促す。変更に問題がなければ、Merge pull requestを実行する。
pullreq1ブランチの内容がmasterへ反映される。

f:id:meo2:20210725053920p:plain

マージ後、pullreqリポジトリのmasterブランチを表示すると、プルリクエストでpullreq1ブランチをmasterブランチへ取り組んだことによりREADME.mdの記述が変わっていることが確認できる。

f:id:meo2:20210725055521p:plain

javascriptDOMの操作

DOMとは

DOM( Document Object Model )はHTMLやXMLを取り扱うためのAPIのこと。 DOMを使えば、HTMLの探索やスタイルの変更・イベントの設定・HTML要素の取得の他に、振る舞いを変えたり、ユーザー操作時の処理を設定することができる。

htmlを用意する

DOMを操作するにはidやclassが必要
テキストエリアとボタンを作成

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>DOM1</title>
  <style>
  </style>
</head>
<body>
    <textarea id="textarea"></textarea>
    <button id="button">ボタン</button>
</body>
</html>

以下のように表示される
f:id:meo2:20210721163113p:plain:w200:h50

練習: ボタンをクリックしたらテキストの入力値を取得しアラートとして表示させるようにする

scriptタグを追加し、要素を記述していく。

~
<body>
    <textarea id="textarea"></textarea>
    <button id="button">ボタン</button>
    
    <script>
        'use strict';
    const button = document.getElementById('button')
    button.addEventListener('click', () => {
      const textarea = document.getElementById('textarea')
      alert(textarea.value)
    })
    </script>
</body>
~

documentオブジェクト

documentには、HTML、CSSJavaScriptなど全体のドキュメントが入っている。
documentオブジェクトで要素を取得し、変数buttonへ代入する。
getElementById('button')でbuttonというidの要素を取得する。idはDOMツリー中で一意である必要がある。
コンソールに入力すると、#documentという、HTMLファイル内の情報全てを取得することができる。 f:id:meo2:20210721173409p:plain

イベントトリブンの設定

イベントドリブンとは、なんらかのアクション(ボタンをクリックする、画面のリロード、など)をしたら何かが起きる処理のこと。
先程定義したDOMに対して、addEventListenerを設定してあげることでイベントを仕掛けることができる。
button.addEventListener('click', () => { })でbuttonをクリックしたときのイベントを設定できる。

テキストの入力値を取得してアラート表示させたいので、テキストのDOMを取得する。
const textarea = document.getElementById('textarea')
alert(textarea.value)valueで入力値を取得。
これで、アラートが表示されるようになる。
f:id:meo2:20210721172936p:plain

gem enum_helpを使用し、enum値のi18n化と権限のプルダウンを実装

前提

enumとransackは導入済み
enum
ransack

実装内容

enumで定義した値の翻訳
ユーザー一覧画面でのプルダウン検索
f:id:meo2:20210712031757p:plain:w500:h200

導入

gem 'enum_help'

bundle install

enumの設定

  • general・・・一般
  • admin・・・管理者 と定義する。
  enum role: { general: 0, admin: 1 }

i18nの設定

 enums:
   user:
     role:
       general: '一般'
       admin: '管理者'  

view

viewで以下のように記述することで呼び出せる。

<%= user.role_i18n %>

プルダウンの実装

controller

  def index
    @q = User.ransack(params[:q])
    @users = @q.result(distinct: true).page(params[:page])
  end

params[:q]で検索フォームに入力した文字を取得(viewから送られてきたパラメータ)デフォルトで:qとなっている。→インスタンス変数@qを定義。
ransackメソッド、送られてきたパラメータをもとにテーブルからデータを検索。
resultメソッドでransackメソッドで取得したデータをオブジェクトに変換し、検索結果を渡す。
distinct: trueで重複を防ぐ
pageメソッドでページネーションの実装。

view

  <%= search_form_for @q, url: admin_users_path do |f| %>
    <div class="row">
        <div class="form-inline align-items-center mx-auto">
            <div class="col-auto">
              <%= f.search_field :first_name_or_last_name_cont, placeholder:'検索ワード', class:'form-control' %>
            </div>
            <div class="col-auto">
              <%= f.select :role_eq, User.roles_i18n.invert.map { |key, value| [key, User.roles[value]]}, include_blank:'指定なし', class:'form-control mr-1' %>
            </div>
            <div class="col-auto">
              <%= f.submit '検索', class:'btn btn-primary' %>
            </div>
        </div>
    </div>
<% end %>

search_form_forメソッドはransackで用意されているメソッド
form_withやform_forと同じ
検索オブジェクトqを指定。
urlオプションでリクエストするurlを渡す。→renderでパーシャルを読み込む時に指定。

<%= f.select :role_eq, 
User.roles_i18n.invert.map { |key, value| [key, User.roles[value]]}, 
include_blank:'指定なし', #第3引数 
class:'form-control mr-1' %> #第4引数,htmlオプション

について
role_eq、eq(equals)は ransackのメソッド、等しい値を持つレコードを返す。
invertメソッドでkeyとvalueを入れ替える
mapメソッド配列.map {|変数名| 具体的な処理 }で戻り値を配列へ返す。
include_blankオプションは先頭に表示されるメッセージに空白行を表示する。

f.select

f.selectを使うと、セレクトボックスの選択項目や選択された時の値や、セレクトボックスに設定したいオプションを指定できる。

<%= f.select :保存されるカラム名, [ ["表示される文字","保存される値"], {オプション}, {htmlオプション} ] %>

User.rolesでuserモデルのroleの値(ハッシュ)を取得
{キー1 => 値1, キー2 => 値…}

pry(main)> User.roles
=> {"general"=>0, "admin"=>1}

enum_helpの導入により、User.roles_i18nというメソッドを使えるようになる。
i18nで設定した日本語を適用

pry(main)> User.roles_i18n
=> {"general"=>"一般", "admin"=>"管理者"}

invertメソッドでハッシュのkeyとvalueを入れ替える。
f.select :保存されるカラム名, [ ["表示される文字","保存される値"]に合わせたいので、ハッシュを入れ替える。
こうすることで、プルダウンが英語→日本語に切り替わる。

pry(main)> User.roles_i18n.invert
=> {"一般"=>"general", "管理者"=>"admin"}

mapメソッドでハッシュを配列にする。
一般、 管理者というキーをkey に、general 、adminという値が valueへ代入。

pry(main)> User.roles_i18n.invert.map { |key, value| [key, User.roles[value]]}
=> [["一般", 0], ["管理者", 1]]

ransackはenumで定義したstringに対応していないので、integerにする必要がある。
[key, User.roles[value]]valueをintegerにしている。

pry(main)> User.roles['general']
=> 0
pry(main)> User.roles['admin']
=> 1

メニューのアクティブ・非アクティブ化

管理画面において、以下のようにサイドバーで選択(クリック)した部分を青色(アクティブ)にさせる。
f:id:meo2:20210712020932p:plain:w200:h180

実装

ヘルパーにメソッドを定義する。

  def active_if(path)
    path == controller_path ? 'active' : ''
  end

controller_path?でcontroler名を取得する。
controller_path?がtrueの場合、activeを返し、そうでない場合何も返さない。
三項演算子
if文の単純な分岐を短く書くことができる。

if 条件
 式1
else
 式2
end
条件 ? 式1 : 式2

上記で定義したヘルパーメソッドをview側へ記述する。

  <%= link_to admin_boards_path, class: "nav-link #{active_if('admin/boards')}" do %>
  <%= link_to admin_users_path, class: "nav-link #{active_if('admin/users')}" do %>

path == controller_path ?により、リンク先に指定したpathとコントローラーが等しければactiveを返す。(青色になる)