[rails 初心者]メソッド化についてまとめてみた

<h2>初めに

縁があり、通っているプログラミングスクールのメンター（質問対応者）さんから次のことを勧められました<br>`メソッド化に力を入れた方がいい`と。。。<br>ということで今回はそのメソッド化についての基本知識？考え方？を自分なりにまとめてみました！！

<details>
<summary>注意事項</summary>
※自分用でまとめてますので、分かり辛かったらすいません。<br>また理解不足・誤りがあったらコメントにてご指摘頂けますと幸いです。m(__)m<ﾖﾛｼｸｵﾈｶﾞｲｼﾏｽ</details>

<h2>前提条件</h2>

①今回は`rails`を題材にして話を進めていきます。

②次の二つの知識についてある程度理解済みであること　　 ※私の場合は60%ぐらいでした・・・多分(;^ω^)
・「メソッド」について理解していること
・「オブジェクト指向」について理解していること
※もしここで不安な方がいましたら、以下のプルダウンのところを開いてリンク先を一読してみてください。

③`posts_conntroller`にて`@post = Post.find(params[:id])`が定義されていること

<details>
<summary>参考資料</summary>

[オブジェクト指向○○とは |「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word1906.html)
[メソッド |「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word1906.html)

</details>

以上の３点だけです。

<h2>開発環境</h2>
・rails 6.1.7<br>
・ruby 3.1.2 <br>
・raty 3.1.1<br>
・jquery 3.6.1<br>
・bootstrap 4.5

<h2>メソッド化とは</h2>

<h4>簡単にまとめると</h4>

**オブジェクト指向**（「モノ（どんな奴でどう動く）」に注目した考え方のこと）**を踏まえた処理をメソッド**（オブジェクト指向における「操作」を定義したもののこと）**化すること**です

これだけだと「(。´・ω・)ん?」てなるので　※これ自分ですｗ
例を用いて説明していきます。

<h4>ちょっと詳しく解説</h4>

例えば次のプログラムがあるとします。
```:app/views/posts/show.html.erb
    ##とある投稿サイトの詳細ページ
    <% if @post.user == current_user %>
        <%= link_to "編集する", edit_post_path(@post.id), class: "btn btn-sm btn-success" %>
        <%= link_to "削除する", post_path(@post.id), data: { confirm: '本当に消しますか？' }, class: "btn btn-sm btn-danger",method: :delete  %>
    <% end %>
```
意味合いとしては・・・「`投稿主`と`ログイン中のuser`が同一人物かどうか」を確認する処理になります。
これを次のように書き換えることをメソッド化といいます。　※めちゃくちゃ簡単にまとめるとこんな感じです |дﾟ)
```:app/models/post.rb
    ##モデルファイルの方
    def written_by?(current_user)
        user == current_user
    end
```
```:app/views/posts/show.html.erb
    ##Viewページの方
    <% if @post.written_by?(current_trader) %>
        <%= link_to "編集する", edit_trader_post_path(@post.id), class: "btn btn-sm btn-success" %>
        <%= link_to "削除する", trader_post_path(@post.id), data: { confirm: '本当に消しますか？' }, class: "btn btn-sm btn-danger",method: :delete  %>
    <% end %>
```

<h4>まとめ</h4>
ちょっとまとめてみると次のようになります。

:::note info 
①まとめたい処理達(オブジェクト)を見つける　※`View`以外にも`controller`でも可能
②`モデルファイル`と`元のViewページ or Conntroller`を編集(メソッド化)する
:::
これについても初めて聞いたときは「(。´・ω・)ん?」となったので　※あ、これも自分ですｗ
次の節にて詳しく説明します！！ ｶﾞﾝﾊﾞﾘﾏｽ(/・ω・)/

<h2>①まとめたい処理達(オブジェクト)を見つける</h2>
<h4>メソッド化する目的</h4>

これは大きく分けて２つの理由があります。

:::note info 
①**DRY原則**を守るため
②**見えないエラー**を防ぐため
:::
ではこれも深掘り&解説していきます！！

<h5>DRY原則を守るため・・・について深掘りしよう</h5>

**1** . まずそもそも`DRY原則`って何？となった方　※毎度お馴染み、これ自分です(/ω＼)ﾊｽﾞｶｼｨ
参考資料を添付しますので、よかったら一読してみてください

<details>
<summary>参考資料</summary>

[DRY原則とは |「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word12000.html)

