Papertrail and Carrierwave

前端 未结 3 2137
情歌与酒
情歌与酒 2021-02-15 01:33

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

I also configured Carrierwave for store diferent files when upda

相关标签:
3条回答
  • 2021-02-15 02:00

    Here is a bit updated version of monkeypatch from @rabusmar, I use it for rails 4.2.0 and paper_trail 4.0.0.beta2, in /config/initializers/paper_trail.rb.

    The second method override is required if you use optional object_changes column for versions. It works in a bit strange way for carrierwave + fog if you override filename in uploader, old value will be from cloud and new one from local filename, but in my case it's ok.

    Also I have not checked if it works correctly when you restore old version.

    module PaperTrail
      module Model
        module InstanceMethods
          private
    
          # override to keep only basename for carrierwave attributes in object hash
          def item_before_change
            previous = self.dup
            # `dup` clears timestamps so we add them back.
            all_timestamp_attributes.each do |column|
              if self.class.column_names.include?(column.to_s) and not send("#{column}_was").nil?
                previous[column] = send("#{column}_was")
              end
            end
            enums = previous.respond_to?(:defined_enums) ? previous.defined_enums : {}
            previous.tap do |prev|
              prev.id = id # `dup` clears the `id` so we add that back
              changed_attributes.select { |k,v| self.class.column_names.include?(k) }.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
                  before = enums[attr][before] if enums[attr]
                  prev[attr] = before
                end
              end
            end
          end
    
          # override to keep only basename for carrierwave attributes in object_changes hash
          def changes_for_paper_trail
            _changes = changes.delete_if { |k,v| !notably_changed.include?(k) }
            if PaperTrail.serialized_attributes?
              self.class.serialize_attribute_changes(_changes)
            end
            if defined?(CarrierWave::Uploader::Base)
              Hash[
                  _changes.to_hash.map do |k, values|
                    [k, values.map { |value| value.is_a?(CarrierWave::Uploader::Base) ? value.url && File.basename(value.url) : value }]
                  end
              ]
            else
              _changes.to_hash
            end
          end
    
        end
      end
    end
    
    0 讨论(0)
  • 2021-02-15 02:12

    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.

    0 讨论(0)
  • 2021-02-15 02:14

    Adding @beardedd's comment as an answer because I think this is a better way to handle the problem.

    Name your database columns something like picture_filename and then in your model mount the uploader using:

    class User < ActiveRecord::Base has_paper_trail mount_uploader :picture, PictureUploader, mount_on: :picture_filename end

    You still use the user.picture.url attribute to access your model but PaperTrail will store revisions under picture_filename.

    0 讨论(0)
提交回复
热议问题