【Rails on Docker on Heroku】ActiveStorage + Cloudinaryで画像を管理するメモ

# はじめに
この記事はRuby on RailsのActiveStorageとCloudinaryを使って画像を管理するアプリを作るためのメモです。PaaSとしてHerokuを使います。

[ActiveStorage](https://railsguides.jp/active_storage_overview.html)はRails 5.2以上で動作するクラウドストレージサービスへのファイルのアップロードやActiveRecordオブジェクトへのファイルのアタッチをやってくれる便利なモデルです。

[Cloudinary](https://cloudinary.com)は画像や動画のCDNや編集の機能を有するSaaSです。Herokuのadd-onsとしても存在しています。

# この記事のゴール
この記事では、Heroku上でユーザー名とプロフィール写真を管理するRuby on Railsアプリケーション（on Docker）をHeroku上で動作させることをゴールにします。DB/StorageはHerokuのadd-onsからPostgres/Cloudinaryを使います。
![Untitled Diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254216/27218aa2-43ac-579d-73e5-a2cf8e9df23a.png)

# 前提
- Rails on Dockerの準備ができていること --> [Quickstart: Compose and Rails | Docker Documentation](https://docs.docker.com/compose/rails/)
- Herokuのアカウントが準備できていること

# メモ
## 1. ActiveStorageのアプリを作る
こちらの記事が参考になりました。
[【Rails 5.2】 Active Storageの使い方 - Qiita](https://qiita.com/hmmrjn/items/7cc5e5348755c517458a)

まずファイルをアタッチするモデルとしてUserモデルをscaffoldで作っておきます。

```
$ docker-compose run --rm web rails g scaffold user name:string
$ docker-compose run --rm web rails db:migrate
```
続いて、ActiveStorageをインストール。

```
$ docker-compose run --rm web rails active_storage:install
$ docker-compose run --rm web rails db:migrate
```
ActiveStorageはモデルに`has_one_attached`を定義するだけでファイルを扱えるようになる優れものです。

```ruby:app/models/user.rb
class User < ApplicationRecord
  has_one_attached :photo
end
```
これで`photo`という名前のファイルをActiveStorageが勝手に管理してくれます。
ファイルのアップロードと参照ができるようにview/controllerも手直ししておきます。

```rb:app/controllers/users_controller.rb
def user_params
  # photoの登録を許可
　params.require(:user).permit(:name, :photo)
end
```

```erb:app/views/users/_form.html.erb
<!-- 以下をいい感じのところに追加 -->
<div class="field">
  <%= form.label :photo %>
  <%= form.file_field :photo %>
</div>
```

```erb:app/views/users/show.html.erb
<!-- 以下をいい感じのところに追加 -->
<div>
  <% if @user.photo.attached? %>
    <%= image_tag @user.photo %>
  <% end %>
</div>
```

これで`User`モデルで`photo`としてファイルを管理できるようになりました。
この段階では`photo`として登録されるファイルは`storage/`に保存されていきます。これは

```config/environments/development.rb
config.active_storage.service = :local
```

```config/storage.yml
local:
  service: Disk
  root: <%= Rails.root.join("storage") %>
```
で定義されています。

## 2. Herokuの準備
HerokuはHeroku CLIで操作していきます。まずはadd-onsでpostgresとcloudinaryの準備が必要です。まずHerokuにログインしてアプリを作成します。

```
$ heroku login
$ heroku create app-name
```
このアプリに対してpostgresとcloudinaryのadd-onsを追加してきます。postgresは無料のhobby-dev、cloudinaryも無料のstarterというプランを使っていきます。cloudinary:starterはクレカ登録してないと使えないみたいですね。

```
$ heroku addons:create heroku-postgresql:hobby-dev
$ heroku addons:create cloudinary:starter
```

## 3. ActiveStorageとCloudinaryを連携させる
ActiveStorageとCloudinaryを連携させるために[GitHub - 0sc/activestorage-cloudinary-service](https://github.com/0sc/activestorage-cloudinary-service)を利用します。

READMEの通りにやっていけばOKです！

```yaml:Gemfile
gem 'cloudinary', require: false
gem 'activestorage-cloudinary-service'
```

```
$ docker-compose build
```
Cloudinaryと連携するための`cloud_name`、`api_key`、`api_secret`の情報をRailsアプリに登録してきます。これらの情報はHerokuサイトでOverviewのところから各アドオンのサイトにリンクされているんですが、リンク先のCloudinaryのDashboardに情報が載ってます。
Rails5.2以降であれば`credentials`を利用しているかと思いますので、そちらの方法で。

```
$ docker-compose run --rm -e EDITOR="vim" web rails credentials:edit
```
```
cloudinary:
  cloud_name: xxxxxxxxxx
  api_key: xxxxxxxxxx
  api_secret: xxxxxxxxxx
```
※vimがないとか言われる場合は[【Rails 5.2.2】Rails on Dockerでcredentialsをeditしたい - Qiita](https://qiita.com/at-946/items/8630ddd411d1e6a651c6)あたりを参考にしていただけますと！
この設定を`config/storage.yml`から呼び出します。

```config/storage.yml
# 以下を追加
cloudinary:
  service: Cloudinary
  cloud_name: <%= Rails.application.credentials.dig(:cloudinary, :cloud_name) %>
  api_key: <%= Rails.application.credentials.dig(:cloudinary, :api_key) %>
  api_secret: <%= Rails.application.credentials.dig(:cloudinary, :api_secret) %>
```

そして、`config/environments/development.rb`で指定されている`config.active_storage.service`も書き換えます。

```rb:config/environments/development.rb
# config.active_storage.service = :local
config.active_storage.service = :cloudinary
```
本当は`production.rb`を書き換えて、heroku側のconfigも`RAILS_ENV=production`とするべきなのですが、今回のメモはお試しなのでdevelopmentで進めますね。

ここまででRailsアプリ、Herokuのadd-onsの準備は完了になりますので、あとはこのアプリをdeployしていくだけです。

## 4. Rails on DockerをHerokuにDeployする
Rails on DockerのHerokuへのDeploy方法は以下の記事でもまとめています。
[Rails on DockerをHerokuでDeployするまで - Qiita](https://qiita.com/at-946/items/6958b1d35330c87b8e50)

この記事ではpostgresのadd-ons追加などが完了しているので必要なところだけ書いていきます。

```
$ heroku container:login
$ heroku container:push web
$ heroku container:release web
$ heroku run rails db:migrate
```
これでアプリのデプロイが完了するはずなので、あとはサイトを開いてみます。

```
$ heroku open
```
ローカルでテストしてみた時と同じようにファイルをアップロードできるはずです。
また、Cloudinaryのサイトの「Media Library」にその画像ファイルが登録されているのみ見れるはずです。

ちなみにActiveStorageはそのモデルが削除されるときに画像ファイルも一緒にdeleteしてくれるのもすごい便利ですね。

# あとがき
ということで、Rails on Docker on HerokuでActiveStorage + Cloudinaryの画像管理ができました。
Carrier Waveとかなしで画像扱えるのは便利でした〜。herokuも1コマンドでアドオンが追加できて便利！