2010-04-01 10 views
75

Tengo una aplicación de rieles que tiene varios modelos con archivos adjuntos de sujetapapeles que están cargados en S3. Esta aplicación también tiene un gran conjunto de pruebas que se ejecuta con bastante frecuencia. La desventaja de esto es que una tonelada de archivos se cargan en nuestra cuenta S3 en cada ejecución de prueba, lo que hace que el banco de pruebas se ejecute lentamente. También ralentiza el desarrollo un poco, y requiere que tengas una conexión a Internet para trabajar en el código.¿Cómo puedo configurar el mecanismo de almacenamiento del clip en función del entorno actual de Rails?

¿Existe alguna manera razonable de configurar el mecanismo de almacenamiento de clips basado en el entorno Rails? Idealmente, nuestros entornos de prueba y desarrollo utilizarían el almacenamiento del sistema de archivos local, y el entorno de producción usaría el almacenamiento S3.

También me gustaría extraer esta lógica en un módulo compartido de algún tipo, ya que tenemos varios modelos que necesitarán este comportamiento. Me gustaría evitar una solución de este tipo en el interior de cada modelo:

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

Actualización: La parte adhesiva es que :path y :url opciones de la fijación tienen que ser diferentes dependiendo de qué sistema de almacenamiento se está utilizando.

¡Cualquier consejo o sugerencia sería muy apreciada! :-)

Respuesta

27

Después de jugar un rato con él durante un tiempo, me ocurrió con un módulo que hace lo que yo quiero.

Dentro 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 

(Nota: Estoy usando algunas interpolaciones paperclip personalizado a continuación, al igual que :uuid_partition, :uuid y :s3_authenticated_url.Tendrá que modificar cosas como sea necesario para su aplicación particular)

Ahora, para cada modelo que tiene archivos adjuntos clip, sólo hay que incluir este módulo compartido, y llame al método (en lugar de de un clip has_attached_file)

has_attachment

Un ejemplo de archivo de modelo: app/models/user.rb:

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

con esto en su lugar, tendrá archivos guardados en los siguientes lugares, dependiendo de su entorno:

Desarrollo:

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

prueba:

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

Producción:

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

Esto hace exactamente lo que estoy buscando, con suerte también será útil para otras personas. :)

-John

+0

Buen trabajo. Sí, se necesitaba mucha más abstracción de la que proporcioné. :) –

+0

Muy buen trabajo. Gracias. Realmente me ayudó. –

+0

Estaba teniendo problemas con el método constante/hash mencionado anteriormente, pero esto funciona muy bien, y me gusta cómo puedo mantener toda mi lógica de clip en un solo lugar. ¡Gracias! – neezer

2

¿No podría simplemente establecer una variable de entorno en production/test/development.rb?

PAPERCLIP_STORAGE_MECHANISM = :s3 

continuación:

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

Hey Barry, Es una buena sugerencia, pero las diversas opciones dentro de "... etc ..." causan problemas. Descubrí que las opciones: ruta y: url deben diferir dependiendo de si se usa: s3 o: almacenamiento del sistema de archivos. Actualizaré la pregunta con un mejor ejemplo. Gracias, --John –

78

Me gusta la sugerencia de Barry mejor y no hay nada que le impide establecer la variable a un hash, que luego pueden ser unen a las opciones clip.

En config/ambientes/development.rb y test.rb establecer algo así como

PAPERCLIP_STORAGE_OPTIONS = {} 

Y en config/ambientes/production.rb

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

Por último, en su modelo de clip:

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

Actualización: Un enfoque similar fue re cently implemented in Paperclip para aplicaciones Rails 3.x. La configuración específica del entorno ahora se puede establecer con config.paperclip_defaults = {:storage => :s3, ...}.

33

Puede establecer datos de configuración globales por defecto en los archivos de configuración específicos del entorno. Por ejemplo, en config/ambientes/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

Menos meta, más explícito, definitivamente el camino a seguir. Esto incluso podría extraerse en un archivo YAML con un espacio de nombres por entorno. Gracias @austinfromboston –

-4

Utilice: interpolación RAILS_ENV al definir la ruta de adjuntos:

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

¿Qué tal esto:

  1. son predeterminados establecido en application.rb. Se usa el almacenamiento predeterminado de: sistema de archivos, pero la configuración para s3 se inicializa
  2. Producción.RB permite: almacenamiento S3 y cambia la ruta predeterminada

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 (descomentar esto para tratar con S3 en el modo de desarrollo)

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

production.rb :

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

En su modelo:

has_attached_file :avatar 
0

Mi solución es la misma que la respuesta @runesoerensen:

creo un módulo de PaperclipStorageOption en config/initializers/paperclip_storage_option.rb El código es muy 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 

y usarlo en nuestro modelo has_attached_file :avatar, { :styles => { :medium => "1200x800>" } }.merge(PaperclipStorageOption.options)

Solo esto, espero que esto ayude

Cuestiones relacionadas