Ruby: Paperclip, S3, and Deep-cloning - ruby

What I have is a Theme model, which contains many Assets. Assets are using Paperclip and storing their file content in my Amazon AWS-S3 system. I'm also using deep_clone because my customers have the ability to copy built in Themes and then modify them to their hearts content. All the deep_clone stuff is working great, but when I deep_clone the assets, the old file contents don't get added to my S3 buckets. The record gets saved to the database, but since the file-contents don't get saved with the new ID the file.url property points to a dead file.
I've tried calling paperclip's save and create method manually but I can't figure out how to get paperclip to "push" the file back to the bucket since it now has a new ID, etc....
require 'open-uri'
class Asset < ActiveRecord::Base
belongs_to :theme
attr_accessor :old_id
has_attached_file :file,
:storage => "s3",
:s3_credentials => YAML.load_file("#{RAILS_ROOT}/config/aws.yml")[RAILS_ENV],
:bucket => "flavorpulse-" + RAILS_ENV,
:path => ":class/:id/:style.:extension"
validates_attachment_presence :file
validates_attachment_size :file, :less_than => 5.megabytes
before_save :delete_assets_in_same_theme_with_same_name
after_create :copy_from_cloned_asset
private
def delete_assets_in_same_theme_with_same_name
Asset.destroy_all({:theme_id => self.theme_id, :file_file_name => self.file_file_name})
end
def copy_from_cloned_asset
if (!old_id.blank?)
if (old_id > 0)
old_asset = Asset.find(old_id)
if (!old_asset.blank?)
self.file = do_download_remote_image(old_asset.file.url)
self.file.save
end
end
end
end
def do_download_remote_image (image_url)
io = open(URI.parse(image_url))
def io.original_filename; base_uri.path.split('/').last; end
io.original_filename.blank? ? nil : io
rescue # catch url errors with validations instead of exceptions (Errno::ENOENT, OpenURI::HTTPError, etc...)
end
end
Any ideas on how I can get paperclip to push the file? I also wouldn't be opposed to doing this using Amazon's aws-s3 gem but I couldn't seem to get that to work either.

According to this former question/answer, it should be possible with this simple line of code:
self.file = old_asset.file

Related

Carrierwave not resizing images on S3

On development, it's correctly creating a resized version of original image and storing both of them, but on s3, it's just uploading 2 identical versions without any kind of modification (apart from renaming it). There are no exceptions shown anywhere. Any idea where the problem might be?
CarrierWave.configure do |config|
config.fog_credentials = {
:provider => 'AWS',
:aws_access_key_id => 'a',
:aws_secret_access_key => 'a',
:region => "us-west-1"
}
config.fog_directory = 'a'
end
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
def store_dir
"images/#{model.id}"
end
version :normalized do
process :resize_to_limit => [450,450]
end
def extension_white_list
%w(jpg jpeg gif png)
end
if Sinatra::Base.development?
storage :file
else
storage :fog
end
end
Although there were no errors, running "convert -version" on production machine revealed that imagemagick was not installed. Installation fixed the issue.

Manually upload and save files in Carrierwave

