I am trying to test for uploading documents in rails. I am using carrierwave to upload documents. The relevant code is given below.
Document Model Test Code
class DocumentTest < ActiveSupport::TestCase
def setup
#category = categories(:category_a)
#document = #category.documents.build(file_name: "abc", file: "abc.doc")
end
test "document valid" do
assert #document.valid?
end
Document Model Code
class Document < ActiveRecord::Base
belongs_to :category
mount_uploader :file, FileUploader
validates :file_name, presence: true
validates :file, presence: true
validate :file_size
end
file_uploader code
The following are the files I have whitelisted.
def extension_white_list
%w(pdf doc htm html docx xlsx xml)
end
I get a Failed assertion when I run the tests. I commented out the validates :file, presence: true code and the tests pass. I have file as type string in the database. However, passing in a string value is the same as passing a blank value. Therefore, the presence: true validation fails. I am not sure why that is failing even though I am passing in a string value. I think I need to pass in additional params or information for the tests to know what kind of file it is. Thanks!
This is not how you pass a file to the carrierwave model. Have a look at these examples:
https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Use-test-factories
Also, carrierwave has its own validators you might want to use:
https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Validate-uploads-with-Active-Record
Related
I have Axlsx::Package.new object which is used to create a excel file in backend and now i have to upload this excel file to s3 using carrierwave gem. I have either file path or Axlsx::Package.new object but in both case it is throwing file can't be blank.. I am not sure what I am doing wrong.
Please help me how can I achieve this.
class Attachment < ApplicationRecord
# CarrierWave
mount_uploader :file, AttachmentUploader
validates :file, presence: true
end
when I execute this line, where I don't have ActionDispath::FileUpload object as i am creating file in backend not from UI form. due to this I have
file = Axlsx::Package.new
file_path = "uploads/exports/download_request_#{current_datetime}.xlsx"
attachment = Attachment.create!(file: file, entity_type: self.class)
i tried with file also and file_path also but it is not working and throwing file can't be blank...
class Attachment < ApplicationRecord
# CarrierWave
mount_uploader :file, AttachmentUploader
validates :file, presence: true
end
it should save to s3 as how it is working in case when i upload file from UI form using input type file tag.
Please help
I'm using Shrine for direct uploads to S3 and I'm trying to user the pretty_location plugin to set the location in my S3 bucket.
I have a document model has the file_datatext attribute and is connected to a FileUploader:
class Document < ApplicationRecord
belongs_to :documentable, polymorphic: true
include FileUploader[:file]
validates :file, presence: true
end
Other models are associated with the document model through the following concern:
module Documentable
extend ActiveSupport::Concern
included do
has_one :document, as: :documentable, dependent: :destroy
accepts_nested_attributes_for :document, allow_destroy: true
end
end
This is my FileUploader:
class FileUploader < Shrine
Attacher.promote { |data| PromoteJob.perform_later(data) }
Attacher.delete { |data| DeleteJob.perform_later(data) }
plugin :upload_options, cache: {acl: "public-read"}
plugin :upload_options, store: {acl: "public-read"}
plugin :logging, logger: Rails.logger
plugin :pretty_location
plugin :processing
plugin :delete_promoted
plugin :recache
plugin :restore_cached_data
plugin :delete_raw
plugin :validation_helpers
def generate_location(io, context = {})
# do something depending on context[:record].documentable
end
end
When uploading files from the user's filesystem via the client browser through nested attributes all works as expected and I'm able to generate a pretty location in my S3 bucket.
However, I have another model where I am trying to upload to S3 a PDF file which is generated in the backend with the following setup.
class Invoice < ApplicationRecord
has_one :documents, as: :documentable, dependent: :destroy
end
The Invoice model doesn't use the concern as I want it to connect to the polymorphic document with a has_many association.
class Api::V1::InvoicesController < Api::V1::BaseController
def upload_pdf
pdf = InvoicePdf.new(#invoice)
attachment = pdf.render
file = StringIO.new(attachment)
file.class.class_eval { attr_accessor :original_filename, :content_type }
file.original_filename = "invoice_#{#invoice.reference}.pdf"
file.content_type = "application/pdf"
#document = #invoice.documents.new(file: file)
if #document.save
render "documents/show.json", status: :created
else
render json: { errors: #document.errors }, status: :unprocessable_entity
end
end
end
The upload works fine and I am able to upload the PDF to my S3 bucket, but I am not able to generate a pretty location because when I'm inside the generate_location method the context[:record] both the documentable_type and the documentable_id are nil.
This is a strange behaviour as in the rails console I am able to see that the association is correctly set after the upload has been done (without pretty_location) by running Invoice.last.documents.file.url.
I have tried creating the document record in different ways, have tried using the same documentable concern that works for other models but the result is alway the same and I have run out of ideas.
Does anyone have a clue why the documentable_type and documentable_id are not being passed into the context object inside the FileUploader?
The above setup actually works. I was using a breakpoint inside the generate_location FileUploader method and the api was breaking because that method was returning nil.
After fixing that, the first time it ran documentable was still nil but the method would run a second time with the documentable attributes present.
def generate_location(io, context = {})
return "" unless context[:record].documentable
path = if context[:record].documentable_type == "SomeClass"
# do something
elsif context[:record].documentable_type == "OtherClass"
# do something else
else
# do something else
end
return path
end
I'm implementing Carrierwave with fog storage into my Rails App. The whole purpose of this app is to store pdf articles and have the ability for anyone with access to the app to retrieve them. Right now, I have the functionality of storing the article pdf working great. But, now I need to be able to retrieve the pdf from S3. Is this possible? I noticed in the docs there is a uploader.retrieve_from_store!("my_file.png") method. I tried to run this in the console and I got this error NoMethodError: undefined method retrieve_from_store! for ArticleUploader:Class Any help with this would be great! I'm just not finding any suitable answers so far. Thanks!
Article Uploader
class ArticleUploader < CarrierWave::Uploader::Base
storage :fog
def extension_whitelist
%w(jpg jpeg gif png pdf)
end
end
Article Model
class Article < ApplicationRecord
mount_uploader :file, ArticleUploader
validates :title, presence: :true
validates :publication_date, presence: :true
validates :source, presence: :true
end
You can access it with the methods that carrierwave has, in your case:
article = Article.find(1)
article.file.url
If you are on development it will output the path for the file and if you are on production and using S3, it will output the whole url, http://s3.amazonaws.com/<vendor>/articles/1/file.pdf for example.
You can find more information on the official docs:
https://github.com/carrierwaveuploader/carrierwave#activerecord
There is also an old rails cast on that http://railscasts.com/episodes/253-carrierwave-file-uploads
I'm trying to make sure that every instance of Picture model has a file attached.
In other words - there are two fields in the form:
:file
:remote_file_url
I want user to fill in at least one of them.
When I validate presence of :file, and submit remote_file_url instead of file - then it gives validation error.
The only way I found for now is do smth like this:
class Picture < ActiveRecord::Base
validate :file_xor_remote_file_url
private
def file_xor_remote_file_url
if !(file.blank? ^ remote_file_url.blank?)
errors.add(:base, "Specify a file to upload, or file URL, not both")
end
end
end
The wiki says you can validate your upload like this:
mount_uploader :avatar, AvatarUploader
validates_presence_of :avatar
It doesn't mention any different handling for remote URLs, but underlines that Carrierwave validates the presence of the file rather than just the presence of an URL. So a given remote URL must refer to a valid file that can be uploaded.
You said that "validate presence of :file", but perhaps this could be the point. Hope this helps.
I have white listed some of the extensions in the carrierwave uploader class
def extension_white_list
%w(doc docx)
end
In some cases I would like to skip the Integrity validation while saving a record. But as per their documentation validates_integrity_of validation exist by default.
https://github.com/carrierwaveuploader/carrierwave/wiki/How-to%3A-Validate-uploads-with-Active-Record
can anyone please tell me how to skip such validation ?
in uploaders/file_uploader.rb
def extension_white_list
if model.do_i_need_validation?
%w(doc docx)
else
file.extension
end
end
and define this instance method in the model
def do_i_need_validation?
condition? ? true : false
end
Just replace the content of the method suitable to your app
I couldn't find anything about this in any of carrierwave's documentation, but reading its source code, one can pass specific uploader options in the mount_uploader call:
mount_uploader :field, MyUploader, options
Validations configuration do exist in uploader options, so you can, for example, disable all validations using:
mount_uploader :field, MyUploader, validate_download: false, validate_integrity: false, validate_processing: false
Note that when doing this the errors are silently ignored, so saves will succeed. This could be unexpected behavior. You can check if the operation actually had any errors using the model helpers <field>_processing_error, <field>_integrity_error and <field>_download_error:
class Article < ActiveRecord::Base
mount_uploader :image, ImageUploader, validate_integrity: false
end
article = Article.find(1)
article.update_attributes!(title: "New article title", image: open("/path/to/invalid_image.jpg")) # => this will actually succeed
article.image_integrity_error # => returns the error message from carrierwave