7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

CarrierWaveでつまづいた箇所(multiple・Cache・_mounter)についてまとめる

Last updated at Posted at 2022-05-21

はじめに

一切かかわりのない方々が書いたコードを保守する機会がありました。
そこでファイルの保存にCarrierWaveを使っており、あまりみかけない使い方をしていて理解に時間がかかったので断片的にはなりますがまとめていきます。

つまづいたところ

CarrierWaveは利用されている割にはネットに情報がないイメージでした
以下が理解に時間がかかった箇所です

ファイルを複数あげたい

こちらの記事を参考にしました。
【Rails】Carrierwaveを用いて画像を複数アップロードする方法

ファイルを単体で上げられる前提で以下を修正しました。

複数ファイルを投稿できるように修正

new.html.erb
= f.file_field :images, multiple: true

CarrierWaveで複数の写真を保存できるように修正

book.rb
mount_uploaders :images, ImageUploader

ポイントは、uploadersにすること

POSTしたパラメータを配列として受けとれるように修正

book_controller.rb
def book_params
  params.require(:book).permit(:title, :body, { images: [] })
end

キャッシュを効かせたい

ファイルアップロードをしたときに、別の箇所(例えばタイトルを入力しないなど)でバリデーションが発生したときに、あげたファイルのデータが消えてしまうため、上げなおさないといけないという問題が起きます。そこでキャッシュを効かせる必要があり対応しました。

【個人メモ】carrierwaveでcacheを取り回す

まず、attr_accesorをモデルに追加します。

book.rb
class Book
  attr_accessor :images_cache
  mount_uploaders :image, ImageUploader
end

attr_accesorについてはRailsから入った人へ【attr_accessor】って?を見るとわかります。DBを使わないでカラムを定義することができるようになります。

つぎに、View側にhidden_fieldを追加します。

new.html.erb
<%= form_with @user, url: user_confirm_path, local: true do |f| %>
  (省略)
  <%= f.file_field %>
  <%= f.hidden_field :images_cache %>
<%= f.submit %>

バリデーションで失敗したときにアップロードしたファイルをを@book.images_cacheに追加してから、newをレンダリングすることで、次のPOSTのときにparams[:images_cache]から前にアップロードしたファイルのデータを取得することができます。

image_cacheへの追加はCreateの最後で行います。

book_controller.rb
  def create
    @book = current_user.users.new(book_params)
  if @book.save!
      redirecto_to test_path
    else
      @book.images_cache = @book.images.map{ |image| images.cache_name }
      render :new
    end
  end

このように保存に失敗したらキャッシュを保存するように設定します。
複数ファイルをアップロードしているので、imagesをmapで回して、cache_nameでキャッシュ用のIDを取得して配列にしてimage_cacheに保存しています。

_mounterについて

私が扱ったコードには_mounterを利用したメソッドがありました。

book_form.rb

def set_image
  _mounter(:images).cache
end

def get_image
  _mounter(:images).uploaders
end

いきなりでてきて戸惑いましたが、以下のコードを参考にすることで理解ができました。

_mounterを使うと以下のファイルのメソッドが呼び出されます。
_mounter(:images).cacheで実際にCarrierWaveでファイルを保存して、_mounter(:images).uploadersで保存したファイルに関するUploadersオブジェクトを取得することができます。このオブジェクトを用いてCreateなどをするとCarrierWaveのレコードが作成されます。

キャッシュの復元

複数ファイルのキャッシュの復元に以下を行っていました。

book_form.rb
def get_cache_images
  _mounter(:images).cache_names=images_cache
end

images_cacheはhiddenで保持したキャッシュです。
ここでも_mounterを利用して、cache_nameメソッドを呼び出してUploaderオブジェクトを取得しています。
以下は_mounterの該当するコードです。

_mounter.rb
    def cache_names=(cache_names)
      cache_names = cache_names.reject(&:blank?)
      return if cache_names.blank?
      clear_unstaged
      cache_names.each do |cache_name|
        begin
          uploader = blank_uploader
          uploader.retrieve_from_cache!(cache_name)
          @uploaders << uploader
        rescue CarrierWave::InvalidParameter
          # ignore
        end
      end
    end

_mounter(:images).cache_names=images_cacheとすることでgetterを呼び出します。左辺に右辺を入れているように見えるので注意が必要です。メソッドを呼び出しているだけです。
これでキャッシュの復元ができるようになります。復元はuploader.retrieve_from_cache!(cache_name)の箇所で実際には行っています。

名前が取れない

images.map{ |i| i[:name]}

で取れました。ハッシュでとるのと複数の考慮が必要でした

おわりに

コードに関しては適当に書いているものなので動くことは保証できませんのでご理解ください。
今回はCarrierWaveについての考え方や使い方をまとめました。バックエンドを気にせずに簡単利用することができるのがよいところですが、カスタマイズもうまくできることがわかりました。

参考文献

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?