マジックナンバーとrailsにおける定数管理gem "config"

## この記事の目的

開発中に「マジックナンバー」と言う聞き慣れない(恥ずかしながら)言葉を耳にし、その意味と問題点を調べたのでまとめました。また、Railsにおける「マジックナンバー」への対応策の一つを紹介したいと思います。

この記事で以下のことが分かると思います。

- マジックナンバーとは何か
- 開発におけるマジックナンバーの問題点
- その解決策の一つであるrailsのgem "config"の扱い方

## 1. マジックナンバーとは

マジックナンバーとは、ハードコーディングされた数値のことです。

ハードコーディングとは、**固有名詞・固有値**をソースコード内に**直接**埋め込んだコードのことです。
ソースコードとは、開発者がコーディングしているファイルの内容のことです(index.html, stylesheet.cssなど大体そうかと)。

ハードコードの中でも数値のことを、プログラミングにおいて「マジックナンバー」と呼びます。

```erb:index.html.erb

# ハードコードの例
<li>
  <p>山田太郎<p>
  <p><span>23</span>歳です</p> # 23がマジックナンバー
</li>
<li>
  <p>田中花子</p>
  <p><span>73</span>歳です</p> # 73がマジックナンバー
</li>
:
```

上記はSNSにおけるユーザー一覧の例です。恐らく、こんな直接的な書き方はしないと思います。
一般にマジックナンバーはNGとされています(そもそもハードコードがNGなのかな...)。
その理由を次に説明します。

## 2. マジックナンバーが何故いけないのか
一般にマジックナンバーは、コーディングの際に避けるべきだ、と言われています。その主な理由は3つだと思われます。

1. 数字だけ見ても何を意味しているか分からない[可読性が低い]
2. 変更する場合は全てのマジックナンバーが利用されている場所を変更する必要がある
[保守性が低い]
3. セキュリティの安全性が下がる[脆弱性を生む]

3つの理由の説明はその他のサイトでも説明されていると思います。ここでは、少し具体的なマジックナンバーを使ってしまいそうな例を提示してみます(実体験)。

```ruby:products_controller.rb
def show
  @product = Spree::Product.find(params[:id])
  @related_products = @product.related_products(4) # マジックナンバー
end
```

この`4`という数値が何を意味しているか、このコードを見ただけでは分かりません。
これは下の画像における表示個数の個数を表します。このように、実際の機能に対してコードの意味が伝わりにくくなってしまうのがマジックナンバーの良くない点です。
これ以降では、Railsにおけるマジックナンバーに関する対応策である`config`と言うgemの扱いを簡単に紹介したいと思います。

[画像挿入]

## 3. Railsにおける定数管理のgem "config"

以下で具体的な使い方は説明しますが、このgemのいいところは以下の2つだと思います

- 定数の意味をソースコード内で表現できる
- 一箇所で定数を管理できる

これがリソースです。
[rubyconfig/config](https://github.com/rubyconfig/config)

gem "config"は、定数管理を指定のYAMLファイルを用いて一箇所で行う為のgemです。場合によっては環境毎に(development, production, test)定数の値を変更することもできます。
ここでは、簡単な方法を一つ紹介し、先程の例を解決したいと思います。

## 4. gem "config"の使い方

ここでは最も簡単だと考えられる、一つのファイルで定数を管理する方法を示します。
その場合`config/settings.yml`のみ書き込むことで定数管理を実現します。

### (1) gem "config"の準備

```ruby:Gemfile
gem "config"
```

```shell:Terminal
$ bundle install
$ rails g config:install
			create  config/initializers/config.rb
      create  config/settings.yml
      create  config/settings.local.yml
      create  config/settings
      create  config/settings/development.yml
      create  config/settings/production.yml
      create  config/settings/test.yml
      append  .gitignore
```

以下のようにファイルが追加されます(自動的に.gitignoreに追加されていました)。

### (2) 定数を管理する

`config`では、木構造のように定数を管理します。
今回は「商品の個数」の中で、「上限の数値」「テストの際の数値」といった定数を定義します。(このように記載することで、先程の例を解決します)

```ruby:config/settings.yml
products_count:
  max_count: 4
  test_count: 10
```

このファイルにて定数の管理を、一括で扱います。また、定数への意味付けも可能になります
[可読性上がる]。

### (3) - 1 定数を取り出す

定数は以下のように記載することで取り出すことができます。

```terminal:Terminal
$ rails c
[1] pry(main)> Settings.products_count.max_count
=> 4
[2] pry(main)> Settings.products_count.test_count
=> 10
```

設定に応じて`Setteings. ~ .~`の~部分を置き換えることで、定数を取り出すことが可能になります。

### (3) - 2 定数を取り出す(ファイル内で)

以上を参考に先程の例を、このように解決することができます。

```ruby:products_controller.rb
def show
  @product = Spree::Product.find(params[:id])
  @related_products = @product.related_products(Settings.products_count[:max_count])
end
```


マジックナンバーを比べていた時に比べ、可読性が少しは上がります(恐らく)。

以上です。この記事が少しでも参考になるといいな、と思います。


## 余談

マジックナンバーには、エンジニアの方にコードレビューして頂いた際に出会いました。「実装要件を満たした」と思った自分に対して、予想のできない指摘が飛んできました。実務の世界では当たり前のことができていない、本当にまだまだなのだな、という実感を沸いた経験でした。

## リソース

以下を参考にさせて頂きました

[https://wa3.i-3-i.info/word12868.html](https://wa3.i-3-i.info/word12868.html)

[https://qiita.com/RyoheiHashimoto/items/38ec132bd2852238295e](https://qiita.com/RyoheiHashimoto/items/38ec132bd2852238295e)

[https://it-biz.online/it-skills/hard-coding/](https://it-biz.online/it-skills/hard-coding/)

また、gem "config"に関しましてはその他多くの説明が存在しますので、詳細はそちらを参考にすると実装が捗るかと思います。