Uploading to S3 on Heroku with Paperclip (delayed_job question) - ruby-on-rails

I'm trying to upload to a portfolio app I've built, specifically trying to find where to hook delayed_job into the process. It all works otherwise. Right now it returns undefined method 'call' for #<Class:0xae68750> on app/controllers/portfolio_items_controller.rb:18:in 'create' so here's my model and that portion of the controller... anyone see anything that could be going wrong? The hook I'm using now I got from this blog: http://madeofcode.com/posts/42-paperclip-s3-delayed-job-in-rails
/app/controllers/portfolio_items_controller.rb
def create
#portfolio_item = PortfolioItem.new(params[:portfolio_item])
if #portfolio_item.save
flash[:notice] = "Portfolio item created. As soon as files are uploaded Portfolio item will be made live."
redirect_to #portfolio_item
else
render :action => 'new'
end
end
/app/models/asset.rb
class Asset < ActiveRecord::Base
attr_accessible :image, :image_file_name, :image_content_type, :image_file_size, :portfolio_item_id, :order
belongs_to :portfolio_item
has_attached_file :image,
:styles => {
:thumb => "20x20#",
:small => "100x100",
:large => "600x600>"
},
:storage => :s3,
:s3_credentials => {
:access_key_id => ENV["S3_KEY"],
:secret_access_key => ENV["S3_SECRET"]
},
:bucket => ENV["S3_BUCKET"],
:path => "portfolio/:attachment/:id/:style/:basename.:extension"
before_source_post_process do |image|
if source_changed?
processing = true
false
end
end
after_save do |image|
if image.source_changed?
Delayed::Job.enqueue ImageJob.new(image.id)
end
end
def regenerate_styles!
self.source.reprocess!
self.processing = false
self.save(false)
end
def source_changed?
self.source_file_size_changed? ||
self.source_file_name_changed? ||
self.source_content_type_changed? ||
self.source_update_at_changed?
end
end
class ImageJob < Struct.new(:image_id)
def perform
Image.find(self.image_id).regenerate_styles!
end
end
Edit: thanks to kind people, it's not the missing .new anymore. But now it's that the before_source_post_process is not defined? And I can't find that method in anywhere but that blog post and this SO question. Is there something more appropriate?

The before_source_post_process won't work for you. It only works for:
has_attached_file :source
In your case it should be
before_image_post_process
Similarly, the source_changed? method should be:
def source_changed?
self.image_file_size_changed? ||
self.image_file_name_changed? ||
self.image_content_type_changed? ||
self.image_update_at_changed?
end

I think this:
#portfolio_item = PortfolioItem.(params[:portfolio_item])
should most likely be this:
#portfolio_item = PortfolioItem.new(params[:portfolio_item])

Related

How to access current_user object in model?