</details>

**2** . なぜこの考え方が必要なのか・・・
「同じ処理文を何回も書くことで`書き間違い`(ヒューマンエラー)が生じてしまうため」
「チーム開発の際など、違う書き方をして`混乱`してしまう可能性があるため」

<details>
<summary>これ以外にもあるという方！！</summary>

※ご縁があり今回話を聞かせていただいた方（現場で実際に働いている方です）の話をもとにまとめてました。
もしこれ以外にも理由があると思う方は、ぜひコメントにて教えて下さいm(__)mｵﾈｶﾞｲｼﾔｽ

</details>

<details>
<summary>これについて、詳しく知りたい方へ</summary>

まずは**ヒューマンエラー**について解説します。
※ヒューマンエラーについては次のリンクを参考にしてください　→　[ヒューマンエラーとは？ 【簡単に】意味、対策、具体例、種類](https://www.kaonavi.jp/dictionary/human_error/)

例えば`スペルミス`が挙げられます。

先程紹介したものと同じViewファイルのプログラミングです。
```:app/views/posts/show.html.erb
    ##とある投稿サイトの詳細ページ
    <% if @post.user == current_user %>
        <%= link_to "編集する", edit_post_path(@post.id), class: "btn btn-sm btn-success" %>
        <%= link_to "削除する", post_path(@post.id), data: { confirm: '本当に消しますか？' }, class: "btn btn-sm btn-danger",method: :delete  %>
    <% end %>
```
これの`<% if @post.user == current_user %>`の部分が`<% if @post.user == crrent_user %>`だった場合、エラーが発生します。
→ もうお分かりだと思いますがスペルミスが原因です(`current_user`の`current`の部分で`u`が抜けてることが分かると思います)(´ﾟдﾟ｀)ｲﾔ,ﾜｶﾘﾂﾞﾗ!!

続いて**違う書き方をして混乱してしまう可能性**について解説します。

例えばさっきの`<% if @post.user == current_user %>`の部分が`<% if @post.user.id == current_user.id %>`だった場合、
→ 書き方的にはどちらも間違っていませんし、今回の例は１行だけというのもあってパッと見た感じで理解できると思います。
　がしかし、現場では`数行あるオブジェを違う書き方にしたせいで`混乱してしまうことがあるらしいです。。。((( ﾟДﾟ;))))ｺﾜ!!

つまり書き方やオブジェクト指向的には間違っていないけど、`書き方が違うだけ`で混乱してしまうということでした(-_-;)ﾑｽﾞｶｼｲ

</details>

<h5>見えないエラーを防ぐため・・・について深掘りしよう</h5>

**1** . まずそもそも`見えないエラー`って何？イメージが付かないとなった方、次のプルダウンのところを一読してみてください。

<details>
<summary>見えないエラーとは</summary>

簡単にまとめると・・・
書き方は間違っていないけど、`オブジェクト指向（要はやってほしい挙動）に合っていない`ことを言います。

例）
先程紹介した、お馴染みのViewファイルです。
```:app/views/posts/show.html.erb
    ##とある投稿サイトの詳細ページ
    <% if @post.user == current_user %>
        <%= link_to "編集する", edit_post_path(@post.id), class: "btn btn-sm btn-success" %>
        <%= link_to "削除する", post_path(@post.id), data: { confirm: '本当に消しますか？' }, class: "btn btn-sm btn-danger",method: :delete  %>
    <% end %>
```
今回も`<% if @post.user == current_user %>`の部分を題材に解説します(/・ω・)/ｶﾞﾝﾊﾞﾘﾏｽ

早速ですが、`<% if @post.user == current_user %>`の部分が`<% if @post.user = current_user %>`の場合、どういう挙動になるか想像してみてください。因みに両者の違いは「＝」の数です。

お分かりかもしれませんが、答えは前者が`同じかどうか`後者は`代入`の意味合いになります。

このように`プログラムの書き方が合っている` + `オブジェクト指向(挙動)が違う`ことを見えないエラーとよく言うそうです( ..)φﾊｼﾞﾒﾃｼｯﾀｶﾓ

</details>

**2** . さっきの「違う書き方をして混乱してしまう可能性がある」との違いは
→　オブジェクト指向(要するにやってほしい挙動)が`合っているかいないか`です。

<details>
<summary>詳しい解説はこちら</summary>

