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é! :-)
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
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, ...}
.
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']
}
})
Que dis-tu de ça:
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
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...
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