Carrierwave Mongoid validation of file - ruby-on-rails

I cant figure out how to validate that carrierwave has uploaded a document to my mongoid object.
i have a Document Class
class Content::Document < Content
mount_uploader :attachment, DocumentUploader
field :attachable_id
field :attachable_type
end
and an uploader:
require 'carrierwave/orm/mongoid'
class DocumentUploader < CarrierWave::Uploader::Base
storage = :filesystem
include CarrierWave::RMagick
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def extension_white_list
%w(doc docx xls xlsx ppt pptx pdf )
end
i would like to validate that the upload exists and that it matches the white list else through a standard validation error
this is on Rails 2.3.8

While it's true that Carrierwave does have extensive tests, you could test for validity with something like this:
it "is valid with valid attributes" do
file_bytes = File.open("spec/binary/avatar.png")
valid_attrs = {:name => "foo", :description => "bar", :avatar => file_bytes}
user = User.new(valid_attrs)
user.should be_valid
end
Hope that helps!

In general you don't need to do that since this behaviour is already tested in carrierwave specs itself.
You can test your uploaders in isolation, using Carrierwave test helpers. E.g. I would just write a spec like
attachment_uploader.extension_white_list.should =~ %w(doc docx xls xlsx ppt pptx pdf)
But if you insist on testing that I would suggest using FakeFS to stub filesystem and then check with
File.exists? document.attachment.current_path
whether attachment has been created.

Related

Carrierwave + Rails 5: read directory vs store directory

Carrierwave's enter link description here gives both the directory where Carrierwave uploads files and the directory where Carrierwave looks for files. However, I'd like to know if there's a way to define something like a read_dir, which would be the path where Carrierwave looked for files, and leave the store_dir only for storing files.
I know this probably doesn't make much practical sense, but I'd just like to know.
You can calculate store_dir based on some condition of the object. You can also set this condition when you need a different directory. For example:
class MyModel < ActiveRecord::Base
attr_accessible :use_directory_for_storing
mount_uploader :file, MyFileUploader
end
class MyFileUploader < CarrierWave::Uploader::Base
# ...
def store_dir
if model.use_directory_for_storing
"some/directory/for/storing"
else
"some/directory/for/reading"
end
end
end
# Usage
object = MyModel.new(params)
object.use_directory_for_storing = true
object.save # the file will be stored in ".../some/directory/for/storing/" directory
object = MyModel.last
# will look for the file in the ".../some/directory/for/reading/" directory
object.file.path # => ".../some/directory/for/reading/..."

Additional fields in CarrierWave Uploader

