「Ruby on Rails5速習実践ガイド」を読んだので。

[Ruby on Rails5速習実践ガイド](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Ruby-Rails-5%E9%80%9F%E7%BF%92%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E5%A4%A7%E5%A0%B4%E5%AF%A7%E5%AD%90/dp/4839962227/ref=sr_1_6?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=7NL117JN69F3&keywords=Rails&qid=1642106965&sprefix=rails%2Caps%2C252&sr=8-6) を読んだので、個人的に気になったことなどをまとめてます。


# 学んだこと

### RSpec, Capybara, FactoryBotを用いたテストコード
- Rspec：Rubyのテスティングフレームワーク
- Capybara：WebアプリケーションのE2Eテスト用フレームワーク(Rails 5.1からデフォルトで同梱されている)
- FactoryBot：テスト用データ作成のgem

1. RSpecインストール
    ```ruby:Gemfile
    gem 'rspec-rails', '-> 3.7'
    gem 'factory_bot_rails', '-> 4.11'
    ```
    
    ```bash:bash
    $ bundle
    $ rails g rspec:install
    ```
1. Capybaraの設定
    `spec/spec_helper.rb`にブラウザにHeadless Chromeを使うことを記述。
    ```ruby:spec/spec_helper.rb
    require 'capybara/rspec' # 追記
    
    RSpec.configure do |config|
       # 追記
      config.before(:each, type: :system) do
        driven_by :selenium_chrome_headless
      end
      ...
    ```
1. FactoryBotでテストデータを作成
    ```ruby:spec/factories/users.rb
    FactoryBot.define do
      factory :user do
        name { 'テストユーザ' }
        email { 'test1@example.com' }
        passsword { 'password' }
      end
    end
    ```

1. テストコードを作成
    ```ruby:rspecテンプレート
    describe '~機能(テスト対象)', type: :system do
      describe '登録' do
    
        context '○○の場合(ある状況・状態)' do
          before do
            # 事前処理
          end
    
          it '○○する(期待される値・概要)' do
            # 期待する処理
          end
        end
    
        context '××の場合(ある状況・状態)' do
          before do
            # 事前処理
          end
    
          it '××する(期待される値・概要)' do
            # 期待する処理
          end
        end
      end
    
      describe '削除' do
        # 略
      end
    end
    ```


1. テスト実行
```bash:
$ bundle exec rspec spec/system/tasks_spec.rb
```

### ransackを用いた検索機能
1. ransackインストール
    ```ruby:Gemfile
    gem 'ransack'
    ```
    
    ```bash:
    $ bundle
    ```

1. ソート処理を追記
    ```ruby:app/controller/tasks_controller.rb
    def index
      @q = current_user.tasks.ransack(params[:q])
      @tasks = @q.result(distinct: true).recent
    end
    ```

1. ソート項目入力欄・を追記
    ```ruby:app/views/index.html.slim
    = seach_from for @q, class: 'mb-5' do |f|
      .form-group.row
        =f.label :name_cont, '名称', class: 'col-sm-2 col-form-label'
        .col-sm-10
          = f.search_field :name_cont, class: 'form-control'
      .form-group.row
        =f.label :created_at_gteq, '登録日時', class: 'col-sm-2 col-form-label'
        .col-sm-10
          = f.search_field :created_at_gteq, class: 'form-control'
      .form-group
        f.submit class: 'btn btn-outline-primary'
      ...


    ```

1. 項目名の横のソートボタンでソートを実行
    ```ruby:app/views/index.html.slim
    tr
      th = sort_linke(@q, :name)
      th = Task.human_attribute_name(:created_at)
      th
      ...
    ```
    
    ```ruby:app/models/task.rb
        # 検索対象にすることを許可するカラムを指定する
        class Task < ApplicationTecord
          def self.randsackble_attributes(auth_object = nil)
            %w[name created_at]
          end
        
          def self.ransackble_associations(auth_object = nil)
            []
          end
        end
    ```

### kaminariを用いたページネーションの実装
1. kaminariインストール
    ```ruby:Gemfile
    gem 'kaminari'
    ```
    
    ```bash:
    $ bundle
    ```

1. controllerにページネーション機能を追加
    ```ruby:pp/controllers/tasks_controller.rb
    def index
      ...
      @tasks = @q.result(distinct: true).page(params[:page])
      ...
    ```

1. ビューにページネーションを追加
    ```ruby:app/views/tasks/index.html.slim
    .mb-3
      = paginae @tasks
      = page_entries_info @tasks
    ```

1. 日本語化
    ```yaml:config/locales/kaminari.ja.yml
    ja:
      views:
        pagination:
          first: "&laquo; 最初"
          last: "最後 &raquo;"
          previous: "&lasquo; 前"
          next: "次 &rsaquo;"
          truncate: "&hellip;"
        helpers:
          page_entries_info:
            one_page:
              display_entries:
                zero: "%{entry_name}がありません"
                one: "1件の%{entry_name}が表示されています"
                other: "%{count}件の%{entry_name}が表示されています"
            more_pages:
              display_entries: "全%{total}件中 %{first}&nbsp;-&nbsp;%{last}件の%{entry_name}が表示されています"
    ```

1. パーシャルテンプレート生成
    ```bash
    $ rails g kaminari:views bootstrap4
    ```

1. 表示件数の変更方法
    表示件数の変更方法は以下3パターンある
    1. コントローラに記述
        ```ruby:app/controllers/tasks_controller.rb
        @tasks = @q.result(distinct: true).page(params[:page]).per(50)
        ```

    1. モデルに記述
        ```ruby:app/models/task.rb
        class Task < ApplicationRecord
          paginates_per 50
        ```

    1. コンフィグファイルに記述
        ```bash
        $ rails g kaminari:config
        ```
    
        ```ruby:config/initializers/kaminari_config.rb
        Kaminari.configure do |config|
          config.defalut_per_page = 50
          ...
        ```



# 良かったところ

一覧画面での検索・ソート機能、メール送信、ファイルのアップロードなど、
一般的に使用される機能の実装方法を網羅的に学ぶことができた。

また、保守や実運用後の話があり、保守性を意識した開発を心掛けるきっかけとなった。

# 難しかったこと
自分がJavascriptの知識がないということもあるが、Javascript の連携の箇所が難しかった。

内容は理解はできるが、こういう処理をするコードを書いてねと言われたときに、
書ける自信がないので、Javascriptとの連携処理を書くときがきたら、再度読み直したいと思う。


# 参考文献

https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Ruby-Rails-5%E9%80%9F%E7%BF%92%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E5%A4%A7%E5%A0%B4%E5%AF%A7%E5%AD%90/dp/4839962227/ref=sr_1_6?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=7NL117JN69F3&keywords=Rails&qid=1642106965&sprefix=rails%2Caps%2C252&sr=8-6