How to write a conditional in a create action?
I recently worked through the 4 episodes here on direct uploads to S3 with shrine.
I have things setup to upload both images and documents and am now trying to write a conditional to create either an associated image or document record depending on whether the uploaded file is an image or a document (so that I only need one form for users). This is what I have so far, but I feel I might be working in the wrong direction.
Models:
class Project < ApplicationRecord
has_many :project_images, as: :attachable
has_many :project_documents, as: :attachable
end
class ProjectImage < ApplicationRecord
include ImageUploader[:image]
belongs_to :attachable, polymorphic: true
end
class ProjectDocument < ApplicationRecord
include DocumentUploader[:image]
belongs_to :attachable, polymorphic: true
end
Project Images Controller:
def create
@project = Project.find(params[:project_id])
@project_id = params[:project_id]
@project_image = @project.project_images.build(project_image_params)
@project_image.update(user_id: current_user.id, project_id: @project_id)
respond_to do |format|
if @project_image.save
format.html { redirect_to @project }
format.json { render :show, status: :created, location: @project }
else
format.html { render :new }
format.json { render json: @project_image.errors, status: :unprocessable_entity }
end
end
end
Project Documents Controller:
def create
@project = Project.find(params[:project_id])
@project_id = params[:project_id]
@project_document = @project.project_documents.build(project_document_params)
@project_document.update(user_id: current_user.id, project_id: @project_id)
respond_to do |format|
if @project_document.save
format.html { redirect_to @project }
format.json { render :show, status: :created, location: @project }
else
format.html { render :new }
format.json { render json: @project_document.errors, status: :unprocessable_entity }
end
end
end
Project View:
<%= form_for ([@project, @project_image]) do |f| %>
<p>
<%= f.file_field :image, multiple: true, name: 'project_image[image]' %>
</p>
<% end %>
I started by trying to write a conditional in project_images#create but couldn't define how an uploaded document would differ from an image. Maybe I can grab the file extension on the way in somehow?
Then I moved onto another approach, but I'm stuck here as well:
Attachment Model:
class Attachment < ApplicationRecord
belongs_to :attachable, polymorphic: true
belongs_to :project
end
Attachments Controller:
class ProjectAttachment < ApplicationRecord
def create
@project = Project.find(params[:project_id])
@project_id = params[:project_id]
@attachment = @project.attachments.build(attachment_params) # @object inserts here somehow?
@attachment.update(user_id: current_user.id, project_id: @project_id)
respond_to do |format|
if @attachment.save
format.html { redirect_to @attachment.project }
format.json { render :show, status: :created, location: @attachment }
else
format.html { render :new }
format.json { render json: @attachment.errors, status: :unprocessable_entity }
end
end
end
private
def create_object
id = params[:project_document_id] || params[:project_image_id]
model = ProjectDocument if params[:project_document_id]
model = ProjectImage if params[:project_image_id]
@object = model.find(id)
end
end
Project View:
<%= form_for ([@project, @attachment]) do |f| %>
<p>
<%= f.file_field :image, multiple: true, name: 'attachment[image]' %>
</p>
<% end %>
Am I on the right track with either of these attempts?
I can think of a couple ways to handle this but it mainly has to do with what the end product is trying to accomplish.
First I would use the shrine plugin, :determine_mime_type, to determine the file type.
Then you could either continue with your current approach of separating the files in to two separate models or you could set a field called "file_type" on your Attachment model. An attachment is either an "image" or a "document". Then you could create a scope on your Project model for attachments that queries file_type on the association, one for images and one for documents .
This allows you to then simply call project.images or project.documents.
Just my 2 cents and what came to mind when I read your question
Yeah I'm with David on this one. Using the mime type will be the best way to determine how to split these. They probably makes sense to be stored in a generic ProjectAttachment model and then filter then by scoping on the type.
If you combine the two models into a single attachments model, you'll want to update your uploader to be generic. I presume you'll be cropping images and obviously you can't crop documents. Your uploader will need to check the file type before doing the cropping so that only happens for images.
Decided to go with Refile instead of Shrine. I found it easier to work with and was able to implement everything I needed in two days, while I was still wrestling with Shrine after two weeks. I probably just lack the experience that Shrine requires.