Rails学習　3日目その2

#Ruby on Rails5速習実践ガイド chapter7

##7-1　登録や編集の実行前に確認画面をはさむ
登録する前に確認画面が出てきてそこで確認してからデータが登録されるように設定する。
まずは確認画面が表示されるアクション（conform_newアクション）をコントローラーに書く

```ruby:app/controller/tasks_controller.rb
 def confirm_new
   @task = current_user.tasks.new(task_params)
   render :new unless @task.valid? #もしデータベースに@taskがなかったら実行
```

その次にルーティングを記入

```ruby:config/routes.rb
resources :tasks do
  post :confirm, action: :confirm_new, on: :new
end
```
 post :confirm, action: :confirm_new, on: :new
この部分は元々以下の形であった

```ruby:config/routes.rb
resources :tasks do
  new do
   post :confirm, action: :confirm_new
  end
end
```
newブロック内に要素が一つだけの場合は後ろにon: :ブロック名とすることでルーティングがかける

次に確認画面のビューを作る

```slim:app/views/tasks/confirm_new.html.slim
h1 新規内容の確認

= form_with model: @task, local: true do |f|
  table.table.table-hover
    tbody
      tr
        th= Task.human_attribute_name(:name)
        td= @task.name
        =f.hidden_field :description
      tr
        th= Task.human_attribute_name(:description)
        td= simple_format(@task.description)
        =f.hidden_field :description
  =f.submit '戻る', name: 'back', class: 'btn btn-secondary mr-3'
  =f.submit '登録', class: 'btn btn-primary'
```

##7-1-3　登録アクションで「戻る」ボタンからの遷移に対応する。
上記の確認画面のビューには戻るボタンと登録ボタンの2つがある。戻るボタンを機能させようと思ったらこの二つのボタンのどちらが押されているかの判断をする必要がある。そのために戻るボタンにはname属性にbackをつけた。プログラミング上ではもしname属性のbackが押されたら元に戻るという操作になる。
つまりparams[:back]の結果が出れば良い。
params[:back]が押されると元に戻るという動作をcreateアクションに書けば良い

```ruby:app/controllers/tasks_controller.rb
def create
  @task = current_user.tasks.new(task_params)

  if params[:back].present? #present?は値がある場合trueとなる真偽値
    render :new
    return
  end
```

>真偽値で使うメソッドの種類

|メソッド名|メソッドの意味|
|:---|:---|
|nil?|変数の値がnil、または値がないときにtrueになる|
|empty?|変数の値が""（文字列の場合）や値が空白の場合、真となります。nil?との違いは、empty?は変数の値はあることはあるが、その値が空を示しているところ|
|blank?|値と言えるものがない場合にtrueとなる|
|present?|値と言えるものがある場合はtrueとなる|


##7-2　一覧画面に検索機能を追加する
タスク一覧にあるタスクの数が多くなってくるといちいち探し出すのがめんどくさくなってくる。そんな時に検索機能があった方が便利だろう。
検索機能をつけるにはRansackというgemを使う

7-2-2　名称による検索
Ransackを使って検索機能を追加する

```ruby:app/controllers/tasks_controller.rb
def index
  @q = current_user.task.ransack(params[:q])
  @task = @q.result(distinct: true).recent
end
```
 >@q = current_user.task.ransack(params[:q])

ransackではquery parameter（params[:q]）を取って検索をかける
ログイン中のユーザーのtaskをransackで検索しそれを＠qと置いている

 >@task = @q.result(distinct: true).recent

検索で出た＠qを
重複する検索結果を除外しつつ（result(distinct: true）)
登録日時の並びで表示する(recent)

ransackが実装できたので実際にビューで検索できるようにしてみる
検索機能の追加にはsearch_form_forを使う

```slim:app/views/tasks/index.html.slim
h1 タスク一覧

= search_form_for @q, class: 'mr-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'

= link_to '新規登録', new_task_path, class:  'btn btn-primary mb-3'
```
name_cont：nameと部分一致している物を選ぶ。
.search_field：記入場所
created_at_gteq：記入した投稿日時より大きい（未来:gteq）の投稿を選ぶ

##7-3　一覧画面にソート機能を追加する

```ruby:app/controllers/tasks_controller.rb
def index
  @q = current_user.task.ransack(params[:q])
  @task = @q.result(distinct: true).recent
end
```

recentでソートをしていたが次は任意の順番でソートをする
まずresentをとる

```ruby:app/controllers/tasks_controller.rb
def index
  @q = current_user.task.ransack(params[:q])
  @task = @q.result(distinct: true)
end
```

```ruby:app/views/tasks/index.html.slim
table.table.table-hover
  thead.thead-default
    tr
      th= sort_link(@q, :name, default_order: :desc)
      th= Task.human_attribute_name(:created_at)
      th
```
sort_linkをつけるとソート操作ができるようになる
第一引数にはransackによって得られた値（ここでは@q）
第二引数にはソートを行う対象のカラム（ここではname）
default_order: :desc

