Wie kann ich den Speichermechanismus der Büroklammer basierend auf der aktuellen Rails-Umgebung einstellen?

75

Ich habe eine Rails-Anwendung mit mehreren Modellen mit Büroklammeranhängen, die alle in S3 hochgeladen werden. Diese App hat auch eine große Testsuite, die ziemlich oft ausgeführt wird. Der Nachteil dabei ist, dass bei jedem Testlauf eine Menge Dateien auf unser S3-Konto hochgeladen werden, wodurch die Testsuite langsam ausgeführt wird. Es verlangsamt auch die Entwicklung ein wenig und erfordert eine Internetverbindung, um an dem Code arbeiten zu können.

Gibt es eine vernünftige Möglichkeit, den Büroklammer-Speichermechanismus basierend auf der Rails-Umgebung festzulegen? Im Idealfall verwenden unsere Test- und Entwicklungsumgebungen den lokalen Dateisystemspeicher und die Produktionsumgebung S3-Speicher.

Ich möchte diese Logik auch in ein gemeinsames Modul extrahieren, da wir mehrere Modelle haben, die dieses Verhalten benötigen. Ich möchte eine solche Lösung in jedem Modell 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 entscheidende Teil ist, dass die Anhänge :pathund :urlOptionen je nach verwendetem Speichersystem unterschiedlich sein müssen.

Jeder Rat oder Anregungen wäre sehr dankbar! :-)

John Reilly
quelle

Antworten:

78

Ich mag Barrys Vorschlag besser und nichts hindert Sie daran, die Variable auf einen Hash zu setzen, der dann mit den Büroklammeroptionen zusammengeführt werden kann.

In config / environment / development.rb und test.rb setzen Sie so etwas wie

PAPERCLIP_STORAGE_OPTIONS = {}

Und in config / environment / Production.rb

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

Endlich in Ihrem Büroklammermodell:

has_attached_file :image, {
    :styles => {:thumb => '50x50#', :original => '800x800>'}
}.merge(PAPERCLIP_STORAGE_OPTIONS)

Update: Ein ähnlicher Ansatz wurde kürzlich in Paperclip for Rails 3.x-Apps implementiert . Umgebungsspezifische Einstellungen können jetzt mit festgelegt werden config.paperclip_defaults = {:storage => :s3, ...}.

runesoerensen
quelle
32

Sie können globale Standardkonfigurationsdaten in den umgebungsspezifischen Konfigurationsdateien festlegen. Zum Beispiel in config / environment / 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']
  }
})
austinfromboston
quelle
Weniger Meta, expliziter, definitiv der richtige Weg. Dies könnte sogar in eine YAML-Datei mit einem Namespace pro Umgebung extrahiert werden. Danke @austinfromboston
Kenneth Kalmer
27

Nachdem ich eine Weile damit herumgespielt hatte, kam ich auf ein Modul, das macht, 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, :uuidund:s3_authenticated_url Sie werden die Dinge ändern müssen , wie für die jeweilige Anwendung benötigt wird .)

Jetzt müssen Sie für jedes Modell mit Büroklammeranhängen nur noch dieses freigegebene Modul einschließen und die has_attachmentMethode aufrufen (anstelle der Büroklammern has_attached_file).

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

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

Mit dieser Option werden Dateien abhängig von Ihrer Umgebung an den folgenden Speicherorten gespeichert:

Entwicklung:

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

Prüfung:

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 macht genau das, wonach ich suche, hoffentlich wird es sich auch für jemand anderen als nützlich erweisen. :) :)

-John

John Reilly
quelle
Gute Arbeit. Ja, es wurde viel mehr Abstraktion benötigt als ich bereitgestellt habe! :)
Barry Hess
Ich hatte Probleme mit der oben erwähnten Konstanten- / Hash-Methode, aber das funktioniert hervorragend, und ich mag es, wie ich meine gesamte Büroklammerlogik an einem Ort aufbewahren kann. Vielen Dank!
Neezer
5

Wie wäre es damit:

  1. Die Standardeinstellungen werden in application.rb festgelegt. Der Standardspeicher von: Dateisystem wird verwendet, aber die Konfiguration für s3 wird initialisiert
  2. Production.rb aktiviert: s3-Speicher 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 (kommentieren Sie dies aus, um es mit s3 im Entwicklungsmodus zu 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 
John Naegle
quelle
2

Könnten Sie nicht einfach eine Umgebungsvariable in Production / Test / Development.rb festlegen?

PAPERCLIP_STORAGE_MECHANISM = :s3

Dann:

has_attached_file :image, :styles => {...},
                  :storage => PAPERCLIP_STORAGE_MECHANISM,
                  # ...etc...
Barry Hess
quelle
1
Hey Barry, das ist ein guter Vorschlag, aber die verschiedenen Optionen im "... etc ..." verursachen Probleme. Ich habe festgestellt, dass die Optionen: path und: url unterschiedlich sein müssen, je nachdem, ob: s3- oder: Dateisystemspeicher verwendet wird. Ich werde die Frage mit einem besseren Beispiel aktualisieren. Vielen Dank,
John
0

Meine Lösung ist dieselbe mit der Antwort von @runesoerensen:

Ich erstelle ein Modul PaperclipStorageOptionin config/initializers/paperclip_storage_option.rb Der Code 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)

Nur es, hoffe diese Hilfe

duykhoa
quelle
-4

Verwenden Sie die Interpolation: rails_env, wenn Sie den Anhangspfad definieren:

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