I am trying to write a method in my "team" model but current_user is showing this error
undefined local variable or method `current_user' for #
def set_default_url
if current_user.id == self.user_id
"/assets/default_black_:style_logo.jpg"
else
"/assets/default_:style_logo.jpg"
end
end
The method current_user is working fine for other models and controllers . I am calling this method like this .
has_attached_file :logo, :styles => {
:medium => "200x200#",
:thumb => "100x100#",
:small => "50x50#"
},
:default_url => :set_default_url
I am using rails 3.2 , ruby 1.9.3 and devise 3.1 . It seems to be simple task but I don't understand where the fault lies . I will be really thankful if someone helps me out here .
current_user is not available in any model, to access current_user in model do this
In your application controller
before_filter :set_current_user
def set_current_user
Team.current_user = current_user
end
in your Team model add this line
cattr_accessor :current_user
Congrats, now current_user is available in every model, to get the current user just use the following line everywhere
Team.current_user
NOTE: Restart the server after adding the lines mentioned above!
Now in your question you can use it like
def set_default_url
if Team.current_user.id == self.user_id
"/assets/default_black_:style_logo.jpg"
else
"/assets/default_:style_logo.jpg"
end
end
Hope this helps!
If you are using it only once than while calling this method pass current_user as argument like
has_attached_file :logo, :styles => {
:medium => "200x200#",
:thumb => "100x100#",
:small => "50x50#"
},
:default_url => :set_default_url(current_user)
and in model
def set_default_url(current_user)
if current_user.id == self.user_id
"/assets/default_black_:style_logo.jpg"
else
"/assets/default_:style_logo.jpg"
end
end
If you dont want above step then follow these
Go to User Model
def self.current_user
Thread.current[:user]
end
def self.current_user=(user)
Thread.current[:user] = user
end
Then go to application controller
before_filter :set_current_user
def set_current_user
User.current_user = current_user
end
Now we can easily fetch current_user in any model not only in Team
just give as User.current_user so in your code
def set_default_url
if User.current_user.id == self.user_id
"/assets/default_black_:style_logo.jpg"
else
"/assets/default_:style_logo.jpg"
end
end
So make use of it.
Hope it solves your issue in a good way. Free to use in any model
User.current_user to fetch the current user
User.current_user= to assign the current user.
Thanks

Ruby on Rails: REST API + file upload + paperclip

I'm trying to upload an attachment using REST API on my server through a PUT request. I can do this by putting the binary file in the request body but I'd also like to save this file as an attachment to a model which uses paperclip to save attachments.
Here's my current involved class definitions:
class Cl < ActiveRecord::Base
after_update :save_tses
validates_associated :tses
has_many :tses
...truncated...
def save_tses
tses.each do |ts|
ts.save(false)
end
end
end
class Ts < ActiveRecord::Base
has_attached_file :tsa, :styles => { :thumb => {:geometry => "100x141>", :format => :jpg} },
:path => ":rails_root/public/system/:attachment/:id/:style/:friendly_filename",
:url => "/system/:attachment/:id/:style/:friendly_filename"
belongs_to :cl
def friendly_filename
"#{self.tsa_file_name.gsub( /[^a-zA-Z0-9_\.]/, '_')}"
end
end
I can save the attachments just fine using the file upload on the html page. I'd like to do this on a controller that receives the file as binary data through a PUT request.
Any suggestions?
Also you can you use -
https://github.com/jwagener/httmultiparty
Got it,
# controller.rb
def add_ts
# params[:id]
# params[:tsa]
#cl = Cl.find(params[:id])
ts = #cl.tses.build(:name => "#{#cl.name}_#{Time.now.to_i}")
ts.tsa = params[:tsa]
if ts.save
render :json => {:status => "OK"}
else
render :json => {:status => "ERROR"}
end
end
# Test
curl -F "tsa=#file.pdf" "http://host/cl/474/add_ts"
=> {"status":"OK"}

Rubyzip problems on Windows

I'm using Rubyzip to unzip files that are uploaded by the user. The file contains a bunch of images, which are unpacked and put into a folder. This works fine on a mac, but on windows it won't unpack the zip file. Here's the model I use:
require "zip/zip"
class Photo < ActiveRecord::Base
validates_presence_of :image_file_name, :message => "Er is geen bestand bijgevoegd!"
belongs_to :album
has_attached_file :image, :styles => {
:original => ["1024x1024>", :jpg],
:medium => "300x250#",
:thumb => "150x100#"
}, :url => "/uploads/photos/:id/:style.:extension"
def zip?
image.content_type == "application/zip"
end
def save_photo
if zip?
extract_zip
true
else
self.save
end
end
def extract_zip
Zip::ZipFile.foreach(image.queued_for_write[:original].path) do |entry|
next if entry.name =~ /__MACOSX/ or entry.name =~ /\.DS_Store/ or !entry.file?
filename = entry.name
basename = File.basename(filename)
tempfile = Tempfile.new(basename)
tempfile.binmode
tempfile.write(entry.get_input_stream.read)
photo = Photo.create(:image => tempfile, :album_id => album_id)
end
end
end
Because it works fine on a mac I think it's the way Windows zips a file. maybe something to do with the header structure or something? Any help is very much appreciated!

Paperclip wrong attachment url on validation errors

I have a preview of attached image in my update form. The problem appears when user gets validation errors on attachment field. In this case image thumbnail url becomes as if an image was uploaded without any errors (it shows name of file that was not saved at server).
Here is how I get image url in my view:
<%= image_tag(#product.photo.url(:medium)) %>.
Controller:
def update
#product = Product.find(params[:id])
#product.update_attributes(params[:product]) ? redirect_to('/admin') : render(:new)
end
def edit
#product = Product.find(params[:id])
render :new
end
Model:
class Product < ActiveRecord::Base
<...>
##image_sizes = {:big => '500x500>', :medium => '200x200>', :thumb=> '100x100>'}
has_attached_file :photo, :styles => ##image_sizes, :whiny => false
validates_attachment_presence :photo
validates_attachment_content_type :photo, :content_type => ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'], :message => I18n.t(:invalid_image_type)
validates_attachment_size :photo, :less_than => 1.megabytes, :message => I18n.t(:invalid_image_size, :max => '1 Mb')
after_post_process :save_image_dimensions
<...>
end
UPD: The simpliest solution is to add #photo_file_url = #product.photo.url(:medium) in controller's update action before #product.update_attributes and <% #photo_file_url ||= #product.photo.url(:medium) %> in the view.
I actually had the same issue and here is what I had done (which seems a bit of a hack) but works and will show the default or previous image on update failure
after_validation :logo_reverted?
def logo_reverted?
unless self.errors[:logo_file_size].blank? or self.errors[:logo_content_type].blank?
self.logo.instance_write(:file_name, self.logo_file_name_was)
self.logo.instance_write(:file_size, self.logo_file_size_was)
self.logo.instance_write(:content_type, self.logo_content_type_was)
end
end
In my app I had similar issues, so I used:
<%= image_tag(#product.photo.url(:medium)) if #photo.valid? %>
That way nothing shows up if the Photo isn't valid (ie successfully created), but when you edit existing records the image is shown.
Hope that helps.

Rails 3 Paperclip Uploadify : Save Uploaded Object Attachments Before Saving Object

I am working with Rails 3 and Paperclip to attach uploaded files to several object types using a polymorphic association. I have created an Asset model and an inherited Image model (will be adding others, like Video and Documents later) as follows:
# app/models/asset.rb
class Asset < ActiveRecord::Base
# Nothing here yet
end
# app/models/image.rb
class Image < Asset
belongs_to :assetable, :polymorphic => true
has_attached_file :file, {
:styles => {
:small => { :geometry => '23x23#', :format => 'png' },
:medium => { :geometry => '100x100#', :format => 'png' } }
}.merge(PAPERCLIP_STORAGE_OPTIONS).merge(PAPERCLIP_STORAGE_OPTIONS_ASSET_IMAGE) # Variables sent in environments to direct uploads to filesystem storage in development.rb and S3 in production.rb
validates_attachment_presence :file
validates_attachment_size :file, :less_than => 5.megabytes
end
I then have another object type, Unit, which I am attaching multiple images to as follows:
# app/models/unit.rb
class Unit < ActiveRecord::Base
# ...
has_many :images, :as => :assetable, :dependent => :destroy
accepts_nested_attributes_for :images
end
# app/controllers/units_controller.rb
class UnitsController < ApplicationController
# ...
def new
#unit = current_user.units.new
# ...
#unit.images.build
end
def create
#unit = current_user.units.new(params[:unit])
# ...
respond_to do |format|
if #unit.save
format.html { redirect_to(#unit, :notice => 'Unit creation successful!') }
else
format.html { render :action => "new" }
end
end
end
def show
#unit = current_user.units.find(params[:id])
#unit_images = #unit.images
# ...
end
def edit
#unit = current_user.units.find(params[:id])
# ...
#unit.images.build
end
def update
#unit = current_user.units.find(params[:id], :readonly => false)
respond_to do |format|
if #unit.update_attributes(params[:unit])
format.html { redirect_to(#unit, :notice => 'Unit was successfully updated.') }
else
format.html { render :action => "edit" }
end
end
end
def destroy
#unit = current_user.units.find(params[:id])
#unit.destroy
respond_to do |format|
format.html { redirect_to(units_url) }
end
end
end
# app/views/units/_form.html.haml
.field # Display already uploaded images
= f.fields_for :images do |assets|
- unless assets.object.new_record?
= link_to(image_tag(assets.object.file.url(:medium)), assets.object.file.url(:original))
.field # Display field to add new image
= f.fields_for :images do |assets|
- if assets.object.new_record?
= assets.label :images, "Image File"
= assets.file_field :file, :class => 'uploadify'
Using these settings I am able to upload images one at at time, per display of the form.
The issues start when I try to integrate Uploadify to add multi file uploading/previewing. I have satisfied all the Uploadify dependancies, but in order to save the images associated with the Unit model I need to somehow include a reverence to the unit_id so that the polymorphic association can be made properly. Below is my current Uploadify code:
%script
$(document).ready(function() {
$('.uploadify').uploadify({
uploader : '/uploadify/uploadify.swf',
cancelImg : '/uploadify/cancel.png',
auto : true,
multi : true,
script : '#{units_path}',
scriptData : {
"#{key = Rails.application.config.session_options[:key]}" : "#{cookies[key]}",
"#{request_forgery_protection_token}" : "#{form_authenticity_token}",
}
});
});
So while I can easily upload with Paperclip along, Uploadify will not work. Any help would be much appreciated. Thank you in advance.
UPDATE:
After doing more research I ran across this comment to a similar issue: Rails3, S3, Paperclip Attachment as it's own model?. Any thoughts on whether or not that would work in this situation? Is there an easy way of determining the unit.id from the /new method and passing it to the Uploadify-created Asset?
We had solved a very similar problem once by saving the model right away when loading the form in a draft state (using state machine). Like this the model is available when you're trying to attach the files you're uploading and once you're submitting the rest of the form, you're basically just updating the model which changes it's state to e.g. published. It's a little work to update the controllers etc., but it did the trick.

Resources