まずオブジェクト指向とは`「モノ（どんな奴でどう動く）」に注目した考え方のこと`でした
参考資料はこちら　→　[オブジェクト指向○○とは |「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word1906.html)

この考え方を今回に置き換えると次のようになります。

・どんなモノがあるのか　　　　　　　　＝取得済みのUser情報（@post.user）とログイン中のUser情報（current_user）の２つがある
・どんな性質の奴らなのか（属性）　　　＝取得済みのUser情報 → 表示中の情報、ログイン中のUser情報 → 今回の処理だけで使いたい情報
・どんな動きができるか　　　　　　　　＝取得済みのUser情報 & ログイン中のUser情報 → 主キー以外の情報も取得可能(紐づけすればできるため)
・実際に何をしているのか（したいのか）＝ログイン中のUserと取得済みのUser情報が合っているかどうかを判断（主キーを使って）


次に、お馴染みのViewファイルを使っていきます。
```:app/views/posts/show.html.erb
    ##とある投稿サイトの詳細ページ
    <% if @post.user == current_user %>
        省略
    <% end %>
```

先程の解説と同じように、`<% if @post.user == current_user %>`の部分が`<% if @post.user = current_user %>`の場合、どういう挙動になるか想像してみてください。因みに両者の違いは「＝」の数です。

答えは前者が`同じかどうか`後者は`代入`の意味合いでした。

ここで少し前のオブジェクト指向のところ(厳密には４つのポイントのところ)に戻って考えてみてほしいんですが、`実際に何をしているのか`の部分が違ってくることがお分かりいただけると思います。

それを考慮してまとめる以下の様な考え方になります。

:::note info 
違う書き方をして混乱してしまう → 本来してほしい**挙動をする** → オブジェクト指向が合っている
見えないエラー  → 本来してほしい**挙動をしない** → オブジェクト指向が合っていない
:::

解説は以上となります( ..)φﾒﾓﾒﾓ

</details>

少々話が脱線気味になってきたので、ここで**メソッド化する目的**について話を戻したいと思います。
まとめると次のような考え方になります。

:::note info 
①**DRY原則**を守るため → スペルミスなどのヒューマンエラーを防ぐ
②**見えないエラー**を防ぐため → 挙動がおかしいエラーを防ぐ
:::
長くて分かり辛い解説を読んでくださった方には申し訳ございませんが、要するにこんな感じです |дﾟ)ｽｲﾏｾﾝ

<h2>②モデルファイルと元のViewページ or Conntrollerを編集(メソッド化)する</h2>

