2010-04-01 8 views
75

Ich habe eine Schienenanwendung, die mehrere Modelle mit Büroklammerbefestigungen hat, die alle auf S3 hochgeladen werden. Diese App hat auch eine große Testsuite, die oft ausgeführt wird. Der Nachteil dabei ist, dass eine Tonne von Dateien bei jedem Testlauf auf unseren S3-Account hochgeladen wird, wodurch die Testsuite langsam läuft. Es verlangsamt auch die Entwicklung ein bisschen und erfordert, dass Sie eine Internetverbindung haben, um an dem Code zu arbeiten.Wie kann ich den Speichermechanismus von Paperclip basierend auf der aktuellen Rails-Umgebung einstellen?

Gibt es eine sinnvolle Möglichkeit, den Büroklammerspeichermechanismus basierend auf der Rails-Umgebung einzurichten? Idealerweise würden unsere Test- und Entwicklungsumgebungen den lokalen Dateisystemspeicher verwenden, und die Produktionsumgebung würde S3-Speicher verwenden.

Ich möchte auch diese Logik in ein gemeinsames Modul irgendeiner Art extrahieren, da wir mehrere Modelle haben, die dieses Verhalten benötigen. Ich möchte eine Lösung wie diese im Inneren jedes Modells vermeiden:

### We don't want to do this in our models... 
if Rails.env.production? 
    has_attached_file :image, :styles => {...}, 
        :path => "images/:uuid_partition/:uuid/:style.:extension", 
        :storage => :s3, 
        :url => ':s3_authenticated_url', # generates an expiring url 
        :s3_credentials => File.join(Rails.root, 'config', 's3.yml'), 
        :s3_permissions => 'private', 
        :s3_protocol => 'https' 
else 
    has_attached_file :image, :styles => {...}, 
        :storage => :filesystem 
        # Default :path and :url should be used for dev/test envs. 
end 

Update: Der klebrige Teil, dass die :path und :url Optionen der Befestigung müssen, ist abhängig davon, welche Speichersystem unterscheiden verwendet wird.

Alle Ratschläge oder Vorschläge würden sehr geschätzt werden! :-)

Antwort

27

Nachdem sie mit ihm spielen um für eine Weile kam ich mit einem Modul auf, das tut, was ich will.

Innen app/models/shared/attachment_helper.rb:

module Shared 
    module AttachmentHelper 

    def self.included(base) 
     base.extend ClassMethods 
    end 

    module ClassMethods 
     def has_attachment(name, options = {}) 

     # generates a string containing the singular model name and the pluralized attachment name. 
     # Examples: "user_avatars" or "asset_uploads" or "message_previews" 
     attachment_owner = self.table_name.singularize 
     attachment_folder = "#{attachment_owner}_#{name.to_s.pluralize}" 

     # we want to create a path for the upload that looks like: 
     # message_previews/00/11/22/001122deadbeef/thumbnail.png 
     attachment_path  = "#{attachment_folder}/:uuid_partition/:uuid/:style.:extension" 

     if Rails.env.production? 
      options[:path]   ||= attachment_path 
      options[:storage]   ||= :s3 
      options[:url]    ||= ':s3_authenticated_url' 
      options[:s3_credentials] ||= File.join(Rails.root, 'config', 's3.yml') 
      options[:s3_permissions] ||= 'private' 
      options[:s3_protocol]  ||= 'https' 
     else 
      # For local Dev/Test envs, use the default filesystem, but separate the environments 
      # into different folders, so you can delete test files without breaking dev files. 
      options[:path] ||= ":rails_root/public/system/attachments/#{Rails.env}/#{attachment_path}" 
      options[:url] ||= "/system/attachments/#{Rails.env}/#{attachment_path}" 
     end 

     # pass things off to paperclip. 
     has_attached_file name, options 
     end 
    end 
    end 
end 

(Anmerkung: Ich verwende einige benutzerdefinierte Büroklammer Einschübe oben, wie :uuid_partition, :uuid und :s3_authenticated_url.Sie müssen die Dinge ändern, wie für die jeweilige Anwendung erforderlich)

nun für jedes Modell, das Büroklammer Anhänge hat, man muss nur diese gemeinsame Modul umfassen, und rufen Sie die has_attachment Methode (statt Büroklammer des has_attached_file)

Ein Beispiel Modelldatei: app/models/user.rb:

class User < ActiveRecord::Base 
    include Shared::AttachmentHelper 
    has_attachment :avatar, :styles => { :thumbnail => "100x100>" } 
end 

in diesem Ort, werden Sie Dateien an den folgenden Orten gespeichert haben, abhängig von Ihrer Umgebung:

Entwicklung:

RAILS_ROOT + public/attachments/development/user_avatars/aa/bb/cc/aabbccddeeff/thumbnail.jpg

Test:

RAILS_ROOT + public/attachments/test/user_avatars/aa/bb/cc/aabbccddeeff/thumbnail.jpg

Produktion:

https://s3.amazonaws.com/your-bucket-name/user_avatars/aa/bb/cc/aabbccddeeff/thumbnail.jpg