I have a directory of existing files which I need to migrate into my rails app as part of a legacy migration. Essentially I need to upload these files manually and save a new record for them in the database. I haven't quite found the proper way to do this. Currently I have the following in a rake task:
#attachments.each do |attachment|
begin
new_attachment = Attachment.new
#attachment_file_path = "/home/username/Attachments/" + attachment.Filename
file = File.open(#attachment_file_path)
new_attachment[:file] = new_attachment.file.store!(file)
# Map old record fields to new
new_attachment.attributes = {
:project_id => attachment.ProjectID,
:name => attachment.Description,
:user_id => attachment.UserId,
:created_at => attachment.CreatedDate,
:updated_at => attachment.LastModifiedDate
}
new_attachment.save!
puts "Attachment added successfully "
rescue => error
puts "Error migrating Attachment: #{error}"
end
end
attachment.rb
class Attachment < ActiveRecord::Base
mount_uploader :file, FileUploader
end
uploader:
class FileUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
include CarrierWave::MimeTypes
process :set_content_type
storage :fog
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def extension_white_list
%w(jpg jpeg gif png pdf doc docx txt)
end
version :thumb do
process resize_to_fit: [152, nil]
end
def default_url
ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
end
protected
def image?(new_file)
if new_file.content_type == nil
return false
else
new_file.content_type.include? 'image'
end
end
end
This does not work currently. The file never gets uploaded, and occasionally I get the following error:
Failed to manipulate with rmagick, maybe it is not an image? Original Error: no decode delegate for this image format
In this instance, the file is a '.doc' file.
What is the correct way to open a local file and upload it manually via Carrierwave?
Any help is appreciated.
Try this
#attachments.each do |attachment|
begin
options = {
:project_id => attachment.ProjectID,
:name => attachment.Description,
:user_id => attachment.UserId,
:created_at => attachment.CreatedDate,
:updated_at => attachment.LastModifiedDate,
:file => File.new(File.join("/home/username/Attachments/",attachment.Filename))
}
new_attachment = Attachment.new(options)
new_attachment.save!
puts "Attachment added successfully "
rescue => error
puts "Error migrating Attachment: #{error}"
end
end
Perhaps that would do for you as carrierwave would internally call store! for you
Question?
Failed to manipulate with rmagick, maybe it is not an image? Original Error: no decode delegate for this image format
Not sure what are you trying to over here because you have define an image? method which is not specified in condition also is that something that you want the content_type to be only present for image file
if no perhaps only the process call would work
process :set_content_type
if yes then perhaps you have to do something like this
process :set_content_type , :if => :image?
def image?(new_file)
%w(jpg jpeg gif).include?(new_file.extension)
end
Hope this help
EDIT based upon the comment
try this just used the condition same logic
version :thumb ,:if => image? do
// your code
end

Add tags to images uploaded by carrierwave via cloudinary gem

In my rails app I'm trying to add tags to images uploaded to cloudinary cloud.
in my carrierwave, ImageUploader class
include Cloudinary::CarrierWave
# def cache_dir
# "#{Rails.root}/tmp/uploads"
# end
#
process :convert => 'jpg'
cloudinary_transformation :quality => 80
process :tags => [ 'tag', model.name]
...
I'm trying to add name of the record in tags, but it gives error
method 'model' is undefined for class ImageUploader.
how can I access value of the field name inside my uploader.
I'm new to rails.
please help, thanks in advance!
You can use the following:
class PictureUploader < CarrierWave::Uploader::Base
include Cloudinary::CarrierWave
process :convert => 'jpg'
cloudinary_transformation
:quality => 80
process :assign_tags
def assign_tags
return :tags => ['tag', model.name]
end
end
You can define any method that returns a hash of parameters. Then you can apply the custom method using the 'process' call. The parameters are passed to the upload API call.

Unable to render PDF to browser using Prawn PDF for Ruby on Rails

I am creating a pdf file in the latest version of the Prawn library (v1.0.1rc) in Rails (3.1.1) and when I run my code it generates the PDF into the root of the application.
I don't want this. I want it to render the output into user's browser window, without saving it locally to the server.
Please tell me how I can achieve this. Here are my files:
views/foo/show.pdf.erb:
<%=
require 'prawn'
pdf = Prawn::Document.new(:page_size => 'LETTER', :page_layout => :landscape, :margin => 50, :top_margin => 20, :bottom_margin => 50)
.....
render_file("foo.pdf")
%>
controllers/foo_controller:
class AuditsController < ApplicationController
before_filter :authenticate_user!
layout 'application'
can_edit_on_the_spot
respond_to :html, :xml, :js, :pdf
def index
#audits = Audit.all
respond_with #audits
end
def show
#audit = Audit.find(params[:id])
respond_with #audit do |format|
format.pdf { render :layour => false }
end
end
Gemfile
gem 'prawn'
/config/initializers/mime_types.rb
Mime::Type.register "application/pdf", :pdf
AuditsController
def show
#audit = Audit.find(params[:id])
respond_to do |format|
format.html
format.pdf do
pdf = Prawn::Document.new
pdf.text "This is an audit."
# Use whatever prawn methods you need on the pdf object to generate the PDF file right here.
send_data pdf.render, type: "application/pdf", disposition: "inline"
# send_data renders the pdf on the client side rather than saving it on the server filesystem.
# Inline disposition renders it in the browser rather than making it a file download.
end
end
end
I used to use the prawnto gem before Rails 3.1, but it doesn't work without a bit of hacking anymore. This is a much cleaner way to instantiate and display the PDF object in 3.1 by accessing Prawn directly.
I got this technique straight from one of Ryan Bates' Railscasts. Been using it ever since. You can view that specific episode here. He goes into much more detail about subclassing Prawn and moving the PDF generating code out of the controller. Also shows a lot of useful Prawn methods to get you started. Highly recommended.
A lot of the episodes are free, but that revised Prawn episode is one of those that are only available with a paid subscription. At $9/month though, a subscription quickly pays for itself.
I find the best way to send a pdf to the client's browser is to put the download into a link. Often you need to generate a pdf after form submission, but also need to redirect to another page.
You can't redirect and send the pdf simultaneously, but you can redirect and then provide a download link, like so:
First add gem 'prawn' to your gemfile. Bundle. Then do the following:
Link to your special printing action in your view
<%= link_to 'print ticket', print_ticket_path %>
route to special printing action in routes.rb
match 'print_ticket', to: 'tickets#print_ticket'
action that sends the outputted file (change per your needs):
def print_ticket
if session[:token]
#pdf = generate_pdf(session[:token])
send_data(#pdf, :filename => "output.pdf", :type => "application/pdf")
end
end
private
def generate_pdf(token)
Prawn::Document.new do
formatted_text [ { :text=>"xxx.org", :styles => [:bold], :size => 30 } ]
move_down 20
text "Please proceed to the following web address:"
move_down 20
text "http://xxx.org/finder"
move_down 20
text "and enter this code:"
move_down 20
formatted_text [ { :text=>token, :styles => [:bold], :size => 20 } ]
end.render
end

How to use Sinatra, Datamapper, DM-Paperclip and S3?

Update: I've switched to CarrierWave (finally got it to work), so although I still appreciate answers to this question I won't be able to try if they actually work since I've completely removed DM-Paperclip from my code.
Hi there,
I'm developing a Sinatra-webapp using DataMapper and are now looking to add some upload-functionality with S3 as storage. I've tried CarrierWave, but I couldn't get that to work so now I'm trying dm-paperclip. This is what I have right now:
Model:
class Article
include DataMapper::Resource
include Paperclip::Resource
property :id, Serial
property :created_at, DateTime
property :updated_at, DateTime
property :title, String
property :body, Text
has_attached_file :screenshot,
:storage => :s3,
:s3_credentials => {
:access_key_id => 'my-access-key-id',
:secret_access_key => 'my-secret_access-key',
:bucket => 'my-bucket'
},
:styles => {
:medium => "300x300>",
:thumb => "100x100>"
}
end
Controller:
post '/articles/create' do
#article = Article.new
#article.title = params[:title]
#article.body = params[:body]
#article.screenshot = params[:screenshot]
begin
#article.save
rescue DataMapper::SaveFailureError => e
puts "Error saving article: #{e.to_s} validation: #{#article.errors.values.join(', ')}"
rescue StandardError => e
puts "Got an error trying to save the article #{e.to_s}"
end
redirect '/articles'
end
Yet when I create a new article it doesn't save anything to my S3 bucket and I don't get any errors either.
Any ideas what I'm doing wrong?
Hey! Please try my fork: https://github.com/solnic/dm-paperclip it includes many patches which have fixed some issues with S3. Within a month or two I will be releasing it.
Apart from the solutions already posted, I would like to add a recomendation.
In my experience, using DataMapper's raise_on_save_failure feature is not of much help for debugging options. I recommend you disable that feature and use something like the following code:
if model.save then
return model
else
error = String.new
model.errors.each do |e|
error << "#{e[0]}\n"
end
raise ArgumentError, error
end
That way, you will get a full explanation of every issue DM encountered when trying to persist your model. I find it very useful not only for debugging but also for showing those messages to the consumers of my application.
Some time ago I did my fork especially for S3 in mind. My fork work with official AWS-SDK, instead of old aws-s3 which is mostly outdated.
If anybody will search for S3 solution for paperclip this is one that work (today)
https://github.com/krzak/dm-paperclip-s3
take a look at readme to get how to configure paperclip for S3

Resources