ではここからは手作業を入れていきます(ノД`)・゜・。ﾔｯﾄﾀﾞﾖ

<h4>メソッド化の手順 ＋ それに対してのポイントをマスターしよう</h4>
メソッドの手順は次の通りなります※今回の例をメソッド化する時の手順ですので、参考程度にお願いいたします。m(__)m

:::note info 
手順①メソッド化するものを探す または 決める（今回は省略）
手順②必要な部分を**コピー**(元データがない場合は、作成)
手順③メソッド化するモデルファイルを決める＆**メソッドを作成**
手順④　③で作成したメソッドでコピーしたものを**貼り付ける**(元データがない場合は、作成)
手順⑤実際に処理したいところ(今回はViewファイル)で**定義する(呼び出す)**
:::

手順に対してのポイントは作業と一緒に紹介します(^^ゞｶﾞﾝﾊﾞﾘﾏｽ


<h5>手順②必要な部分をコピー(元データがない場合は、作成)</h5>


もう何回も見てきた例のプログラムを使っていきます。

```:app/views/posts/show.html.erb
    ##とある投稿サイトの詳細ページ
    <% if @post.user == current_user %>
        省略
    <% end %>
```
これの`@post.user == current_user`の部分をコピー・・・手順①終わりです(;^ω^)ﾅﾇ?!

<details>
<summary>ここでのポイントは？</summary>

それはズバリ、`必要な箇所`をコピーすることです。

まずは完成版のモデルファイルを見てみましょう

```:app/models/post.rb
    ##作成したメソッド
    def written_by?(current_user) #ログイン中のUser情報(current_user)をメソッド処理( written_by?() )にかける
        user == current_user    　#本当は self.user == current_user である（selfは紐づけしている時は省略可能）
                                  #そして今回はpostモデルなので、self == post と考えてOK
                                  #つまり post.user == current_user となる※内部的な意味合いで
                                  #今までの情報を考慮して、まとめると @post.user == current_user と同じ挙動ができることになる
    end
```

因みに公式？書き方？については以下の通りとのことでした。※メンター情報です

```:app/models/post.rb
    ##メソッドの公式
    def メソッド名?(View や conntroller から持ってきた 引数 )
        処理内容
    end
```
結果論の話になりますが、コピー元の**オブジェクト**と完成版の**メソッド**を見比べた時、同じ書き方をしている箇所があります。
`trader == current_user`の部分です。

つまり今回のポイントである「`必要な箇所`をコピーする」＝「`trader == current_user`の部分」となります。<br>


因みに、ここでの参考資料は 
[1.1 AttributeMethodsモジュール|Rails ガイド](https://railsguides.jp/active_model_basics.html#attributemethods%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB)
[[Rails]selfについての理解を深めたい[初心者]](https://qiita.com/jackie0922youhei/items/0865114ff5f701611fcc)
＋メンターさん情報です

</details>

<h5>手順③メソッド化するモデルファイルを決める＆メソッドを作成</h5>

メソッド作成先のモデルファイル：`Post`モデル
メソッド名：`written_by?()`で作成

・・・以上で手順③終わりです( ﾟДﾟ)ﾊﾔ

<details>
<summary>ここでのポイントは？</summary>

:::note info 
・作成先の`モデルの決め方`
・`主役(レシーバ)`を決めること
・メソッド名はSVO「S(主語)はO(目的語)をV(述語動詞)する」
:::

まずは作成先の**モデルの決め方**について・・・
答えから説明すると候補として「**Post** or **User**」の２択になります

この２択になった理由としては最初のプログラム（以下のViewページ）にて`<% if @post.user == current_user %>`の部分がありました。
この行限定で、`この部分に関係するモデル`は「**Post** or **User**」の２つのみになります。
よって候補は「**Post** or **User**」の２択になったわけです

```:app/views/posts/show.html.erb
    ##とある投稿サイトの詳細ページ
    <% if @post.user == current_user %>
        省略
    <% end %>
```
候補が出たら、選択フェーズになりますが、今回は**Post**モデルを選びました。

選んだ理由としては
・呼び出しているViewページは**Post**に属するものだった（そこで呼び出している）ため
・**User**だと大元過ぎると考えたため

ということで、ここで「モデルの決め方」についての説明を終わりにします。<br>
<br>


次に **主役(レシーバ)** を決めることについて・・・

まずそもそも`レシーバ`って何？となった方は、「 . の左側」で覚えておきましょう。私もそう覚えました(*´▽｀*)ｴﾍﾍ

以下リンクが参考資料です
[Ruby レシーバについて|teratail](https://teratail.com/questions/1687)
[rubyのレシーバとは|Qiita](https://qiita.com/you8/items/e5f5c27cfed60a23fa75)

話を戻して・・・
今回の主役(レシーバ)は`@post`になります。
具体的には最初にお見せした完成版のViewページにて`@post.written_by?(current_trader)`の中の`@post`になります

参考として完成版のプログラムを載せときます。
```:app/views/posts/show.html.erb
    ##完成版のViewページ
    <% if @post.written_by?(current_trader)  %>
        省略
    <% end %>
```
ここで疑問に思うのが、「なぜ`@post`なのか？」
理由は
①先程`Post`モデルにメソッドを作ることを決めたため
②もともとあったデータでも`@post`の位置がレシーバの位置だから

まず理由①について・・・
先程の手順までで決まったことを振り返ると、
今回作成するメソッド( `written_by?()` )は`Post`モデル内で作られることになる。

そしてここからが重要になりますが、
メソッドが作成されるモデル(今回は`Post`)とレシーバ(今回は`@post`)は同じ属性(今回は**Post**)でないといけません。
これは「そういうルールがあるんだ(´ﾟдﾟ｀)ﾍｪ~　」で覚えた方がいいかもしれません※個人の意見です

まとめると　「メソッド( `written_by?()` )の属性」　＝　「`@post`の属性」　となることが重要、、、みたいです(´ﾟдﾟ｀)ﾍｪ~

次に理由②について・・・
これはみれば一目瞭然なので、解説はなしにします。※悪しからずm(__)m

```:app/views/posts/show.html.erb
    ##もともとあった方の記述
    <% if @post.user == current_user %>
        省略
    <% end %>
```

</details>

<h5>手順④　③で作成したメソッドでコピーしたものを貼り付ける(元データがない場合は、作成)</h5>

⓪今までのことを考慮したメソッドを用意
```:app/models/post.rb
    ##完成前のモデルファイル０
    def written_by?()
        
    end
```

①コピーを張り付ける
```:app/models/post.rb
    ##完成前のモデルファイル１
    def written_by?()
        @post.user == current_user
    end
```
②エラーが発生しないように少し編集
・`@post`を宣言していない
・今回は`self`が使える条件下である
```:app/models/post.rb
    ##完成前のモデルファイル２
    def written_by?()
        self.user == current_user
    end
```
③`self`はレシーバの場合、省略可能のため・・・
```:app/models/post.rb
    ##完成前のモデルファイル３
    def written_by?()
        user == current_user
    end
```
まだ未完成状態ですが、手順④は以上になります。

<details>
<summary>ここでのポイントは？</summary>

:::note info 
・エラーを無くすこと
・やってほしい挙動かどうか
:::

今回のポイントはチェックするだけなので、解説は割愛します。
</details>

<h5>手順⑤実際に処理したいところ(今回はViewファイル)で定義する(呼び出す)</h5>

⓪今までのことを考慮した記述を用意
```:app/views/posts/show.html.erb
    ##完成前のViewページ
    <% if @post.written_by?() %>
        省略
    <% end %>
```
①今回はログイン中`User`がチェック対象になるので・・・
```:app/views/posts/show.html.erb
    ##完成後のViewページ
    <% if @post.written_by?(current_user) %>
        省略
    <% end %>
```
②引数(`current_user`)が決定したので、モデルファイルも・・・
```:app/models/post.rb
    ##完成版のモデルファイル
    def written_by?(current_user)
        user == current_user
    end
```
<details>
<summary>ここでのポイントは？</summary>

:::note info 
・引数を決める
・引数を決めたらモデルファイルに記述すること（解説は割愛します）
:::

引数とは・・・[引数 |「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word1442.html)

引数の決め方について・・・
①どういった挙動をしてほしいかを考慮する
②欲しい情報は何か？

まず①から考えていきましょう。
今回してほしい動作については、さんざん言ってきましたが「ログイン中の`User`が`投稿主`と同一人物かどうか」をチェックすることです。

今の考え方を踏まえて②を考えます。
まず`投稿主`の情報は既に`@post`に入っている(つまり@post.user)　→　手元に揃っているのでわざわざ再度宣言しなくてもいいことになる
次にログイン中の`User`の情報は何も定義していない　→　手元にないので、どこかから持ってくる必要がある　→　`current_user`を使う

結論・・・
ログイン中の`User`の情報を`current_user`を使うことで取得＆引数として利用。といった感じになります(ノД`)ﾒﾁｬﾑｽﾞ