Dies tut genau das, was ich suche, hoffentlich wird es auch für jemand anderen nützlich sein. :)

-Johannes

+0

Gute Arbeit. Ja, es war viel mehr Abstraktion nötig, als ich zur Verfügung gestellt habe! :) –

+0

Sehr gute Arbeit. Vielen Dank. Es hat mir wirklich geholfen. –

+0

Ich hatte Probleme mit der oben erwähnten Konstanten/Hash-Methode, aber das funktioniert großartig, und ich mag es, wie ich meine ganze Büroklammer-Logik an einem Ort halten kann. Vielen Dank! – neezer

2

Konnte man nicht einfach eine Umgebungsvariable in production/test/development.rb setzen?

PAPERCLIP_STORAGE_MECHANISM = :s3 

Dann:

has_attached_file :image, :styles => {...}, 
        :storage => PAPERCLIP_STORAGE_MECHANISM, 
        # ...etc... 
+1

Hey Barry, Das ist ein guter Vorschlag, aber die verschiedenen Optionen innerhalb der "... etc ..." verursachen Probleme. Ich habe festgestellt, dass die Optionen: path und: url unterschiedlich sein müssen, je nachdem, ob s3 oder: filesystem storage verwendet wird. Ich werde die Frage mit einem besseren Beispiel aktualisieren. Danke, --John –

78

ich Barrys Vorschlag besser gefällt und es gibt nichts, was man von Setzen der Variable auf einen Hash zu halten, die dann mit der Büroklammer Optionen zusammengefügt werden können.

In config/Umgebungen/development.rb und test.rb so etwas wie

PAPERCLIP_STORAGE_OPTIONS = {} 

Und in config/Umgebungen/production.rb

PAPERCLIP_STORAGE_OPTIONS = {:storage => :s3, 
           :s3_credentials => "#{Rails.root}/config/s3.yml", 
           :path => "/:style/:filename"} 

schließlich in Ihrem Büroklammer Modell gesetzt:

Update: Ein ähnlicher Ansatz wurde re Cently implemented in Paperclip für Rails 3.x apps. Umgebungsspezifische Einstellungen können jetzt mit config.paperclip_defaults = {:storage => :s3, ...} eingestellt werden.

33

Sie können globale Standardkonfigurationsdaten in den umgebungsspezifischen Konfigurationsdateien festlegen. Zum Beispiel in config/Umgebungen/production.rb:

Paperclip::Attachment.default_options.merge!({ 
    :storage => :s3, 
    :bucket => 'wheresmahbucket', 
    :s3_credentials => { 
    :access_key_id => ENV['S3_ACCESS_KEY_ID'], 
    :secret_access_key => ENV['S3_SECRET_ACCESS_KEY'] 
    } 
}) 
+0

Weniger Meta, expliziter, definitiv der Weg zu gehen. Dies könnte sogar in eine YAML-Datei mit einem Namespace pro Umgebung extrahiert werden. Danke @austinfromboston –

-4

Verwenden Sie das: RAILS_ENV Interpolation, wenn Sie die Anlage Pfad definieren:

has_attached_file :attachment, :path => ":rails_root/storage/:rails_env/attachments/:id/:style/:basename.:extension" 
5

Wie wäre es damit:

  1. Defaults sind eingerichtet in application.rb. Der Standardspeicher von: filesystem wird verwendet, aber die Konfiguration für s3 wird initialisiert
  2. Produktion.rb ermöglicht: s3 Speicherung und ändert den Standardpfad

application.rb

config.paperclip_defaults = 
{ 
    :hash_secret => "LongSecretString", 
    :s3_protocol => "https", 
    :s3_credentials => "#{Rails.root}/config/aws_config.yml", 
    :styles => { 
    :original => "1024x1024>", 
    :large => "600x600>", 
    :medium => "300x300>", 
    :thumb => "100x100>" 
    } 
} 

Development.rb (uncomment dies mit s3 im Entwicklungsmodus versuchen)

# config.paperclip_defaults.merge!({ 
# :storage => :s3, 
# :bucket => "mydevelopmentbucket", 
# :path => ":hash.:extension" 
# }) 

Production.rb :

config.paperclip_defaults.merge!({ 
    :storage => :s3, 
    :bucket => "myproductionbucket", 
    :path => ":hash.:extension" 
}) 

In Ihrem Modell:

has_attached_file :avatar 
0

Meine Lösung ist das gleiche mit @runesoerensen Antwort:

ich ein Modul PaperclipStorageOption in config/initializers/paperclip_storage_option.rb Der Code erstellen ist sehr einfach:

module PaperclipStorageOption 
    module ClassMethods 
    def options 
     Rails.env.production? ? production_options : default_options 
    end 

    private 

    def production_options 
     { 
     storage: :dropbox, 
     dropbox_credentials: Rails.root.join("config/dropbox.yml") 
     } 
    end 

    def default_options 
     {} 
    end 
    end 

    extend ClassMethods 
end 

und verwenden Sie es in unserem Modell has_attached_file :avatar, { :styles => { :medium => "1200x800>" } }.merge(PaperclipStorageOption.options)

Just it, hoffe diese Hilfe