Search code examples
ruby-on-railsruby-on-rails-3carrierwavepaper-trail-gem

Papertrail and Carrierwave


I have a model that use both: Carrierwave for store photos, and PaperTrail for versioning.

I also configured Carrierwave for store diferent files when updates (That's because I want to version the photos) with config.remove_previously_stored_files_after_update = false

The problem is that PaperTrail try to store the whole Ruby Object from the photo (CarrierWave Uploader) instead of simply a string (that would be its url)

(version table, column object)

---
first_name: Foo
last_name: Bar
photo: !ruby/object:PhotoUploader
  model: !ruby/object:Bla
    attributes:
      id: 2
      first_name: Foo1
      segundo_nombre: 'Bar1'
      ........

How can I fix this to store a simple string in the photo version?


Solution

  • You can override item_before_change on your versioned model so you don't call the uploader accesor directly and use write_attribute instead. Alternatively, since you might want to do that for several models, you can monkey-patch the method directly, like this:

    module PaperTrail
      module Model
        module InstanceMethods
          private
            def item_before_change
              previous = self.dup
              # `dup` clears timestamps so we add them back.
              all_timestamp_attributes.each do |column|
                previous[column] = send(column) if respond_to?(column) && !send(column).nil?
              end
              previous.tap do |prev|
                prev.id = id
                changed_attributes.each do |attr, before|
                  if defined?(CarrierWave::Uploader::Base) && before.is_a?(CarrierWave::Uploader::Base)
                    prev.send(:write_attribute, attr, before.url && File.basename(before.url))
                  else
                    prev[attr] = before
                  end
                end
              end
            end
        end
      end
    end
    

    Not sure if it's the best solution, but it seems to work.