web-dev-qa-db-fra.com

Comment puis-je définir le mécanisme de stockage de Paperclip en fonction de l'environnement Rails actuel?)

J'ai une application Rails qui a plusieurs modèles avec des attachements Paperclip qui sont tous téléchargés sur S3. Cette application a également une grande suite de tests qui est exécutée assez souvent. L'inconvénient avec cela est qu'une tonne de fichiers sont téléchargés sur notre compte S3 à chaque exécution de test, ce qui ralentit la suite de tests. Cela ralentit également un peu le développement et vous oblige à disposer d'une connexion Internet pour travailler sur le code.

Existe-t-il un moyen raisonnable de définir le mécanisme de stockage Paperclip en fonction de l'environnement Rails? Idéalement, nos environnements de test et de développement utiliseraient le stockage du système de fichiers local et l'environnement de production utiliserait le stockage S3.

Je voudrais également extraire cette logique dans un module partagé d'une sorte, car nous avons plusieurs modèles qui auront besoin de ce comportement. Je voudrais éviter une solution comme celle-ci à l'intérieur de chaque modèle:

### 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

pdate: La partie collante est que la pièce jointe :path et :url les options doivent différer selon le système de stockage utilisé.

Tout conseil ou suggestion serait grandement apprécié! :-)

75
John Reilly

Après avoir joué avec pendant un certain temps, j'ai trouvé un module qui fait ce que je veux.

À l'intérieur 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

(Remarque: j'utilise des interpolations Paperclip personnalisées ci-dessus, comme :uuid_partition, :uuid et :s3_authenticated_url. Vous devrez modifier les choses selon vos besoins pour votre application particulière)

Désormais, pour chaque modèle doté de pièces jointes Paperclip, il vous suffit d'inclure ce module partagé et d'appeler le has_attachment méthode (au lieu de Paperclip has_attached_file)

Un exemple de fichier modèle: app/models/user.rb:

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

Avec cela en place, vous aurez des fichiers enregistrés aux emplacements suivants, selon votre environnement:

Développement:

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

Production:

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

Cela fait exactement ce que je recherche, j'espère que cela s'avérera utile pour quelqu'un d'autre aussi. :)

-John

27
John Reilly

J'aime mieux la suggestion de Barry et rien ne vous empêche de définir la variable sur un hachage, qui peut ensuite être fusionné avec les options Paperclip.

Dans config/environnements/development.rb et test.rb, définissez quelque chose comme

Paperclip_STORAGE_OPTIONS = {}

Et dans config/environnements/production.rb

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

Enfin dans votre modèle Paperclip:

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

Mise à jour: Une approche similaire a été récemment implémentée dans Paperclip pour Rails 3.x apps Les paramètres spécifiques à l'environnement peuvent désormais être définis avec config.Paperclip_defaults = {:storage => :s3, ...}.

78
runesoerensen

Vous pouvez définir des données de configuration par défaut globales dans les fichiers de configuration spécifiques à l'environnement. Par exemple, dans config/environnements/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']
  }
})
32
austinfromboston

Que dis-tu de ça:

  1. Les valeurs par défaut sont établies dans application.rb. Le stockage par défaut de: système de fichiers est utilisé, mais la configuration de s3 est initialisée
  2. Production.rb active: le stockage s3 et modifie le chemin par défaut

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 (décommentez ceci pour essayer avec s3 en mode développement)

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

Production.rb:

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

Dans votre modèle:

has_attached_file :avatar 
5
John Naegle

Ne pourriez-vous pas simplement définir une variable d'environnement dans production/test/development.rb?

Paperclip_STORAGE_MECHANISM = :s3

Alors:

has_attached_file :image, :styles => {...},
                  :storage => Paperclip_STORAGE_MECHANISM,
                  # ...etc...
2
Barry Hess

Ma solution est la même avec la réponse @runesoerensen:

Je crée un module PaperclipStorageOption dans config/initializers/Paperclip_storage_option.rb Le code est très simple:

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

et l'utiliser dans notre modèle has_attached_file :avatar, { :styles => { :medium => "1200x800>" } }.merge(PaperclipStorageOption.options)

Just it, espérons que cette aide

0
duykhoa