Hello, we want to upload a file to an already existing S3 structure and we want to validate the form that the user has uploaded the file otherwise show them the error.
Our code looks like this:
class ObjectController < ApplicationController
load_resource only: [:create]
def create
if csv_is_present? && @object.save
digest = Digest::MD5.hexdigest("#{@object.parent_id}#{@object.id}#{SALT}")
@object.file.attach(
key: "objects/#{@object.parent_id}/#{digest}",
content_type: "text/csv",
filename: params[:object][:file].original_filename,
io: params[:object][:file]
)
redirect_to suppressed_numbers_path, notice: "Successfully created a object."
else
flash.now[:alert] = "Creation of the object was unsuccessful."
render :new, status: :unprocessable_entity
end
end
private
def object_params
# NOTE: Do not permit file as we are not doing assignment of attributes
# We should be calling file.attach as this is the only way for us
# to specify the key of the blob.
params.require(:object).permit(:parent_id)
end
def csv_is_present?
if params[:object][:file]
true
else
@object.errors.add(:file, "can't be blank")
false
end
end
end
class Object < ActiveRecord::Base
has_one_attached :file
end
And we don’t like that, we don’t like that the upload and validation is happening in the controller. We could move the upload to the model with an after commit, but because we have
has_one_attached :file
active_storage attaches the file before that and we need to have a ‘dummy’ attribute to hold the file from the param. Which looks like this:
class ObjectController < ApplicationController
load_resource only: [:create]
def create
if @object.save
redirect_to suppressed_numbers_path, notice: "Successfully created a object."
else
flash.now[:alert] = "Creation of the object was unsuccessful."
render :new, status: :unprocessable_entity
end
end
private
def object_params
params.require(:object).permit(:parent_id, :file_from_param)
end
end
class Object < ActiveRecord::Base
has_one_attached :file
attribute :file_from_param
validates :file_from_param, on: [:create], present: true
after_commit, on: [:create] do
digest = Digest::MD5.hexdigest("#{parent_id}#{id}#{SALT}")
file.attach(
key: "objects/#{parent_id}/#{digest}",
content_type: "text/csv",
filename: params[:object][:file].original_filename,
io: params[:object][:file]
)
end
end
But then we have a dummy attribute that is in the model and we need to have an API of that. Why is there no way of telling active_support to not upload the file directly as we can do some work an upload it on our own time. Are we looking at this wrong?