Rails学び まとめ

新しい環境で仕事を始めてみて、学生時代ぶりにRails, Ruby, MySQLを触ったので、学習メモとして雑多に残す。
元々はJavaを使っていたのと、業務内容は運用だったので、そもそも開発をあまりしていない状態だったので、新しい発見が多い。

## Ruby and Rails
- [Struct](https://qiita.com/k-penguin-sato/items/54189d5ed4e5f7463266)
    - RubyにおけるStruct(構造体クラス)は簡易的なクラスのようなもの
    - 利用ケース
        - まとまったデータを扱いたいが、クラスを作るまでもない場合。
        - クラス内で特定のデータのまとまりを表現する場合。
    - 通常のクラスとの違い
        - 継承、メンバの追加・削除ができない。
        - 明示的にアクセスメソッドを定義しなくても、構造体クラス外でメンバの参照・更新が可能。
- scope
    - データアクセスの際の条件式に名前をつけて、メソッドチェーンみたいな感じで繋げてかける。
```ruby:scopeの定義例
class Blog < ApplicationRecord
    scope :published, -> { where(published: true) }
end
```
```ruby:scopeを使わない例
Blog.where(published: true)
```
```ruby:scopeを使って書く例
Blog.published
```
   
- JSON_CONTAINS(target, candidate[, path])
    - JSON形式で格納されているカラムを検索できる。
    - WHERE句としても使えて、[この記事](https://qiita.com/ukyooo/items/19da224866affa97a211)がとてもわかりやすかった。
    - targetで指定したjson_field_nameのvalueにcandidateが含まれていれば1を返し、含まれていなければ2を返す。
```sql:JSON_CONTAINSの利用例
mysql> SET @j = '{"a": 1, "b": 2, "c": {"d": 4}}';
mysql> SET @j2 = '1';
mysql> SELECT JSON_CONTAINS(@j, @j2, '$.a');
+-------------------------------+
| JSON_CONTAINS(@j, @j2, '$.a') |
+-------------------------------+
|                             1 |
+-------------------------------+
mysql> SELECT JSON_CONTAINS(@j, @j2, '$.b');
+-------------------------------+
| JSON_CONTAINS(@j, @j2, '$.b') |
+-------------------------------+
|                             0 |
+-------------------------------+
```

- p
    - 「puts」と「p」はいずれも改行するが、「p」メソッドはオブジェクトの情報も出力する。
- rubyの%記法
    - プログラムの可読性を上げるための書き方
    - 例
        - %w記法
            - 文字列の配列定義をしたいときにダブルクォーテーションとかがいらなくなるので、エスケープ処理とかもいらない。
        - %W記法
            - %wと比べて式展開が可能
        - %r記法
            - 正規表現のエスケープをなくせる
        - %i記法
            - %wと同様に空白区切りの文字列から配列を作成できるが、配列の要素がシンボルになる。
```ruby:%wを使わない場合
puts ["ラーメン", "ブログ", "Ruby", "\"わっしょい\""]
```
```ruby:%wで書く場合
puts %w(ラーメン ブログ Ruby "わっしょい")
```

- freeze
    - そのオブジェクトのレシーバーオブジェクトを凍結して破壊的メソッドを使えなくさせる。
    - 定数との違いは、定数は警告が出るが変更可能な一方、freezeしたオブジェクトは変更が不可能になる。
- [Rubyにおける定数](https://qiita.com/yukiyan/items/39f945c55b6dc5e11ed7)
    - 不変の値というより、グローバル変数に近い。
    - 意図せずに書き換えられてしまうことがある。
    - Rubyでは先頭が大文字になっている識別子のことを定数としていることから、クラス名であるStringやArrayなども定数として扱っている。
```ruby:freezeの利用例
PERMIT_ID = ['0001', '0002', '1234']
PERMIT_ID.freeze
PERMIT_ID.reject! { |id| id == '1234' }
# => RuntimeError: can't modify frozen Array

p PERMIT_ID
# => ["0001", "0002", "1234"]
```
- アソシエーション
    - テーブル同士を関連づけるもの。これによってテーブル操作を効率化する。
    - [参考にした記事](https://railsguides.jp/association_basics.html)
```ruby:著者が新しい書籍を一つ追加する場合（アソシエーションなし）
@book = Book.create(published_at: Time.now, author_id: @author.id)
```

```ruby:著者を削除する場合（アソシエーションなし）
@books = Book.where(author_id: @author.id)
@books.each do |book|
  book.destroy
end
@author.destroy
```

```ruby:アソシエーション定義
class Author < ApplicationRecord
  has_many :books, dependent: :destroy
end

class Book < ApplicationRecord
  belongs_to :author
end
```

```ruby:著者が新しい書籍を一つ追加する場合（アソシエーションあり）
@book = @author.books.create(published_at: Time.now)
```

```ruby:著者を削除する場合（アソシエーションあり）
@author.destroy
```
- メソッドや関数名の末尾の「!」と「?」
    - !はレシーバに対して破壊的変更をすることを明示的にしている。!も含めてメソッド名
    - ?は真偽値を返すメソッドに使われている。
        - include?
        - nil?
        - empty?
    - [参考にした記事](https://prograshi.com/framework/rails/what-are-exclamation-and-question-marks-at-the-end/)
- validates
    - 標準のバリデーターを指定する
- validate
    - カスタムのバリデーターを指定する
- class << self
    - 特異クラスという。クラスメソッドを定義する際に、.selfをつけなくてよくなる。
- attr_accessor
    - 外部からクラス内の変数を参照するためのgetterとsetterを定義するもの
- [コールバック Railsガイド](https://railsguides.jp/active_record_callbacks.html)
    - これによってモデルの一貫性を保てるかつ、コード量を減らせる。
    - e.g.
        - before_validation
        - after_save
        - before_create
            - オブジェクトがDBに新規保存(create)される直前に実行されるもの。
- ActiveRecordとApplicationRecordの違い
    - Rails5からモデルの継承元がActiveRecord::BaseからApplicationRecordに変更された。
- path_toとかlink_toで指定されているPrefixのパスが何を指しているのか
    - rails routesでどのメソッドに紐づいてるかが参照できる。
- Rubyの破壊的メソッドについて
- rubyの方言集
    - [知って得する21のRubyのトリビアな記法](https://melborne.github.io/2011/06/22/21-Ruby-21-Trivia-Notations-you-should-know-in-Ruby/)
- double splat演算子（**）
    - ハッシュオブジェクトをキーワード引数に変換するために必要
- 特異メソッド（javaでいうstaticメソッド？）
    - `def self.メソッド名`で定義する
- クラスメソッド
- mergeメソッド
    - ハッシュを結合する。パラメータを結合するのとかでよく使う。
- params
    - フォームやパスパラメータとして送られてきた情報を取得するメソッド
- permitメソッド
    - 保存するパラメータの許可処理を行ってストロングパラメータにする
```ruby
params.permit(:name, :content)
```
- ストロングパラメータとは
    - updateやcreateメソッドでレコードを保存する際に、ストロングパラメータにしないと保存できなくなった。
- requireメソッド
    - パラメータの中にモデルに対応するキーが存在するかを確認し、存在する場合にそのバリューを返す。
- [一括アップデートの方法](https://shinkufencer.hateblo.jp/entry/2018/05/23/233906)
    - 1レコードずつinsertとかupdateとかするとパフォーマンスに影響があるので、gemのactiverecord-importを使う。
    - INSERT
        - `{{テーブルのモデル}}.import {{insertしたいモデルを持つ配列}}`
    - UPDATE
    - `{{テーブルのモデル}}.import {{updateしたい値に差し替えたモデル}} on_duplicate_key_update: {{アップデートしたいカラムのシンボルの配列}}`
```ruby:bulk update
no_name_users = User.where(name: nil)
update_users = []
no_name_users.each do |users|
  users.name = "NoName"
  update_users << users
end

# nameだけUpdate
User.import update_users,  on_duplicate_key_update:[:name]
```
- safe navigation operator
    - ぼっち演算子とも呼ばれ、`object&.foo`のような形式で書く。objectがnilでない時にメソッドfooを呼ぶ。
- [createとcreate!の違い](https://qiita.com/saitok7/items/20adc17112d6d0bcf9d5)
    - `create!`はバリデーションに引っかかった場合に例外を発生させるが、`create`はバリデーションで引っかかった場合は、保存はせずにそのまま次のリダイレクトが実行される。

## RSpec
- describe, RSpec.describeはテストのグループ化を宣言します。メソッド単位とかでテストをグループ化するケースとかで使う。
- context
    - 条件別にグループ化するために使う。このコンテキストグループはこういう条件のもとテストしているんだなということが分かりやすくなる。可読性が増すので
- it
    - テストを example という単位にまとめる役割をします。
`it do ... end` の中のエクスペクテーション（期待値と実際の値の比較）がすべてパスすれば、その example はパスしたことになります。
- let
        - インスタンス変数の代わりにletを使う。`let(:foo) { ... }`のように書くと、`{ ... }`の中の値が`foo`として参照できる。letを使うメリットは、遅延評価されるため、効率が良いテストコードを書くことができる。詳しくは[こちら](https://qiita.com/jnchito/items/cdd9eef2ed193267c651)
- before
    - `before do ... end`で囲まれた部分はexampleの実行前に必ず呼ばれる。このブロックの中では、テストを実行する前の共通処理やデータのセットアップ等を行うことが多い。ネストしたdescribeやcontextの中でbeforeを使うと、親->子の順番で呼ばれる。あとは階層に応じて実行されるスコープが決まる。
- subject
    - テストの主語を設定できる。例えば`subject { user.greet }`のように定義すると、itの中で、今まで expect(user.greet).to eq 'ぼくはたろうだよ。' と書いていた部分が is_expected.to eq 'ぼくはたろうだよ。' に変えることができる。
- rspec の type: :model
    - モデルタイプのテストにはこのタグをつける。こうすることでロードされるものや使えるメソッドが変わる。
    - [詳しくはこちら](https://relishapp.com/rspec/rspec-rails/v/3-0/docs/model-specs)
- 特定のテストだけ実行したい時
    - `rspec spec/models/user_spec.rb:12`こんな感じで行数だけ指定する。


## Rakeタスク
Rubyで書かれたコードをタスクとして作成しておき、必要に応じて呼び出し実行することができる機能
- 用途
    - 何かしらのデータの連携
    - データベースのバックアップ
    - 定期的にデータを更新、削除する
- namespace
    - タスクを管理しやすくするために用途などに応じて、名前をつけてタスクをまとめる。
- desc -> descriptionの略
    - `desc "タスクの説明"`みたいな感じで書く
- task
    - `task task_name: :environment do`みたいな感じで書く。task_nameにタスクの名前を定義する。
- 実行方法
    - `rails 名前空間:タスク名`で実行する。名前空間がネストしている場合は、`名前空間1:名前空間2:タスク`になる。





## 深掘りメモ
### let
遅延評価

### factory bot
### method_missing