【Rails】本番環境でのアセットプリコンパイル

## はじめに
herokuへデプロイした際にアセットパイプラインやマニフェストファイルについて理解を深めたので、その内容をまとめました。

※こちらは前回の投稿です。
[【エラー】Error: node is not installed. の解決方法](https://qiita.com/k12da/items/a0c486579ebba6ca0e23)

## 開発環境
Ruby | 3.2.3
Ruby on Rails | 7.1.3
MySQL | mysql5.7
Docker Compose

## 前提
### **アセットパイプライン**
Railsでアセットファイル（画像・CSS・JavaScriptなど）を効率的に配信するためのフレームワークです。
アセットファイルは以下の例のようなディレクトリ構造になっています。
```
app 
 └─ assets
    ├─ config
    |  └─ manifest.js
    ├─ images
 　　　　　　|　　　　└─ default_avatar.png
　　   └─ stylesheets
       └─ application.css
 
```

### **アセットプリコンパイル**
Railsアプリケーションのアセット（JavaScript、CSS、画像など）を事前にコンパイルするためのコマンドです。
以下のコマンドで、本番環境向けに手動でプリコンパイルすることができます。
```
$ RAILS_ENV=production rails assets:precompile
```

### **manifest.js**
アセットをコンパイルするときに、アセットのcssファイルやjsファイルなどをまとめて読み込むための設定を行う、Sprocketsのマニフェストファイルです。
ディレクティブという独自のコメントを記述することによって、対象のファイルを指定することができます。
```assets/config/manifest.js
//= link_tree ../images
//= link_directory ../stylesheets .css
//= link_tree ../../javascript .js
//= link_tree ../../../vendor/javascript .js
```

>このファイルにはディレクティブ（directive）が含まれています。ディレクティブは、単一のCSSファイルやJavaScriptファイルをビルドするためにどのファイルが必要かをSprocketsに指示します。
上のマニフェストファイルは、./app/assets/imagesディレクトリやそのサブディレクトリにあるすべてのファイル、./app/javascriptディレクトリや./vendor/javascriptで直接JSとして認識されるすべてのファイルの内容をインクルードすることを意味しています。
このマニフェストファイルは ./app/assets/stylesheetsディレクトリにあるすべてのCSSを読み込みます（ただしサブディレクトリは含めません）。
./app/assets/stylesheetsフォルダにapplication.cssファイルとmarketing.cssファイルがあると仮定すると、ビューに<%= stylesheet_link_tag "application" %>または<%= stylesheet_link_tag "marketing" %>と書くことでこれらのスタイルシートを読み込めるようになります。

#### Q.javascriptファイルがassetsディレクトリから読み込まれないのはなぜ？
>その理由は、./app/javascriptが既にimportmap-rails gemのデフォルトのエントリポイントになっていて、vendorフォルダはダウンロードしたJSパッケージの置き場所になっているからです。


### **Sprockets**
端的に言うと、アセットファイル（cssファイルや画像ファイルなど）をひとまとめにして、圧縮してくれる機能です。


## アセットプリコンパイルの実行結果
```
$ RAILS_ENV=production bin/rails assets:precompile
I, [2024-06-08T03:33:49.625772 #159]  INFO -- : [dotenv] Loaded .env
I, [2024-06-08T03:33:52.526211 #159]  INFO -- : Writing /event-management-app/public/assets/application-af22eef2d991d90a3c42d9384913469543786a9e07f8575a15308052a8a6e48d.css
I, [2024-06-08T03:33:52.526843 #159]  INFO -- : Writing /event-management-app/public/assets/application-af22eef2d991d90a3c42d9384913469543786a9e07f8575a15308052a8a6e48d.css.gz
I, [2024-06-08T03:33:52.531707 #159]  INFO -- : Writing /event-management-app/public/assets/login-ed2245a88bb76e43ed8d3494c42c9d40a6c0294533fe1304f7fd420b26fd18a3.css
I, [2024-06-08T03:33:52.531923 #159]  INFO -- : Writing /event-management-app/public/assets/login-ed2245a88bb76e43ed8d3494c42c9d40a6c0294533fe1304f7fd420b26fd18a3.css.gz
I, [2024-06-08T03:33:52.535468 #159]  INFO -- : Writing /event-management-app/public/assets/bootstrap.min-9c65d9e27926afe0cb1ca7bd76bef901e6271544f59549d308fc11d62bb6d7fe.css
```

### アセットファイルはどこに保存される？
この時にSprocketによってアセットが読み込まれます。
またSprocketは、ひとまとめにしたアセットファイルをpublicフォルダ配下にコピーします。
```
app 
 └─ assets
 |   ├─ config
 |   |  └─ manifest.js
 |   ├─ images
 |　　　　　　|　　　　└─ default_avatar.png
　　|   └─ stylesheets
 |      └─ application.css
 └─ public
    └─ assets
        ├─ .sprockets-manifest-0e17426482147bbe2b1dcc26aa8c7b01.json
        ├─ application-5dfd84014934ea2834c32c61ca4f95059397f0251a70ba4ae985abe8fe7e377a.css
        └─ default_avatar-d15493959b5c7f75a17701785b0641a74400150c7b44830ecd5e95b5864a434b.png
```

### manifest.jsに記載がない場合はどうなる？
アセットパイプラインの仕様か、本番環境でアセットファイルがプリコンパイルされません。
そのため、以下のasset.rbファイルに手動でプリコンパイルするファイルを指定します。
（非常に非効率で面倒くさい）

```config/initializers/assets.rb
Rails.application.config.assets.precompile +=
%w(
  *.js *.css *.png
  bootstrap.min.js
  popper.js
  javascript/controllers/application.js
  javascript/controllers/hello_controller.js
  javascript/controllers/index.js
  application.js
  flash.js
  preview.js
  default_avatar.png
  events/no_image.png
  categories/art.png
  categories/medical_care.png
  application.css
  login.css
  events/index.css
  events/pagination.css
  shared/colors.css
  shared/footer.css
  shared/common.css
  users/mypage.css
)
```
なお、categoriesやeventsなどのサブディレクトリ直下のファイルを一つずつ追記しているのは、[サブディレクトリが含まれずコンパイルされないから](https://railsguides.jp/asset_pipeline.html#%E3%83%9E%E3%83%8B%E3%83%95%E3%82%A7%E3%82%B9%E3%83%88%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%A8%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%86%E3%82%A3%E3%83%96)

以下のようにワイルドカードを指定してできると思ったのになぁ...
```
  categories/*.png
  events/*.png
```

（もしサブディレクトリ下のファイルを含める方法があれば教えてください🙏）

## デプロイ実行結果
```
$ git push heroku main
Enumerating objects: 12, done.
Counting objects: 100% (12/12), done.
Delta compression using up to 8 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (7/7), 780 bytes | 260.00 KiB/s, done.
//（省略）
remote: -----> Detecting rake tasks
remote: -----> Preparing app for Rails asset pipeline
remote:        Running: rake assets:precompile
remote:        Asset precompilation completed (1.50s)
remote:        Cleaning assets
remote:        Running: rake assets:clean
remote: -----> Detecting rails configuration
(省略)
remote: -----> Compressing...
remote:        Done: 250.3M
remote: -----> Launching...
remote:        Released v5
remote:        https://event-manage-app-13c44e27b30b.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
To https://git.heroku.com/event-manage-app.git
   1c6fcbb6..aa2c83d1  main -> main
```
デプロイ時に自動的にプリコンパイルが実行されます。

## まとめ
- アセットパイプラインを使うことで複数のファイル（css,js,画像など）を連結/圧縮してまとめてコンパイルできる。
- マニフェストファイル（manifest.js）には、アセットファイルを連結する対象のファイルを設定する。

## 参考記事
[1 アセットパイプラインについて](https://railsguides.jp/asset_pipeline.html#%E3%82%A2%E3%82%BB%E3%83%83%E3%83%88%E3%83%91%E3%82%A4%E3%83%97%E3%83%A9%E3%82%A4%E3%83%B3%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)
[3.1 マニフェストファイルとディレクティブ](https://railsguides.jp/asset_pipeline.html#%E3%83%9E%E3%83%8B%E3%83%95%E3%82%A7%E3%82%B9%E3%83%88%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%A8%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%86%E3%82%A3%E3%83%96)
[4.1 アセットをプリコンパイルする](https://railsguides.jp/asset_pipeline.html#%E3%82%A2%E3%82%BB%E3%83%83%E3%83%88%E3%82%92%E3%83%97%E3%83%AA%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E3%81%99%E3%82%8B)
[【Rails】アセットパイプライン（Sprockets）の基本情報と実装方法](https://autovice.jp/articles/143)
[Railsのマニフェストファイルを使ったJsとStylesheetの呼び出し](https://qiita.com/samurai_runner/items/da22eddb64e867b4e145)
[コードのデプロイ(heroku)](https://devcenter.heroku.com/ja/articles/git#deploy-your-code)