【Ruby on rails】PDFを登録できるようにする

# はじめに
プログラミングを勉強して2か月ほどです。
初心者なので間違っているところがあれば、ご指摘いただけると幸いです。
※@tohonozo(とほ のぞ)さんの記事を参考に作成しました。
　とても分かりやすく参考になりました。
　ありがとうございます。

# PDFを保存したい
今回、私が作成しているアプリケーションでPDFを保存できるようにしたかったのでやってみました。
Active Sorageなどで画像の保存はやったことはありましたが、PDF保存は初めてでした。
いろんなところで躓いたので、この記事にまとめておこうと思います。

# Let's start!!!
## 最初にCloudinaryに登録
このリンクから登録をはじめましょう。

https://cloudinary.com/

右上のsign up for freeを押すと、登録画面に飛びます。

## PDFファイルを投稿するために簡単なアプリを作成
今回はItemモデルを使用します。

### モデルとコントローラーを作成
`$ rails g model item`
`$ rails g controller items`
上記2つのコマンドを実行してモデルとコントローラーを作成します。

モデルとコントローラーができたらマイグレーションファイルの中身を記述しましょう。


```20xxxxx_create_items.rb
class CreateItems < ActiveRecord::Migration[7.0]
  def change
    create_table :items do |t|
      t.string :name, null: false
      t.string :pdf

      t.timestamps
    end
  end
end
```
マイグレーションファイルが完成したら、`$ rails db:migrate`を実行しましょう。

### Viewの作成
```items/index.html.erb
<a href="<%= image_url(@item.pdf_url) %>" target="_blank">
  <%= image_tag "PDF.jpg", class: "mx-auto item-show-icon" %>
</a>
```
`image_url`は、Railsのヘルパーメソッドの一部で、画像やその他のリソースのURLを生成するために使用されます。この場合、PDFファイルのURLを生成するために使用されています。
`@item.pdf_url`の記述でPDFファイルを生成しています。
`target="_blank"`はリンクを新しいタブまたはウィンドウで開くように指示します。

```items/new.html.erb
<%= form_with model: @item, local: true do |f| %>
<div class="click-upload">
    <p>
      クリックしてファイルをアップロード
    </p>
    <%= f.label :pdf %><br />
    <%= f.file_field :pdf, id:"group-image" %>
</div>
<% end %>
```
上記の記述でPDFを投稿するフォームができると思います。
※idやclass名はこちらで指定したものになるので気にしないでください。

### コントローラーへの記述
```items_controller.rb
private

def item_params
    params.require(:item).permit(:name, :pdf).merge(
end
```
この記述をすることでPDFをパラメーターで取得することができます。

以上で、PDFファイルを投稿する最低限のアプリができました。
それでは、PDFファイルを投稿できるように実装していきましょう！

## PDFファイルの投稿
まずはgemの導入から始めていきます。

### gem追加
```:Gemfile
gem 'carrierwave', '~> 3.0'
gem 'cloudinary'
gem 'dotenv-rails'
```
Gemfileに上記の内容を追記後に
`$ bundle install`を実行しましょう。

### PDFアップローダーの作成
ターミナルに`$ rails g uploader Pdf`と入力して作成しましょう。
`uploader`とは、CarrierWaveというRailsのgemを使ってファイルのアップロードを扱うために使用されます。

### モデルへの記述
```item.rb
class Item < ApplicationRecord

  mount_uploader :pdf, PdfUploader

end
```
`mount_uploader :pdf, PdfUploader`はファイルを指定の場所に保存することを表します。

### アップローダーへの記述
```uploaders/pdf_uploader.rb
# Choose what kind of storage to use for this uploader:
  storage :file
# storage :fog
```
上記を下図のように変えます。
```uploaders/pdf_uploader.rb
 # Choose what kind of storage to use for this uploader:
  if Rails.env.production?
    include Cloudinary::CarrierWave
    CarrierWave.configure do |config|
      config.cache_storage = :file
    end
  else
    storage :file
  end
  # storage :fog
```
cloudinaryは外部のストレージサービスです(S3のようなもの)。本番環境(production)ではcloudinaryにファイルが保存され、それ以外(開発環境=ローカル)では自分のpcに保存されます。(publicフォルダー内に保存されます)

