Cloudinary URLs break with Mongoid 3? - ruby-on-rails

I've using the Cloudinary gem, which up until now has worked fine as an embedded Photo model in Mongoid:
class PhotoUploader < CarrierWave::Uploader::Base
include Cloudinary::CarrierWave
def public_id
return model.id
end
end
class Photo
include Mongoid::Document
include Mongoid::Timestamps
embedded_in :place#, :inverse_of => :photos, :autosave => true
mount_uploader :image, PhotoUploader
#...
end
However, after upgrading to Mongoid 3, getting the image URL no longer works. Consider this Photo model:
1.9.3p194 :019 > p
=> #<Photo _id: 507bc3c82a450b14bd00e00a, _type: nil, created_at: 2012-10-15 08:05:28 UTC, updated_at: 2012-10-15 08:05:28 UTC, image_filename: nil, caption: nil, original_url: "http://www.reactionny.com//images/assets/101755_316529.JPG", image: "v1350288333/507bc3c82a450b14bd00e00a.jpg">
1.9.3p194 :020 > p.image
=> /assets/fallback/default.png
1.9.3p194 :021 > p.image_url
=> "/assets/fallback/default.png"
1.9.3p194 :022 > p['image']
=> "v1350288333/507bc3c82a450b14bd00e00a.jpg"
Why is it no longer returning the fully-qualified Cloudinary URL, in the form http://res.cloudinary.com/XXXXXX/image/upload/v1350288842/507bc5ca2a450b14bd00e896.jpg?

It seems that carrierwave-mongoid changed the name of the field in which the image is saved from yyy to yyy_filename.
In your model inspection printing you can see that image_filename is nil.
The integration with the Cloudinary GEM and CarrierWave seems to be working fine, but you need to migrate your model.
The relevant field used to be called 'image' and it's now called 'image_filename'. You should probably update your model to use the new field name and copy all values.
Another possible solution would be to specifically set the attribute name using mount_on:
mount_uploader :image, PhotoUploader, mount_on: :image

Related

Mongoid in rails not saving document

I'm using rails 4.2.5 with mongoid 5.1.0 on windows 10 in dev mode. I have created a model "Signup" but it won't save to MongoDB. I can see that rails connects to MongoDB but no transactions are performned.
What am I missing?
Model code:
class Signup
include Mongoid::Document
field :email, type: String
field :date, type: DateTime, default: ->{ Time.now }
end
Console tests:
irb(main):034:0> s = Signup.create
=> #<Signup _id: 57b9d0436fc5511c04a945ce, email: nil, date: 2016-08-21 16:01:07 UTC>
irb(main):035:0> Signup.count
=> 0
irb(main):036:0> s.save!
=> true
irb(main):037:0> Signup.count
=> 0
irb(main):038:0>
found the solution, I used a dot in the database name in mongoid.yml (project.io) which somehow hindered db creation. I removed it (project_io) and everything is working now as it should.

Issues with Globalize and CarrierWave uploads in Rails 4.2 app

