現在、個人開発中のアプリがかなり完成に近づきつつある中、新たに新しいバグを見つけてしまいました、、(リリース前に気付いてよかった)
前置き長いんで、さっさと解決策知りたい方はこの辺飛ばしちゃってください!!
プロフィール画像などの画像のアップロード機能をActiveStorageを使い実装したのですが、以下のようなバグが見つかりました。
例えば、あるユーザーがユーザー編集ページで画像の設定をしたとします。そして、次にパスワードを変更したくなったとします。当然、ユーザーは新しいパスワードのみ入力し、その値が編集ページのフォームからポストされます。
このとき、フォームのファイル選択エリア(file_field)には前回アップロードした画像は設定されていないので、この状態でupdateアクションが実行されてしまうと前回アップロードした画像が消えてしまい、アプリで設定しているデフォルトの画像で上書きされてしまうのです。
つまり、このままでは編集をするたびに毎回画像をアップロードしないと画像の変更を維持できないという最悪のUXをユーザーに提供してしまいます。これはなんとかしなければいけませんね、、というわけで解決策を書きます!
##解決策(超簡単)
コントローラーに以下を追記します。
def update
@user = User.find(params[:id])
@user.avatar.attach(params[:avatar]) if @user.avatar.blank?
if @user.update(user_params)
flash[:success] = 'プロフィールを更新しました'
redirect_to @user
else
render 'edit'
end
end
**if @user.avatar.blank?**を追記するだけです!
実は、ググってもググってもActiveStorageですでに設定された画像のキャッシュを作成するやり方がわからなかったので、自分で考えて色々と仮説検証した結果この一文を編み出し、解決することができました。(CarrierWaveなら出てきた)
テストもパスすることを確認しました。(この一文を消すとちゃんとテストに失敗することも確認済)
しかし、これだと
既に設定されている画像を普通に変更する場合、@user.avatar.bkank?
がfalseになって@user.avatar.attach(params[:avatar])
が実行されずに画像の変更ができないんじゃないか?
と思いませんか?僕は思います。。wでも、実際画像は更新されるし、テストもパスするのでとりあえずこれで良しとしようと思います。
どなたかこのあたりの原理が分かる方、もしよければコメントいただけますと幸いです。
##テストコード
補足として実際にパスしたテストコードをここに掲載します(関連する部分のみ抜粋)。
require 'rails_helper'
RSpec.describe '画像のアップロード', type: :system do
let(:user) { FactoryBot.create(:user) }
before do
valid_login(user)
end
# 画像をアップロードして保存する
def upload_user_avatar(user)
visit edit_user_path(user)
attach_file 'user_avatar', "#{Rails.root}/spec/fixtures/images/test.jpg"
click_on '保存する'
end
it 'userがアップロードした画像がマイページに表示されること' do
upload_user_avatar(user)
expect(page).to have_selector("img[src$='test.jpg']")
end
it "userが画像の更新に成功すること" do
visit edit_user_path(user)
attach_file 'user_avatar', "#{Rails.root}/spec/fixtures/images/updated_test.jpg"
click_on '保存する'
expect(page).to have_selector("img[src$='updated_test.jpg']")
end
it "画像をアップロードせずに編集したとき、デフォルトの画像データで上書きされないこと" do
upload_user_avatar(user)
visit edit_user_path(user)
click_on '保存する'
expect(page).to have_selector("img[src$='test.jpg']")
end
end
#最後まで読んでいただきありがとうございます!
日々学んだことをアウトプットしてます!ご指摘などあれば是非ともコメントいただけますと嬉しいです!!