views:

90

answers:

2

Hi guys!

I'm having a ton of trouble with uploading multiple attachments with paperclip and processing them with a watermark.

I have 2 models, Ad and Photo. The Ad has_many :photos and the Photo belongs_to :ad.

To be more exact in /models/ad.rb I have:

class Ad < ActiveRecord::Base

  has_many :photos, :dependent => :destroy
  accepts_nested_attributes_for :photos, :allow_destroy => true  
end

and my photo.rb file looks like this:

class Photo < ActiveRecord::Base

belongs_to :ad

has_attached_file :data,
:styles => {
  :thumb => "100x100#",
  :first => {
    :processors => [:watermark],
    :geometry => '300x250#',
    :watermark_path => ':rails_root/public/images/watermark.png',
    :position => 'SouthEast' },
  :large => {
    :processors => [:watermark],
    :geometry => '640x480#',
    :watermark_path => ':rails_root/public/images/watermark.png',
    :position => 'SouthEast' }
}
end

In my view I use this to add the file fields

<% f.fields_for :photos do |p| %>

  <%= p.label :data, 'Poza:' %> <%= p.file_field :data %>

<% end %>

In my controller, in the edit action i use 4.times {@ad.photos.build} to generate the file fields.

It all works fine and dandy if I don't use the watermark processor, if I use a normal has_attached_file declaration, like this:

has_attached_file :data,
:styles => {
  :thumb => "100x100#",
  :first => '300x250#',
  :large => '640x480#'
}

But when I use the watermark processor I always get this error:

 NoMethodError in PublicController#update_ad

 You have a nil object when you didn't expect it!
 You might have expected an instance of ActiveRecord::Base.
 The error occurred while evaluating nil.[]
  ..............................
 /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/nested_attributes.rb:350:in `assign_nested_attributes_for_collection_association'
 /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/nested_attributes.rb:345:in `each'
 /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/nested_attributes.rb:345:in `assign_nested_attributes_for_collection_association'
 /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/nested_attributes.rb:243:in `photos_attributes='
 /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2746:in `send'
 /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2746:in `attributes='
 /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2742:in `each'
 /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2742:in `attributes='
 /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2628:in `update_attributes'
 /home/alexg/Sites/vandmasina/app/controllers/public_controller.rb:217
 /home/alexg/Sites/vandmasina/app/controllers/public_controller.rb:216:in `update_ad'

