8

我有一個模型,可以同時使用:Carrierwave用於存儲照片,PaperTrail用於版本控制。Papertrail和Carrierwave

我還配置Carrierwave店面diferent文件時更新(這是因爲我想版本的照片)與config.remove_previously_stored_files_after_update = false

的問題是,PaperTrail嘗試存儲從照片(CarrierWave上傳)整個Ruby的對象,而不是隻是一個字符串(這將是它的URL)

(版本表,列對象)

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

我怎樣才能解決這個存儲的照片版本一個簡單的字符串錫永?

回答

11

您可以在版本模型上覆蓋item_before_change,因此您不要直接調用上傳者accesor並使用write_attribute代替。或者,因爲你可能想這樣做的幾種模式,可以直接猴子貼劑的製造方法,是這樣的:如果

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 

不知道這是最好的解決辦法,但它似乎工作。

+0

恨你! xD,它的工作 – eveevans 2012-02-25 01:04:21

+1

這是一個遺憾,這不是PaperTrail的一部分 - 很好的修復 – John 2013-09-11 20:39:52

+0

我試着把這段代碼放在/config/initializers/papertrail.rb中,但它仍然添加完整的上傳對象。這是與Rails 4.1。 – 2014-05-07 15:01:29

5

添加@ beardedd的評論作爲答案,因爲我認爲這是處理問題的更好方法。

名稱數據庫列類似picture_filename,然後在模型中使用安裝上傳:

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

你仍然使用user.picture.url屬性來訪問您的模型,但是PaperTrail將存儲picture_filename正在進行修正。

+0

這非常有用,謝謝! – 2015-04-10 08:52:25

+0

這真的是一個好方法嗎?在更換上傳的文件時是否只會更改文件名,或者是否還會有很多元信息,如圖像寬度,高度等等?這隻會在跟蹤文件名時丟失,並且在恢復舊版本時信息不正確。 – 2015-04-10 14:48:13

+0

我做了一個快速測試:我比較了兩個完整載波對象(一個上傳文件爲'avatar.jpg',另一個用'nayeli.jpg')的轉儲。看起來確實只有時間戳和文件名是不同的,除了一行(#237)存儲了之前上傳文件的文件名('url.jpg',它是'avatar.jpg'之前的文件)。據我所知,你的解決方案應該基本上工作,而第237行在恢復修訂時會錯誤,因爲它沒有被正確地跟蹤。在這裏查看結果:https://github.com/jmuheim/base/commit/c5f93b261efa02ff70265ef7397dfd77aecb644e – 2015-04-11 07:49:00

3

這是來自@rabusmar的monkeypatch的一個更新版本,我使用它的rails 4.2.0和paper_trail 4.0.0.beta2,在/config/initializers/paper_trail.rb

如果對版本使用可選的object_changes列,則需要第二種方法替代。如果你在上傳器中覆蓋filename,舊值將來自雲,而新值來自本地文件名,但在我的情況下它沒問題,它對於載波+霧有點奇怪。

另外我還沒有檢查,如果它恢復舊版本時,它是否正常工作。

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

謝謝,多數民衆贊成:) – eveevans 2015-04-07 20:50:01

+0

這並不妨礙以前的文件被同名的新文件覆蓋。 – 2015-04-11 08:19:01

+0

恢復舊版本適用於我。謝謝! – 2015-04-11 21:44:39

0

我要添加到以前的答案如下:

它可以發生,你上傳不同的文件具有相同的名稱,這可能會覆蓋以前的文件,所以你將不能夠恢復舊的。

您可能use a timestamp in file namescreate random and unique filenames for all versioned files

更新

這似乎並沒有在所有的邊緣情況下的工作對我來說,一個請求請求內分配比單個文件更對同一個對象的時候。

我現在使用這一權利:

def filename 
    [@cache_id, original_filename].join('-') if original_filename.present? 
end 

這似乎是工作,作爲@cache_id爲每個生成和每一個重新上傳(這是不是這樣的,因爲它似乎在提供的思路上面的鏈接)。

0

@Sjors普羅沃斯特

我們還需要覆蓋pt_recordable_object方法PaperTrail ::型號:: InstanceMethods模塊

def pt_recordable_object 
    attr = attributes_before_change 
    object_attrs = object_attrs_for_paper_trail(attr) 

    hash = Hash[ 
     object_attrs.to_hash.map do |k, value| 
      [k, value.is_a?(CarrierWave::Uploader::Base) ? value.url && File.basename(value.url) : value ] 
     end 
    ] 

    if self.class.paper_trail_version_class.object_col_is_json? 
     hash 
    else 
     PaperTrail.serializer.dump(hash) 
    end 
    end 
1

這是實際的功能對我來說,把這個on config/initializers/paper_trail/.rb

module PaperTrail 
    module Reifier 
    class << self 
     def reify_attributes(model, version, attrs) 
     enums = model.class.respond_to?(:defined_enums) ? model.class.defined_enums : {} 
     AttributeSerializers::ObjectAttribute.new(model.class).deserialize(attrs) 
     attrs.each do |k, v| 

      is_enum_without_type_caster = ::ActiveRecord::VERSION::MAJOR < 5 && enums.key?(k) 

      if model.send("#{k}").is_a?(CarrierWave::Uploader::Base) 
      if v.present? 
       model.send("remote_#{k}_url=", v["#{k}"][:url]) 
       model.send("#{k}").recreate_versions! 
      else 
       model.send("remove_#{k}!") 
      end 
      else 
       if model.has_attribute?(k) && !is_enum_without_type_caster 
       model[k.to_sym] = v 
       elsif model.respond_to?("#{k}=") 
       model.send("#{k}=", v) 
       elsif version.logger 
       version.logger.warn(
        "Attribute #{k} does not exist on #{version.item_type} (Version id: #{version.id})." 
       ) 
       end 
      end 
     end 
     end 
    end 
    end 
end 

這將覆蓋物化方法對S3 +工作的Heroku

對於上傳者保持從更新或刪除記錄的舊文件爲此在上傳

configure do |config| 
    config.remove_previously_stored_files_after_update = false 
end 
def remove! 
    true 
end 

然後做了一些程序來從清除舊文件時間,好運

+0

'model.send(「#{k}」)。recreate_versions!'似乎沒有爲我恢復文件對象:( – 2017-03-24 13:14:50

+1

其實,我認爲它正在工作,我只需要調用保存在我所在的模型上測試一下,謝謝! – 2017-03-24 13:25:41