### APIキーを非公開にする
Cloudinaryのアカウントには`Cloud name`、`API Key`、`API Secret`というアカウント固有のIDが付与されています。
APIキーは、APIを使用する際のセキュリティの一部として使用されます。不正なアクセスを防ぐためにAPIキーは秘密に保持されるべきです。れらの値が間違って公開されないような処理を施す必要があります。

`Cloud name`、`API Key`、`API Secret`の値はCloudinaryのDashboardで確認できます。
![cloudinary2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3564879/3af3e60e-97c5-f9b7-dab5-1cfa7bd337e4.png)

### .envファイルへの記述
`.env`というファイルをアプリケーションディレクトリに手動で作成します。
`.env`ファイルはアプリケーションの環境変数を設定するためのテキストファイルです。
※`Gemfile`や`README.mb`などど同じ部分に作成しましょう

作成した`.env`ファイルに下記の内容を記述します。
```:.env
CLOUD_NAME=xxxxxxxxxxx #ここはみなさんのIDを記述しましょう。
CLOUDINARY_API_KEY=xxxxxxxxxxx #ここはみなさんのIDを記述しましょう。
CLOUDINARY_API_SECRET=xxxxxxxxxxxxx #ここはみなさんのIDを記述しましょう。
```

そして、これらの情報を公開しては意味がないので隠しておきましょう。
アプリケーションディレクトリの中の`.gitignore`に下記の記述を追加します。
※.gitignore ファイルは、Git バージョン管理システムによってトラッキングされないファイルやディレクトリを指定するための設定ファイルです。
```.gitignore
/.env
```
これでAPIキーを非公開にすることができました。

### APIキーを環境変数へ変更
`config`フォルダに`cloudinary.yml`ファイルを作成してください。
`cloudinary.yml`ファイルに下記の記述をそのままコピペしてください。
```config/cloudinary.yml
development:
  cloud_name: <%= ENV['CLOUD_NAME'] %>
  api_key: <%= ENV['CLOUDINARY_API_KEY'] %>
  api_secret: <%= ENV['CLOUDINARY_API_SECRET'] %>
  enhance_image_tag: true
  static_file_support: false
production:
  cloud_name: <%= ENV['CLOUD_NAME'] %>
  api_key: <%= ENV['CLOUDINARY_API_KEY'] %>
  api_secret: <%= ENV['CLOUDINARY_API_SECRET'] %>
  enhance_image_tag: true
  static_file_support: false
test:
  cloud_name: <%= ENV['CLOUD_NAME'] %>
  api_key: <%= ENV['CLOUDINARY_API_KEY'] %>
  api_secret: <%= ENV['CLOUDINARY_API_SECRET'] %>
  enhance_image_tag: true
  static_file_support: false
```
これでAPIキーを環境変数に変更することができました。

これで完了！
挙動を確かめてみましょう！

# ここで苦戦...
これで本番環境でPDFを確認できると思いましたが、cloudinaryに遷移してPDFを読み込めませんでしたという表示がされました。
PDFファイルが表示されなかった...

cloudinaryを確認してみると投稿したPDFファイルはしっかりと保存されている。
原因がわからない... 

そこで、保存されているファイルを見ていると気になるところを発見！
![cloudinary.blocked.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3564879/b8be8525-45fe-2b44-f01d-8e017bde552e.jpeg)
※この画面はサイドメニューの「Media Library」から見れます。

`Permission`のところが`Blocked for delivery`となっています。
ここが原因でした！
この記述により制限がかけられていたようです。

それでは、PDFファイルが開くように設定しましょう。
![cloudinary.zip.delivery.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3564879/1ff521b8-8431-f9be-db53-341d038c9175.jpeg)
※「settings」⇒「security」でこの画面に遷移します。

`PDF and ZIP files delivery`の`Allow delivery of PDF and ZIP files`にチェックを入れます。

そして先ほどの画面に戻って確認してみると...
![cloudinary.public.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3564879/77f5f1f8-b865-a3f0-03ec-40c4a145ea48.jpeg)
`Permission`のところが`Public`となっています。
これでようやくPDFが表示されました！

原因がわからず、かなり時間がかかってしまいましたが無事に表示されました！

# 終わりに
みなさん実装お疲れさまでした！
PDF表示ができたと思います。
分かりづらい点が多かったと思いますが、最後まで読んでいただきありがとうございました！！