</details>

<h2>最後に

とてつもなく長文でしたが、ここまでお付き合いいただきありがとうございました ^^) _旦~~ｵﾂｶﾚｻﾏﾃﾞｽ

ありがたいことに、メンター（質問対応者）さんに教えてもらった考え方？概念？を覚えるために今回記事にしてみました。
実はメソッド化やオブジェクト指向・DRY原則など、今回の記事を書いた際に初めて知ったことが多々あります。
何度も申し訳ございませんが、初学者の立場でなので`誤字脱字や誤った情報`についてはコメントで教えていただけると幸いです。勉強になるので！！よろしくお願いいたします。m(__)mｵﾈｶﾞｲｼﾔｽ

<h4>まとめ</h4>

メソッド化の手順は次の通りなります

:::note info 
手順①メソッド化するものを探す または 決める（今回は省略）
手順②必要な部分を**コピー**(元データがない場合は、作成)
手順③メソッド化するモデルファイルを決める＆**メソッドを作成**
手順④③作成したメソッドでコピーしたものを**貼り付ける**(元データがない場合は、作成)
手順⑤実際に処理したいところ(今回はViewファイル)で**定義する(呼び出す)**
:::

手順に対してのポイントは次の通りなります

:::note info 
ポイント①もともとあった処理文(必要な箇所のみ)を**コピー**
ポイント②**主役**(レシーバ)を決める
ポイント③作成先のモデルファイルと**メソッド名**を決める
ポイント④メソッドの中身(さっきコピーした文)を**移植する**
ポイント⑤実際に処理したいところで**定義する(呼び出す)**
:::