8

Ho un modello che utilizza entrambi: Carrierwave per le foto di archivio e PaperTrail per il controllo delle versioni.Papertrail e Carrierwave

Ho anche configurato Carrierwave per i file diferent negozio quando gli aggiornamenti (Ecco perché voglio versione le foto) con config.remove_previously_stored_files_after_update = false

Il problema è che PaperTrail tenta di memorizzare l'intera Rubino oggetto dalla foto (CarrierWave Uploader) invece di semplicemente una stringa (che sarebbe la sua url)

(tabella di versione, oggetto colonna)

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

Come posso risolvere questo problema per memorizzare una semplice stringa nella foto ver Sion?

risposta

11

È possibile sostituire il modello con versione item_before_change in modo da non chiamare direttamente l'utente del caricatore e utilizzare invece write_attribute. In alternativa, dal momento che si potrebbe desiderare di farlo per diversi modelli, è possibile scimmia-patch il metodo direttamente, in questo modo:

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 

Non so se è la soluzione migliore, ma sembra funzionare.

+0

ti odio! xD, Funziona – eveevans

+1

E 'un peccato questo non fa parte della PaperTrail - buona correzione – John

+0

Ho provato a mettere questo codice in /config/initializers/papertrail.rb, ma è ancora l'aggiunta dell'oggetto completo caricamento. Questo è con Rails 4.1. –

5

Aggiungendo il commento di @ beardedd come risposta perché penso che questo sia un modo migliore per gestire il problema.

Nome tua colonne del database qualcosa come picture_filename e poi nel modello montare l'uploader utilizzando:

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

È ancora utilizzare l'attributo user.picture.url per accedere al tuo modello, ma PaperTrail memorizzerà revisioni sotto picture_filename.

+0

Questo è molto utile, grazie! –

+0

È davvero un buon modo? Cambierà solo il nome del file quando si sostituisce un file caricato, o ci saranno anche molte meta informazioni come larghezza, altezza, ecc.? Questo andrebbe perso quando si traccia solo il nome del file, e quando si ripristina una versione precedente le informazioni sarebbero errate. –

+0

Ho fatto un test rapido: ho confrontato le discariche di due oggetti carrierwave completi (uno con un file 'avatar.jpg' caricato e uno con' nayeli.jpg'). Sembra che in realtà solo i timestamp e nomi di file sono diversi, ad eccezione di una linea (# 237) in cui il nome del file del file caricato in precedenza viene memorizzato ('url.jpg', che era il file prima di' avatar.jpg'). Per quanto posso vedere, la soluzione dovrebbe funzionare in pratica, mentre la riga 237 sarebbe errata quando si ripristinano le revisioni perché non è tracciata correttamente. Vedere i risultati qui: https://github.com/jmuheim/base/commit/c5f93b261efa02ff70265ef7397dfd77aecb644e –

3

Ecco una versione un po 'aggiornata di monkeypatch da @rabusmar, lo uso per i binari 4.2.0 e paper_trail 4.0.0.beta2, in /config/initializers/paper_trail.rb.

Il secondo metodo di sostituzione è necessario se si utilizza la colonna object_changes facoltativa per le versioni. Funziona in un modo un po 'strano per carrierwave + fog se si sostituisce filename in uploader, il vecchio valore sarà da cloud e quello nuovo da filename locale, ma nel mio caso è ok.

Inoltre non ho controllato se funziona correttamente quando si ripristina la vecchia versione.

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

Grazie, questo è cool :) – eveevans

+0

Ciò non impedisce un file precedente per essere sovrascritto con un nuovo file con lo stesso nome. –

+0

Ripristinare una vecchia versione funziona per me. Grazie! –

0

voglio aggiungere alle risposte precedenti il ​​seguente:

Può accadere che si caricano file diversi con lo stesso nome, e questo potrebbe sovrascrivere il file precedente, in modo da non essere in grado per ripristinare quello vecchio.

È possibile use a timestamp in file names o create random and unique filenames for all versioned files.

Aggiornamento

Questo non sembra funzionare in tutti i casi limite per me, quando si assegna più di un singolo file allo stesso oggetto all'interno di una singola richiesta richiesta.

sto usando questo adesso:

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

Questo sembra funzionare, come il @cache_id viene generato per ogni caricare nuovamente (che non è il caso come sembra per le idee fornite in i link sopra).

0

@Sjors Provoost

Abbiamo anche bisogno di sovrascrivere pt_recordable_object metodo in PaperTrail :: Modello :: InstanceMethods modulo

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

Questo è ciò che in realtà le funzioni per me, mettere questo su 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 

Questo ridefinisce il metodo reificare di lavorare su S3 + Heroku

Per uploader per mantenere i vecchi file da record aggiornati o cancellati farlo nel uploader

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

Poi fare un po 'di routine per cancellare i vecchi file da di volta in volta, buona fortuna

+0

'model.send (" # {k} "). Recreate_versions!' Non sembra ripristinare l'oggetto file per me :( –

+1

In realtà, penso che funzioni. Avevo solo bisogno di chiamare save sul modello in cui ero testarlo, grazie! –

Problemi correlati