Ruby on Railsに関するメモ

##高速に開発するために
・とりあえず、適切なブランチに移動し、コントローラーとViewファイルを開くor用意する

## アプリ作成時の手順
- 仕様を決める
- DB構成を決める
- システム構成(サーバー、外部サービスなど)を決める
- Railsアプリをセットアップ
- モデルとDBを作成
- モデルのテスト、ファクトリを作成
- 画面表示に必要なcontroller、viewを作成
- routingを作成
- Sassの構成を決定

##良いコードを書くために
・可能な限り処理をモデルのメソッドに切り出し、コントローラーを薄くする

・view内のロジックはhelperに逃がす

・jsはページごとのjsファイルを作ってそこに書く

・パーシャルを使い、可読性と再利用性を上げる

・住所など、繰り返し使う文字列はlocaleにまとめる

・日付をformatするとき、`strftime`メソッドを書かずに`l`メソッドとlocaleを活用するとDRYなコードになる
[【初心者向け・動画付き】Railsで日時をフォーマットするときはstrftimeよりも、lメソッドを使おう](https://qiita.com/jnchito/items/831654253fb8a958ec25)

・ネストを減らす

・最初は取っつきづらくても、出来るだけ短い書き方に慣れていった方が良い

##好きなところ
・文字列の中に変数を含ませたいとき、`"こんにちは#{name}さん"`って式展開ができて、余計な`.`とか`+`とかが無くて良い

・引数や条件式に`()`をつけなくて良い

・文末に`;`が無くて良い

・ブロックを`{}`で囲まなくて良い

・`nil`が偽として扱われるので、条件式がスッキリする

・for文を`array.each do |n|`で代用している

・ORマッパー

・resourcesでルーティングが一気に出来上がる

・gemが豊富

・link_toにdata: {confirm: "削除しますか？"}をつけると、リンク先に遷移する前にコンファームを表示することができる。

・配列の末尾にデータを追加する時の書き方`customers << record`が直感的

##好きじゃないところ
・過去に色々変更があったみたいで、古い情報を見ると今は推奨されていない書き方だったりする

・Webpackerが迷走してるっぽい。JSのベストプラクティスがいまいちわからない

##忘備録
・URLのパラメーターは`params[:id]`、フォームで入力されたパラメーター（strong_parameterを通す）は`task_params`とかで出力

・migrationファイルを作る時、コマンドラインで`string`を省略できる。
（`rail g migratinon name age:integer`）

・`resources`でルーティングを作って、showアクションを指定しなかった場合、updateアクションの後に戻るボタンを押すとshowアクションにルーティングされてエラーが発生する。
→404ページに遷移させることができないので、showアクションのルーティングは消すべき。そもそもshowアクションを作った方が良いかも。

・`link_to`に`data: {confirm: "削除しますか？"}`をつけると、リンク先に遷移する前にコンファームを表示することができる。
例：`<%= link_to '削除', admin_email_path(i), method: :delete, data: {confirm: "削除しますか？"}, class: "-delete" %>`
[Railsでdata-confirmを使ってデータ削除時にアラートを表示する](https://kurose.me/data-confirm-destroy/)

・carrierwaveの`process resize_to_fill`を使って、指定した比率の画像を取得する時、小さい値を入れると画像がぼやけがちなので、少し大きめのサイズを指定すると良い。
（元画像より小さい値が指定されてる場合にぼやける）
関連：[CarrierWave + RMagick 画像のリサイズをまとめてみました](http://noodles-mtb.hatenablog.com/entry/2013/07/08/151316)

・`form_with`で非同期通信をした時に、画面の位置を動かしたくなかった。`redirect_to hoge_path(anchor: "hogeid")`とアンカーリンクをつけて試みたところ、アンカーリンクの内容に関わらず位置が動かなくなった。
→`anchor`オプションをつけるだけで動かなくなるのか？

・`each.with_index(1)`で、eachの中に1から始まるインデックスを表示することができる。
[rubyでeach_with_indexでindexの値を0以外から始める方法](http://mikamisan.hatenablog.com/entry/2017/06/10/103402)

・新たにテーブルを作成する時、migrationファイル内の`:string`を省略できるが、後からフィールドを追加する時には省略できない。

・`redirect_to hoge_url, notice: '成功しました'`という書き方で、リダイレクトとflashメッセージを同時に記述することができる。

・`success: '成功しました'` = `flash: {notice: '成功しました'}`
値が1つしか無いハッシュは、親と括弧を省略できる。

・`redirect_to`の引数に設定できるflashは`notice`と`alert`だけ。`success`とかオリジナルのものは無理

・◯`render(:new) && return`
　◯`render :new and return`
　×`render :new && return`

・パラメーターを元にユーザーを取得するとか、そのユーザーを検証するとか、複数のアクションの冒頭で共通して行う処理は、privateにメソッドを作って、`before_action`に設定すると良い
→特に`get_user`とかは頻出

・絶対に成功するsaveは`save!`と書き(失敗時に例外が発生し処理が中断)、それ以外は`save`の戻り値で処理を分岐させる。

・エラーログに日本語を書くと文字化けしがちだから、英語で書くのが無難

・`.to_s(:delimited)`で、3桁以上の数値にカンマをつけることができる

・とりあえず各ファイルの冒頭に`# frozen_string_literal: true`を書き込んだ方が良いっぽい。文字列の破壊的変更をしたいときは`hogehoge.dup`のように`.dup`をつけると良い。
[Ruby2.3で導入されたfrozen_string_literalマジックコメントでImmutable Stringを実現する](https://blog.toshimaru.net/ruby-immutable-string/)

・Bundlerなしでgemを使う場合、必要な場面で毎回`require`しないといけない。Bundlerを使ってgemを使うと`require`を明示する必要がなくなる

・Gemfile内のgemに`require: false`を追加すると、自動的にrequireされなくなる。
→テスト環境でしか使わないgemにrequire: falseが指定されることが多い。
→`group test`内に書けば事足りるかも

・`redirect_to`の引数は`◯◯_path`だけじゃなく`redirect_to '/user/show'`みたいな書き方もできる

・日時のフォーマットは`.strftime`を使う

```rb
Time.zone.now.strftime('%Y年%m月%d日')   #=>2020年02月04日
Time.zone.now.strftime('%Y年%-m月%-d日') #=>2020年2月4日
```

・`redirect_to`と`return`を1行で書きたい時には、`redirect_to(hoge_path) && return`のように、引数をカッコで囲むと不具合が起きづらい

・railsでは（読むのが難しくなるとしても）できるだけ短い記述が好まれる

・`before_save`系には以下の種類がある。
after_initialize    (1)
before_validation   (2)
after_validation    (3)
before_save         (4) 
before_create       (5)
after_create        (6)
after_save          (7)
after_commit        (8)
[Does before_save run after validations?](https://stackoverflow.com/questions/29262699/does-before-save-run-after-validations)

・`User.new`と`User.build`は同じ。慣習的に、モデルの関連付けを行なっている場合などには`build`が使われる。(`user.tasks.build`など)

・`skip_before_action`ってのがあるのか！

・`.blank?`は`.nil?`と`.empty?`の合体版。nilに`empty?`を使うとエラーが発生するので、`.blank?`の方が使いやすい印象。

・`hoge: true`は`:hoge`って書ける？

・boolean型のcompletedという属性があった時、自動的に`completed?`というメソッドが使えるようになる？

・外部サーバーが落ちた時のリスクを減らすため、font-awesomeなどはできるだけcdnを使わないようにした方が良い。

・scopeはすごく便利そうだからどんどん使っていこう
[Railsのモデルのscopeを理解しよう](https://qiita.com/ozin/items/24d1b220a002004a6351)

・viewで使う関数で、特定のモデルとの関連が深いものは`helper`ではなく`decorator`を使う。
[Decoratorの役割とDraperについて](https://qiita.com/jonson29/items/00077b54bb91ed74fdb8)

・`each`を`map`や`inject`で置き換える

・`truncate(文字数)`で文字列を省略して表示できる

```terminal
pry(main)> 'あいうえおかきくけこ'.truncate(6)
=> "あいう..."
```

・外部サイトからのPOSTを許可したい時、コントローラーに`protect_from_forgery :except => [:action_name] `と書くと、そのコントローラーではCSRF対策をオフにすることができる。


・`N+1`問題は`includes`を使って回避

```rb
#@comments = Comment.where(contact_id: params[:id])
@comments = Comment.where(contact_id: params[:id]).includes(:user)
```
```erb
<% @comments.each do |comment| %>
  <li><%= comment.content %>：<%= comment.user.name %></li>
<% end %>
```

・文字列から特定の値を省きたい時は`hogestring.gsub(/不要な文字列/, '')`

・先頭２文字以外を「※」に変える

```rb
'1234567890'.gsub(/([0-9]{2})([0-9]+)/, '※※\2') 
```

・`**hash`でハッシュを展開できる

```erb:view
<%= link_to "csv出力", hoge_path(format: :csv, **take_params) %>
```
```rb:helper
def take_params
    {
      from: params[:from],
      to: params[:to]
    }
end
```

・バリデーションエラーになったinput要素は`div.field_with_errors`で囲まれる。radioボタン等のスタイルをカスタマイズしている場合、スタイルが崩れるので注意。
https://yukimasablog.com/rails-field-with-errors

・Railsでformから受け取ったparamsの型は「ActionController::Parameters」だから、このままだとループで回して必要なデータだけ取り出す、という処理はできない。params.to_unsafe_h でハッシュに変換すると良い。
https://qiita.com/kanna/items/171118e479c2dc76bb7c

##ハマったところ
・gem`paranoia`を利用中、`application_record.rb`に`acts_as_paranoid`と記述してしまい、論理削除を必要としないテーブルで
`UnknownAttributeError: unknown attribute 'deleted_at' for Bill.`と怒られまくってしまった。

・gemを追加したあとは、サーバーを再起動しないと謎のエラーが起こりがち。

・管理者用の名前空間に作ったヘルパーメソッド`helper/admin/sessions_helper.rb`の冒頭に`module Admin::SessionsHelper`ではなく`module SessionsHelper`と書いてしまい、ヘルパーメソッドが無いと怒られた。

・2つのヘルパーファイル（`helper/`以下]と`helper/admin/`以下）に同名のヘルパー関数を作ってしまい、後者を参照したいのに前者が参照されてしまった。
→viewから呼び出す時は、名前空間とか関係なく全部の関数が読み込まれる！

・データベースのカラム名に`type`を使った
→[予約語](https://shinkufencer.hateblo.jp/entry/2018/08/14/235900)だから普通に使うとエラーになる

・ハッシュの取り出しは@params.nameではなく`@params['name']`

・`form_with`で名前空間`admin`にあるコントローラーのアクションにデータを送りたい場合`<%= form_with(model: [ :admin, @image ]) do |f| %>`のように書く

・ルーティングのPrefixが重複していて、formのactionがおかしくなっていた

・`application.scss`で` *= require_tree .`と記述すると、パーシャルまで個別に読み込んでしまい、undefined variableのエラーが発生する。

・DBを作ったブランチをマージせずに削除したら、`There is 1 other session using the database.`というエラーが表示され`、rails db:migrate:reset`が出来なくなった。
→データベースクライアント(Table Plus)を再起動したら解決。

・application_controllerに`protect_from_forgery`が書き込まれておらず、フォーム送信時に`ActionController::InvalidAuthenticityToken`のエラーが発生した
→Rails6からはデフォルトで書き込まれなくなった？

・違う名前空間の同名ファイルをいじっていた

・`<%= form_with model: @hoge url: hoges_path, local: true do |f| %>`でフォームを作ったのに、`@hoge`と関係無い情報（ファイル）送信して、`params[:file]`で受け取ろうとしていた

・CSVを扱う時に`CSV.foreach(file)`だとうまく行くときと行かない時がある。`CSV.foreach(file.path)`にしたら解決した。

・`minimagick`画像のリサイズをしていた時、`resize_to_fit: [400, 400]`を指定しても画像が正方形にならず、minimagick自体が動作していないと思って時間を使った。
→指定したサイズに強制的に変更するオプションは`resize_to_fill: [400, 400]`だった

・turbolinksをOFFにしたら`local: true`をつけ忘れたフォームが動かなくなった。

・HerokuのPostgresで、ひらがなの並び替えがうまく行かなかった
[PostgreSQLで正しく文字列ソート出来ない時の解決方法](https://qiita.com/anoworl/items/af9f12f915b8969ea40d)←で解決

・`["hoge", nil, nil, "fuga"].split`を実行したら`[["hoge"], [], ["fuga"]]`とカラムが3つに減ってしまった。
→`split(',')`のように引数をとって解決

・メソッド名に`send`を使ったら、予約語だったらしく`ArgumentError： wrong number of arguments (given 0, expected 1)`が消えなかった

・繰り返し処理で配列にインスタンスを追加して、最終的にその配列を返す関数を作った時、処理の最後に配列名を書かなかったら、配列の中身がインスタンスじゃなくてハッシュっぽくなってて、取り出したデータに対して`save`メソッドが使えなかった。

・modelのメソッド内で`redirect_to`は使えない

・アカウント有効化メール内のリンクのparamsにメールアドレスを仕込んだ時,`+`が半角スペースになってしまった。
→解決策1：メールアドレスをパラメーターに持たせず、トークンだけで認証する
→解決策2：リンクのメールアドレス部分を`CGI.escape()`でエスケープする。エスケープ解除はrailsが勝手にやってくれる
→解決策3：`params[:email].gsub(' ', '+')`で半角スペースを+に置き換える
→リンクを`hogehoge_url`の形で書けば、CGI.escapeを書かなくても自動でエスケープしてくれる

・ルーティングが見つからなかった時のリダイレクト用のルーティングの下に他のルーティングを書いても、使えなかった。
→ルーティングは上から評価される

・wicked pdf利用時、本番環境でのみ`The asset "pdf.css" is not present in the asset pipeline.):`というエラーが表示され上手く動かなかった。
→CSSは全部HTML内に直書きしていたので、CSS読み込みの記述自体を削除して対応。

・DBの構造(ユニークキーなど)を変えた後、本番環境でユーザー情報の更新ができなくなった。
→本番環境のデータで、ユニークじゃないものがあったから。

・joinを使った複雑な検索を行った後の要素の集合を`count`メソッドでカウントしようとしたらエラーが出た
→代わりに`size`や`length`を使ったら解決した。理由はよくわからないままだけど、単純に数を数えたいだけの時は`count`を使う必要は無いみたい。countメソッドは、引数にブロックを渡したい時に使う。
[Rubyにおけるsize, length, countの違いについて](https://qiita.com/tacumai/items/28100326874dae9e5f5c)

・すでにnullのデータが入ってるカラムに`default :null`を追加しようとして失敗した

・sorcery使用時、initializerの中の`Rails.application.config.sorcery.submodules = []`
を誤って消してしまったら、モデル内のメソッドを一切呼び出せなくなった

・フォームの送信ボタンのdisable属性をゴニョゴニョする処理をjsで書いていたら、capybaraで`click_on '送信'`でクリックできなくなった。
→`find('#submit').click`で解決。いろんな書き方がある。

・Rubyの[mergeメソッド](https://docs.ruby-lang.org/ja/latest/method/Hash/i/merge=21.html)を使って、オブジェクトを結合することができる
`user_params.merge(group_id: params[:user][:groups])`みたいに使える