I am trying to add additional fields to the CarrierWave Uploader so that they are stored as part of the Uploader itself and together with the CarrierWave fields, such as #file, #model, #storage etc.
The fields are also version-specific, which is why I'd prefer to be able to access them via <my_model>.<my_uploader>.attribute and<my_model>.<my_uploader>.versions[:<the_version>] instead of additional columns in the model.
I did try the carrierwave-meta gem, but ran into an error with it ( NoMethodError: undefined method \'original_filename' for #<CarrierWave::Storage::Fog::File:0xab4134c> )
that seems to not have been fixed yet.
Any ideas or suggestions on how to best accomplish this?
I'm not 100% clear what you are trying to do.
when I use carrierwave gem, I do create a path that holds some of that information. In my applaications I normally have a file app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
def store_dir
# "uploads/image/file/187/"
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
...
end
from this I always know the model, what type of file, and the id.
All other info about this model I normally save in the database.
I hope this helps and sets you in the right direction
your error is connected with fog
In my Picture Uploader I can set an attribute reader and writer
class PictureUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def field
#field
end
def field=(field)
#field = field
end
# attr_accessor :field # for an even shorter way
end
The I open the rails console to test the model:
picture = PictureUploader.new
=> #<PictureUploader:0x0055804db336e8 #model=nil, #mounted_as=nil>
picture.field=('your text')
=> "your text"
picture.field
"your text"
About the versioning and error you are having 'NoMethodError: undefined method \'original_filename' for #<CarrierWave::Storage::Fog::File:0xab4134c>' I agree with MZaragoza
CarrierWave::Storage::Fog::File.new takes three parameters
def store!(file)
f = CarrierWave::Storage::Fog::File.new(uploader, self, uploader.store_path)
f.store(file)
f
end
uploader, self and uploader.store_path so to help us solve this problem you should include your CarrierwaveUploader model code and the output of uploader.store_path
Thanks a lot

Store two versions of a file at once using Carrierwave

I'm using Carrierwave and Fog gems to store a file to my Amazon S3 bucket (to /files/file_id.txt). I need to store a slightly different version of the file to a different location in the bucket (/files/file_id_processed.txt) at the same time (right after the original is stored). I don't want to create a separate uploader attribute for it on the model - is there any other way?
This my current method that stores the file:
def store_file(document)
file_name = "tmp/#{document.id}.txt"
File.open(file_name, 'w') do |f|
document_content = document.content
f.puts document_content
document.raw_export.store!(f)
document.save
end
# I need to store the document.processed_content
File.delete(file_name) if File.exist?(file_name)
end
This is the Document model:
class Document < ActiveRecord::Base
mount_uploader :raw_export, DocumentUploader
# here I want to avoid adding something like:
# mount_uploader :processed_export, DocumentUploader
end
This is my Uploader class:
class DocumentUploader < CarrierWave::Uploader::Base
storage :fog
def store_dir
"files/"
end
def extension_white_list
%w(txt)
end
end
This is how my final solution looks like (kinda) - based on Nitin Verma's answer:
I had to add a custom processor method for the version to the Uploader class:
# in document_uploader.rb
...
version :processed do
process :do_the_replacements
end
def do_the_replacements
original_content = #file.read
File.open(current_path, 'w') do |f|
f.puts original_content.gsub('Apples','Pears')
end
end
considering that you need similar file but with different name.
for this you need to create a version for file in uploader.
version :processed do
process
end
and now second file name will be processed_{origional_file}.extension. if you want to change file name of second file you can use this link https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Customize-your-version-file-names

how to save file to model from outside with carrierwave?

I have model:
class RealtyPhoto < ActiveRecord::Base
attr_accessible :name, :photo
mount_uploader :photo, RealtyPhotoUploader
belongs_to :realty_object
end
And uploader:
class RealtyPhotoUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def default_url
[version_name, "/assets/default-flat.jpg"].compact.join('_')
end
def extension_white_list
%w(jpg jpeg gif png)
end
end
I write in my code something like this:
require 'open-uri'
object = RealtyObject.last
photo = RealtyPhoto.new
photo.photo = open('http://milushov.ru/bg.jpg').read
object.realty_photos << photo
And i get an error on that line:
photo.photo = open('http://milushov.ru/bg.jpg').read
ArgumentError: string contains null byte
from /home/roma/.rvm/gems/ruby-1.9.3-p327#miel/gems/carrierwave-0.7.1/lib/carrierwave/sanitized_file.rb:113:in `expand_path'
Does anybody know how to download and save file from outside? (when i save file from form on the page - all ok). Maybe using CarrierWave Uploader?
Carrierwave upload image from remote url using remote_column_name_url in your case
photo.remote_photo_url = "url"
You seemed to have forgotten to close the file handle. Closing the file will likely make your application work correctly.
you could also do it in another way..
more efficient is
model[:name] = filename
model.save
surl = model.photo.url
FileUtils.copy(filesrc,surl,derefrence=true)
this approach basically saves you from copying the file to one location and ten copying it to another location when saved

How to prevent carrierwave from adding the filename string to the mounted column when file is not uploaded?

I am using Rails and ActiveRecord.
I have carrierwave mounted on one of the columns(:logo) of a model(Listing). My default filename is "disp_logo". Let's say I just do Listing.create! In this case, I haven't really uploaded any file. I did not do Listing.logo=<some file> or Listing.remote_logo_url=<some url>. But, carrierwave still inserts the string "disp_logo" in the :logo column. Why does it do that? How can I prevent carrierwave from doing so?
My uploader class has the following methods:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def filename
"disp_logo"
end
That's what I mean by default filename.
I want the column :logo to have NULL if image is not uploaded. Instead it has "disp_logo".
It would seem you created your migration with "disp_logo" as the default value for your column. You should set the default image in your uploader instead, like this:
# Provide a default URL as a default if there hasn't been a file uploaded:
def default_url
"/" + [version_name, "disp_logo.jpg"].compact.join('_')
end
Or if you use rails 3.1 and the assets pipeline:
# Include the Sprokets helpers for Rails 3.1+ asset pipeline compatibility:
include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
# Provide a default URL as a default if there hasn't been a file uploaded:
def default_url
asset_path [version_name, "disp_logo.jpg"].compact.join('_')
end
Edit:
Modify your filename method as follows:
def filename
"something.jpg" unless original_filename.nil?
end
I came up with a solution that fits my use-case better.
class LogoUploader < CarrierWave::Uploader::Base
attr_accessor :upload_failed
def filename
upload_failed ? "failed" : "disp_logo"
end
end
class SomeModel < ActiveRecord::Base
mount_uploader :logo, LogoUploader
def some_method_that_assigns_a_file_and_uploads_it
self.remote_logo_url = <some_url>
OR
self.logo = <some file>
<some_other_logic>
save!
rescue
logo.upload_failed = true
save!
end
end
This way, I know which records were processed:logo #=> "disp_logo", which records failed:logo #=> "failed" and which are yet to be processed:logo #=> nil

Resources