##7-4　メールを送る
Railsはメールを送るためにメイラーという仕組みを利用している。メイラーを使いメールを作成・送信する。
メイラーはメイラーファイルに記述する

```ruby:app/mailers/task_mailer.rd
class TaskMailer < ApplicationMailer
  def creation_email(task)
    @task = task
    mail(
      subject: 'タスク作成完了メール'
      to: 'user@example'
      from: 'taskleaf@example'
     )
  end
end
```

subjectやto、fromといった情報のmailをcreation_emailメソッドとしておく

##7-4-3　メールの送信処理
実際にメールを送信できるようにしていく

```ruby:app/controllers/tasks_controller.rb
def create
  @task = current_user.tasks.new(task_params)

  if params[:back].present?
    render :new
    return
  end

  if @task.save
    TaskMailer.creation_email(@task).deliver_now
    redirect_to @task, notice: "タスク「#{@task.name}」を登録しました"
  else
    render :new
  end
end
```
上記のようにTaskMailer.creation_email(@task).deliver_nowをつけると@taskについてのメールが送信される。deliver_nowは即時送信を行うためのメソッド。他にもdeliver_laterメソッドも存在している

##7-5　ファイルをアップロードしてモデルに添付する
Active Storageというファイル管理gemが出てきて、それらを使って写真や画像を添付する
実際に添付するには以下の方法で添付する

```ruby:app/models/task.rb
class Task < ApplicationRecord
  has_one_attached :image
....

end
```

has_one_attachedメソッドを使うことによって1つのタスクに1つの画像（image）を紐付けることが可能になる。

```ruby:app/views/tasks/_form.html.slim
...
...
...
.form-group
  =f.label :image
  =f.file_field :image, class: 'form-control'
...
end
```

ja.ymlにも「image:画像」と設定しておく
task_paramsメソッドのpermitもimageも許可しておく

確認画面にも表示されるようにコードを書く

```ruby:app/views/tasks/show.html.slim
...
tr
  th= Task.human_attribute_name(:image)
  td= image_tag @task.image if @task.image.attached?
tr
...
...
...
```
if @task.image.attached?は画像が添付されているかどうかを表す。
実際に画像があればimage_tagで表示する

##7-7　ページネーション
タスクが多くなればなるほど今までやっていた情報を全て表示する方法では動作が重たくなってきてしまう。ページネーションは一定数以上のファイル数になればそれ以上の表示は次のページへ渡すなどをし、ページによって情報を分散させ動作を軽くする方法。
このページネーションにはkaminariというgemファイルが用いられる。

元々のtasks_controllerファイルを

```ruby:app/controllers/tasks_controller.rb
def index
  @q = current_user.task.ransack(params[:q])
  @task = @q.result(distinct: true)
end
```

.page(params[:page])を後ろにつけることによって１ページあたりに表示する件数を決めることができる（デフォルトで１ページ２５件）

```ruby:app/controllers/tasks_controller.rb
def index
  @q = current_user.task.ransack(params[:q])
  @task = @q.result(distinct: true).page(params[:page])
end
```

次に実際にビューファイルに反映されるようにする

```ruby:app/views/tasks/index.html.slim
.mb-3
  =paginate @tasks
  =page_entries_info @tasks
...
...
...
```

paginateヘルパーメソッドとpage_entries_infoヘルパーメソッドを使うことによって自動的に作成してくれる。

##7-8 非同期処理や定期実行を行う（Jobスケジューリング）
Railsではバックグラウンドで様々な処理を非同期に行うためのActive Jobというフレームワークが用意されている。ActiveJobは以下の時に使われる
>・処理が重たくユーザーを待たせてしまっている
・指定した日時にアクションを起こしてほしい

そんな時に使うのがsidekiqという仕組みである

①まずRailsとsideskiqを同期する

```ruby:config/environments/development.rb
config.active_job.queue_adapter = :sideskiq
```
②app/jobs/sample_job.rbを作りperformメソッドを作る

```ruby:app/jobs/sample_job.rb
class SampleJob < ApplicationJob
  queue_as :deault

  def perform(*args)
    Sidekiq::Logging.logger.info "サンプルジョブを実行しました"
  end
end
```

performメソッドを呼び出して処理する

```ruby:app/controller/tasks_controller.rb
def create
  @task = current_user.tasks.new(task_params)
  ...
  if @task.save
    TaskMailer.created_email(@task).deliver_now
    SampleJob.perform_later
...
...
...
```
SampleJob.perform_laterが非同期処理を行うようになる

##7-8-3　実行日時指定
setメソッドを使うと日時を指定して処理を実行できるようになる

```
#翌日の正午に実行
SampleJob.set(wait_until: Date.tomorrow.noon).perform_later

#１週間後に実行
SampleJob.set(wait: 1.week).perform_later
```