I have issues with translated files in my Rails 4.2 app.
Background
Here are the gem versions I'm using:
gem "rails", "4.2.1"
gem "carrierwave" # 0.10.0
gem "globalize" # 5.0.1
And my model:
class Download < ActiveRecord::Base
belongs_to :download_type
has_and_belongs_to_many :products
translates :title, :part_number, :file
mount_uploader :file, DownloadFileUploader
validates :title, presence: true
def to_param
"#{id}-#{title.parameterize}"
end
end
The Issues
In my view, I want to list a Download and all of the current translations for that download, but all I get is the current locale data for each translation. In the Rails console:
> I18n.locale
=> :en
> download = Download.find(481)
=> #<Download id: 481, title: "SmartSensor HD Quick-reference Guide (User)", part_number: "WX-500-0171", download_type_id: 3, created_at: "2015-01-16 22:49:13", updated_at: "2015-04-20 16:59:25", file: "smartsensor_hd_user_quick-reference_guide-20150116...", download_updated_at: nil>
> download.translations.count
=> 8
> download.translated_locales
=> [:de, :en, :es, :fr, :it, :pt, :ru, :"zh-CN"]
> download.file.class
=> DownloadFileUploader
> download.file.url
=> "/uploads/download/file/481/smartsensor_hd_user_quick-reference_guide-20150116154913-en.pdf"
> download.title
=> "SmartSensor HD Quick-reference Guide (User)"
> download.part_number
=> "WX-500-0171"
And when the locale changes:
> I18n.locale = :de
=> :de
> download.file.class
=> DownloadFileUploader
> download.file.url
=> "/uploads/download/file/481/smartsensor_hd_user_quick-reference_guide-20150116154913-en.pdf"
> download.title
=> "SmartSensor HD Kurzanleitung"
> download.part_number
=> "WX-502-0006"
If I try and access the translation directly:
> I18n.locale = :de
=> :de
> download.translation.file.class
=> String
If I change how the uploader is mounted in my model:
Translation.mount_uploader :file, DownloadFileUploader
Existing translations list correctly—including the file, but I can no longer upload files. What gets stored in the database is this:
/uploads/download/translation/file/401/%23%3CActionDispatch%3A%3AHttp%3A%3AUploadedFile%3A0x007f9c12e6fe00%3E
Notice that it inserts /translation into the path, which I can fix in the uploader, but the filename isn't actually a file.
If I move translates :title, :part_number, :file below mount_uploader :file, DownloadFileUploader in my model, Globalize overrides the mounted uploader and that column is returned as class String when accessing it.
The Cry for Help
Help! 😮
I wrote this simple gem https://github.com/dalpo/carrierwave_globalize. which it should allow to use Globalize and Carrierwave together.
You have to extend your model with the CarrierwaveGlobalize module and use the mount_translated_uploader class method to mount your carrierwave uploader instead of the mount_uploader mehtod.
Follow the instructions in the readme for more info.
Old question, but... do not use Globalize with CarrierWave on the same attribute, they both make overrides for the default behavior.
You can do this in your model:
class Download < ActiveRecord::Base
belongs_to :download_type
has_and_belongs_to_many :products
translates :title, :part_number
mount_uploader :file, DownloadFileUploader
validates :title, presence: true
def to_param
"#{id}-#{title.parameterize}"
end
end
And then override file storing for your uploader in system to use I18n.locale:
def store_dir
"uploads/#{I18n.locale}/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end

how to prevent accepts_nested attributes_for from doing unnecessary validations in rails 2.3.5

