ヘルパーを使ったリファクタリング

# 何をしたか
クエリパラメータを付与したリンクを共通化することで
クエリパラメータのキーが変更された場合に、
**49箇所** で変更が必要だったものを **1箇所** で管理できるようにしました。

# どんなメリットがあるか
- リンクのクエリパラメータ変更時に **改修が一箇所** で済みます
- 重複箇所を減らせるため、コードの不必要なところまで読まなくて済み、 **工数削減** につながります

# 具体的な内容

実務で下記のようなコードに出会いました。
データベースにあるデータが、ベタ書きされています。

```slim
= link_to 'A', search_path('search[tags]': ['1']), class: 'style-classes'
= link_to 'B', search_path('search[tags]': ['2']), class: 'style-classes'
= link_to 'C', search_path('search[tags]': ['3']), class: 'style-classes'
= link_to 'D', search_path('search[tags]': ['4']), class: 'style-classes'
= link_to 'E', search_path('search[tags]': ['5']), class: 'style-classes'
/ .
/ .
/ .
/ 同じコードが大量に続く
```

データベースのデータの構造的には下記のように、
Railsのモデルで、 **name** が **link_toのテキスト** であり、
**クエリパラメータ** として渡したい値が **id** となるイメージです。

```slim
= link_to language.name, search_path('search[tags]': [language.id]), class: 'style-classes'
```

```ruby
class Skill
  attr_accessor :id, :name
  def initialize(id:, name:)
    @id = id
    @name = name
  end
end

language = Skill.new(id: 1, name: 'Ruby')
language.id #=> 1
language.name #=> Ruby
```

また、下記のようなブロックメソッド呼び出しで **link_to** が利用され、
クエリパラメータを渡している箇所もありました。

```slim
= link_to(search_path(:'search[tags]' => "#{language.id}"), class: 'style-classes') do
  button = language.name
```

# これらを踏まえてやりたかったこと

- クエリパラメーター付きのリンク生成の箇所で改修箇所を少なくしたい
- ３パターン全てに対応したい
    - nameとidを持ったオブジェクト（モデル）から参照する箇所
    - 文字列と数値で渡している箇所
    - リンクでボタンをラップしている箇所

# どのようにしたか

Rubyにおける「ブロック」という概念自体理解していなかったため、
参考文献で学習していく中で、そういった方法があることを知り、活用してみました。
今回は、Helperで定義しました。

```ruby
def serach_link(name, id, class_str, &block)
  # search_pathはRailsのルーティングで定義したURLジェネレーターのものを利用
  path = search_path('search[tags][]': id)
    
  if block_given?
    link_to(path, class: class_str, &block)
  else
    link_to(name, path, class: class_str)
  end
end
```
第一引数には、リンクに表示したい **文字列**
第二引数には、クエリパラメータで飛ばしたい **id**
第三引数には、 **Bootstrapのクラス**
第四引数には、 **ブロック** を受け取るようにしました。

ローカル変数`path`には、idを値にしたクエリパラメータ付きのPathをRailsのURLジェネレーターを利用して格納しています。
そして、link_toを使ってリンクを生成する際の引数をブロックの有無で条件分岐しています。

こうすることで、意図した挙動となり、クエリパラメータのキーが変わったとしても変更しやすくなりました。
また、実際にはBootstrapの長いクラス名や、クエリパラメータのキーがもっと長いので、可読性が上がりました。

## Railsのlink_toのソースコード
下記のような実装となっているため、
メソッド内の一行目で、ブロックが渡された場合多重代入によって
optionsがhtml_optionsへ、
nameがoptionsへ、
blockがnameへ、
それぞれ代入されるようになっているようです。

```ruby
def link_to(name = nil, options = nil, html_options = nil, &block)
  html_options, options, name = options, name, block if block_given?
  options ||= {}

  html_options = convert_options_to_data_attributes(options, html_options)

  url = url_target(name, options)
  html_options["href"] ||= url

  content_tag("a", name || url, html_options, &block)
end
```
https://github.com/rails/rails/blob/984c3ef2775781d47efa9f541ce570daa2434a80/actionview/lib/action_view/helpers/url_helper.rb#L209

---

ここまでが実際に適用したコードとなるのですが、今見返してみるとさらにより方法があるのではないかと思い、考えてみることにしました。

# さらにリファクタリング

ブロック付きメソッド呼び出しの場合obj.nameは利用しないので、
引数を3つにできると思いました。

```ruby
def serach_link(obj, class_str, &block)
  path = search_path('search[tags][]': obj.id)

  if block_given?
	link_to(path, class: class_str, &block)
  else
	link_to(obj.name, path, class: class_str)
  end
end
```

ただ、この方法だとデータベースのデータをベタ書きしている箇所の呼び出し方法はメソッド呼び出しの方法にする必要があるため、
データベースから呼び出す形にして、オブジェクトのメソッドで呼び出す形にする方がいいのかなと思いました。

データベースのデータベタ書きの箇所をHashで管理しようとすると、
別のHelperにする必要が出てきますが、おおよそ下記のようになるのかなと思いました。
あくまでも一例ですので、もっと良い方法が必ずあると思います。
```slim
- resources = [
-   {id: 1, name: 'A'},
-   {id: 2, name: 'B'},
-   {id: 3, name: 'C'},
-   {id: 4, name: 'D'},
-   {id: 5, name: 'E'},
-   .
-   .
-   .
- ]

- resources.each do |resource|
  = serach_link(resource, 'style-classes')
```

```ruby
def serach_link(obj, class_str)
  path = search_path('search[tags][]': obj[:id])

  link_to(obj[:name], path, class: class_str)
end
```

# 今後

実装時は「完璧じゃん、最高！」とか思っていても、時間が経って見返してみると
「え、なんでこんなことしてんの自分」ということが、よくあります。
成長している証ではあるのですが、振り返っても「よくできてるなぁ」と思えるように今後も学習を続けていきます。

# 参考文献

- [プログラミング中級者になりたい人のためのクリーンコード入門](https://www.udemy.com/course/clean-code-tutorial/)
    
リファクタリングの目的やメリットについて理解が深まりました。
    
- [Ruby技術者認定試験合格教本 Silver/Gold対応　Ruby公式資格教科書](https://www.amazon.co.jp/gp/product/B016B877OG/ref=ppx_yo_dt_b_d_asin_title_o00?ie=UTF8&psc=1)
- [プロを目指す人のためのRuby入門［改訂2版］　言語仕様からテスト駆動開発・デバッグ技法まで](https://www.amazon.co.jp/gp/product/B09MPX7SMY/ref=ppx_yo_dt_b_d_asin_title_o01?ie=UTF8&psc=1)
    
Rubyの基礎学習に利用しました。
**ブロックを使ったメソッド定義**を学ぶことで、
「同じパラメーターのキーで値を渡す必要があるが、構造が違う」
といったケースにも対応できました。
また、**キーワード引数**や**多重代入**などの知識もRailsのコードリーディングをする際に役立ちました。