The parameters are ok, as far as I can say

 Parameters:

 {"commit"=>"Salveaza modificarile",
  "ad"=>{"price"=>"6000",
  "oras"=>"9",
  "photos_attributes"=>{"0"=>{"data"=>#<File:/tmp/RackMultipart20100928-5130-b42noe-0>},
  "1"=>{"data"=>#<File:/tmp/RackMultipart20100928-5130-r0ukcr-0>},
  "2"=>{"data"=>#<File:/tmp/RackMultipart20100928-5130-mb23ei-0>},
  "3"=>{"data"=>#<File:/tmp/RackMultipart20100928-5130-1bpkm3b-0>}},

The Watermark processor /lib/paperclip_processors/watermark.rb looks like this:

  module Paperclip
class Watermark < Processor

class InstanceNotGiven < ArgumentError; 
end

def initialize(file, options = {},attachment = nil)
  super
  @file = file
  @current_format = File.extname(@file.path)
  @basename = File.basename(@file.path, @current_format)
  @watermark = ':rails_root/public/images/watermark.png'
  @current_geometry = Geometry.from_file file # This is pretty slow
  @watermark_geometry = watermark_dimensions
end

def watermark_dimensions
  return @watermark_dimensions if @watermark_dimensions
  @watermark_dimensions = Geometry.from_file @watermark
end

def make
  dst = Tempfile.new([@basename, @format].compact.join("."))
  watermark = " \\( #{@watermark} -extract #{@current_geometry.width.to_i}x#{@current_geometry.height.to_i}+#{@watermark_geometry.height.to_i /
                2}+#{@watermark_geometry.width.to_i / 2} \\) "
  command = "-gravity center " + watermark + File.expand_path(@file.path) + " " +File.expand_path(dst.path)

  begin
    success = Paperclip.run("composite", command.gsub(/\s+/, " "))
  rescue PaperclipCommandLineError
    raise PaperclipError, "There was an error processing the watermark for #{@basename}" if @whiny_thumbnails
  end
  dst
end

end
end

I have tried the processor in a normal app, without multiple attachments and it works perfect. It doesn't work with nested_attributes as far as I can tell.

The app is rails 2.3.5 with ruby 1.8.7 and paperclip 2.3.11

If you can provide any help it would be appreciated a lot, since I've been trying to figure this out for 2 days now :)

A: 

Just from a quick glance it looks like watermark_path should be "#{Rails.root}/..." though it looks like you have a lot going on here.

Also, I don't see your form as in form_for. Make sure you have {:multipart => true}

abdollar
If you mean the declaration in the model file, I'll give it a try, though it doesn't get to that point anyway.
v3rt1go
In watermark.rb at the @atermark path I used to have RAILS_ROOT+'/public/images/watermark.png' but that didn't change anything. As far as I know the :rails_root symbol works the same way
v3rt1go
Putting something in a quote is a literal string ':rails_root' is not a symbol - its a string
abdollar
Heh you are right. Missed the quotes there, just to freaking tired. Gonna change it back and tell you the results, though I don't think this is the reason.
v3rt1go
Tried it and as I said, it made no difference. It doesn't even get to the watermark path part. The problem is with the nested_attributes. Somehow the paperclip processor doesn't work well with them.
v3rt1go
check your form has multipart set
abdollar
It's there. If I don't use the processor part the photos get submitted and everything works. Problems start when I try to use the watermark processor.
v3rt1go
A: 

Oh, man, that was a tough one!

You have few errors in your code and none is related to nested models. My explanation is for Rails 3 & Paperclip 2.3.3

a) the :rails_root thing doesn't work. This interpolation is used only in url/path and not on custom options. So you should replace it with Rails.root.join("public/images...")

b) you simply ignore the :watermark_path option and you use only hardcoded path (in initialization method). So it doesn't matter what you have in your :styles as it always go for .../images/watermark.png. The :rails_root thingy there again so it cannot work.

c) when you pass a parameter to Paperclip.run("composite", "foo bar there") it actually executes this command:

composite 'foo bar there'

can you see the single quotes? Because of that the composite command see your parameters as one huge parameter and doesn't understand it at all. If you pass it as an array, then every item is enclosed in the quotes, not the array as a whole.

So here is the improved version of watermark processor:

module Paperclip
  class Watermark < Processor

  class InstanceNotGiven < ArgumentError;
  end

  def initialize(file, options = {},attachment = nil)
    super
    @file = file
    @current_format = File.extname(@file.path)
    @basename = File.basename(@file.path, @current_format)
    # PAWIEN: use default value only if option is not specified
    @watermark = options[:watermark_path] || Rails.root.join('public/images/watermark.png')
    @current_geometry = Geometry.from_file file # This is pretty slow
    @watermark_geometry = watermark_dimensions
  end

  def watermark_dimensions
    return @watermark_dimensions if @watermark_dimensions
    @watermark_dimensions = Geometry.from_file @watermark
  end

  def make
    dst = Tempfile.new([@basename, @format].compact.join("."))
    dst.binmode

    begin
      # PAWIEN: change original "stringy" approach to arrayish approach
      # inspired by the thumbnail processor
      options = [
        "-gravity",
        "center",
        "#{@watermark}",
        "-extract",
        "#{@current_geometry.width.to_i}x#{@current_geometry.height.to_i}+#{@watermark_geometry.height.to_i / 2}+#{@watermark_geometry.width.to_i / 2}",
        File.expand_path(@file.path),
        File.expand_path(dst.path)
      ].flatten.compact.join(" ").strip.squeeze(" ")

      success = Paperclip.run("composite", options)
    rescue PaperclipCommandLineError
      raise PaperclipError, "There was an error processing the watermark for #{@basename}" if @whiny_thumbnails
    end
    dst
  end

  end
end

Hope that helped you!

UPDATE: You need to use latest paperclip gem from github

gem 'paperclip', '>= 2.3.3', :git => "http://github.com/thoughtbot/paperclip.git"

In this version the formats of #run were changed once again so I've updated the code. It really should work as I've created test application and it's doing what supposed.

UPDATE 2: Repo with working example:

git://repo.or.cz/paperclip-mass-example.git
pawien
Thanks for the answer. It seems better now, but I still get an error.
v3rt1go
wrong number of arguments (8 for 3) in PublicController#update_adThe trace looks like this:paperclip_processors/watermark.rb:40: in `run`paperclip_processors/watermark.rb:40: in `make`controllers/public_controller.rb:217controllers/public_controller.rb:216:in `update_ad`My update action is the classical 2.3 one, no changes there.
v3rt1go
It seems that error is at the Paperclip.run(...) line. There were some changes lately and it depends on what version of paperclip you are using. Just open the file .../lib/paperclip/thumbnail.rb (from your gem so it will be somewhere in ~/.rvm) and take a look there. BTW I don't have problems to run it.
pawien
The history of this thing is pretty wild :-) http://github.com/thoughtbot/paperclip/commits/master/lib/paperclip/thumbnail.rb
pawien
arghhh!!! I'll take a look. Sometimes I miss good old buggy RMagick :)) Thanks a lot m8, I'll be back with some feedback
v3rt1go
Updating to paperclip 2.3.3 returned the original error `NoMethodError in PublicController#update_ad`This leads me to the idea that the changes made to watermark.rb updated the command to work with paperclip 2.3.3 but did not fix the initial error. Now the same thing happens, it loses the photo_attributes after processing.
v3rt1go
I'll try creating a rails 3 app with paperclip 2.3.3 multiple attachments and this watermark file. If it works I'll update the main app to rails 3. It should have been done ages ago anyway.
v3rt1go
a quick update - the problem is that you have to use head version from git, like: gem 'paperclip', '>= 2.3.3', :git => "http://github.com/thoughtbot/paperclip.git" I took a look into my project that is using the paperclip in mass assignment and that was the case (there is some bug that deletes options while accessing them)
pawien
ok, updated the answer, should work now...
pawien
Same error m8 on rails 2.3.5. I used the paperclip 2.3.3 gem but with no results. I'll have to update the app to rails 3.0 before trying anything else. Thanks for the huge effort, I'll be back with news once I upgrade
v3rt1go
I've prepared working example (Rails 3 only). Works like a charm... git clone http://repo.or.cz/r/paperclip-mass-example.git
pawien
Yup, works as advertised. The correct git url for you repo is `git://repo.or.cz/paperclip-mass-example.git` . I've done exactly the same thing with a new rails 3.0 app and it works perfectly. I'll just have to upgrade the app to 3.0 but it's such a huge app, I never seem to find the time to upgrade it :)Regarding the paperclip-mass-example, I was thinking of adding some extra features to it, like image cropping, dynamic field generation, perhaps some AJAX magic and put it on GitHub, if it's ok with you, that is :)
v3rt1go
The only watermarking app from GitHub is a really old one, 2009, and doesn't work with mass assignment, so people might actually need this.
v3rt1go
super! I hope your upgrade is going to be smooth. Of course you can use the example as you wish - I'm really happy that our effort will lead to something useful for everyone.
pawien