I'm using accepts_nested_attributes_for in Rails 2.3.5 and find it does unnecessary validations on save.
In my application I have thousands of records so this is a problem.
To ilustrate this, I prepared a simple example.
class Language < ActiveRecord::Base
has_many :phrases
accepts_nested_attributes_for :phrases
end
class Phrase < ActiveRecord::Base
validates_presence_of :value
belongs_to :language
def before_validation
print "\nValidating: #{self.value}\n"
end
end
$ script/console
Loading development environment (Rails 2.3.5)
>> lang = Language.first
=> #<Language id: 1, name: "Italiano">
>> lang.phrases
=> [#<Phrase id: 7, value: "Buona notte", language_id: 1>,
#<Phrase id: 10, value: "Ciao", language_id: 1>,
#<Phrase id: 11, value: "Prego", language_id: 1>]
>> lang.phrases_attributes = [{:id => "7", :value => "Buon giorno"}]
=> [{:value=>"Buon giorno", :id=>"7"}]
>> lang.save
Validating: Buon giorno
Validating: Ciao
Validating: Prego
=> true
As this example shows it is validating not only the child that was added, but all other children.
Am I missing something?
How can I avoid doing all these validations?
As i indicated earlier, this is a real handicap for a large app.
After a suggestion I received from a Jason King of the SDRuby group I tried updating to 2.3.18.
That solved the problem

gem Paperclip interpolation

Good day.
I have rails 3.1. and gem Paperclip
in my application to manage companies contracts:
MODEL
model/contract.rb
has_many :contract_files
model/contract_file.rb
has_attached_file :data
CONSOLE
Loading development environment (Rails 3.1.0)
1.9.2p290 :001 > cont = Contract.first
Contract Load (0.1ms) SELECT "contracts".* FROM "contracts" LIMIT 1
=> #Contract id: 1, organization: "Com.org", and etc ....
1.9.2p290 :002 > cont.contract_files
ContractFile Load (0.2ms) SELECT "contract_files".* FROM "contract_files"
WHERE "contract_files"."contract_id" = 1
=> #[ContractFile id: 88, caption: "asdf", and etc ...]
QUESTION
Be kind, tell me please, how can I extract Contract id: 1 in
model/contract_files.rb
has_attached_file :data,
:url => "/assets/paperclip/:contract_id/:filename"
EXAMPLE WHAT I WANT TO GET
On http//localhost:3000/contracts/1 get such files pathes:
http//localhost:3000/contracts/1/assets/paperclip/1/XXX.pdf
http//localhost:3000/contracts/1/assets/paperclip/1/XXY.pdf
http//localhost:3000/contracts/1/assets/paperclip/1/XXZ.pdf
Thanks a lot for helping.
If I get it clear you might do it this way:
Contract.find( params[:id] ).contract_files.map { |cf| cf.data.url }
# Dont't forget to handle nonexistent id
Upd
To place contract_id in url you should recover default paperclip :url and :path parameters this way:
#model/contract_file.rb
has_attached_file :data, :path => "public/contracts/:parent_id/assets/paperclip/:id.:extension", :url => "/contracts/:parent_id/assets/paperclip/:id.:extension"
Paperclip.interpolates :parent_id do |a, s|
a.instance.contract.id
end

RESTful file uploads with CarrierWave

I'm trying to build an API backend for file uploads. I want to be able to upload files with a POST request that has a Base64-encoded string of the file. The server should decode the string, and save the file using CarrierWave. Here's what I have so far:
photo.rb:
class Photo
include Mongoid::Document
include Mongoid::Timestamps
mount_uploader :image_file, ImageUploader
end
image_uploader.rb:
class ImageUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
Rails console:
(summary)
ruby-1.8.7-p334 :001 > img = File.open("../image.png") {|i| i.read}
=> "\377���JFIF\000\001\002\001\000H\000H\000\000\377�Photoshop 3.0\0008BIM\003...
ruby-1.8.7-p334 :003 > encoded_img = Base64.encode64 img
=> 3af8A\nmLpplt5U8q+a7G2...
ruby-1.8.7-p334 :005 > p = Photo.new
=> #<Photo _id: 4e21b9a31d41c817b9000001, created_at: nil, updated_at: nil, _type: nil, user_id: nil, image_file_filename: nil>
ruby-1.8.7-p334 :006 > p.user_id = 1
=> 1
ruby-1.8.7-p334 :007 > p.image_file = Base64.decode64 encoded_img
\255��=\254\200�7u\226���\230�-zh�wT\253%����\036ʉs\232Is�M\215��˿6\247\256\177...
ruby-1.8.7-p334 :008 > p.save
=> true
ruby-1.8.7-p334 :009 > p.image_file.url
=> nil
full
The problem appears to be related to the process of converting a Base64-decoded string to a file. CarrierWave seems to expect a File object, and instead I'm giving it a String. So how do I convert that String to a File object. I'd like this conversion not to save anything to the file system, simply create the object and let CarrierWave do the rest.
CarrierWave also accepts a StringIO, but it expects a original_filename method, since it needs it for figuring out the file name and doing the extension check. How you do it changed between Rails 2 and 3, here's both methods:
Rails 2
io = StringIO.new(Base64.decode64(encoded_img))
io.original_filename = "foobar.png"
p.image_file = io
p.save
In Rails 3, you need to make a new class and then manually add original_filename back
class FilelessIO < StringIO
attr_accessor :original_filename
end
io = FilelessIO.new(Base64.decode64(encoded_img))
io.original_filename = "foobar.png"
p.image_file = io
p.save
You don't have to monkeypatch StringIO or put any of this in your model. You can override the cache!() method in your uploader definition. Or you can take it a step further and make a module for yourself to include. My file is a serialized string coming from a json document. The object passed in looks like this { :filename => 'something.jpg', :filedata => base64 string }.
Here is my module:
module CarrierWave
module Uploader
module BackboneLink
def cache!(new_file=sanitized_file)
#if new_file isn't what we expect just jump to super
if new_file.kind_of? Hash and new_file.has_key? :filedata
#this is from a browser, so it has all that 'data:..' junk to cut off.
content_type, encoding, string = new_file[:filedata].split(/[:;,]/)[1..3]
sanitized = CarrierWave::SanitizedFile.new(
:tempfile => StringIO.new(Base64.decode64(string)),
:filename => new_file[:filename],
:content_type => content_type
)
super sanitized
else
super
end
end
end
end
end
And then I can include it in an uploader.
uploaders/some_uploader.rb:
class SomeUploader < CarrierWave::Uploader::Base
include CarrierWave::Uploader::BackboneLink
class AppSpecificStringIO < StringIO
attr_accessor :filepath
def initialize(*args)
super(*args[1..-1])
#filepath = args[0]
end
def original_filename
File.basename(filepath)
end
end
also see the carrierwave wiki https://github.com/carrierwaveuploader/carrierwave/wiki/How-to%3A-Upload-from-a-string-in-Rails-3

Resources