Как я могу настроить механизм хранения скрепок на основе текущей среды Rails?

У меня есть приложение rails, в котором есть несколько моделей с прикрепленными к ним скрепками, которые все загружены в S3. Это приложение также имеет большой набор тестов, который запускается довольно часто. Обратной стороной этого является то, что тонна файлов загружается в нашу учетную запись S3 при каждом запуске теста, из-за чего набор тестов работает медленно. Это также немного замедляет разработку и требует подключения к Интернету для работы над кодом.

Есть ли разумный способ настроить механизм хранения скрепок на основе среды Rails? В идеале наши среды тестирования и разработки должны использовать хранилище локальной файловой системы, а производственная среда - хранилище S3.

Я также хотел бы извлечь эту логику в какой-то общий модуль, поскольку у нас есть несколько моделей, которым потребуется такое поведение. Я бы хотел избежать такого решения внутри каждой модели:

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

Обновление. Важная деталь заключается в том, что параметры :path и :url вложения должны различаться в зависимости от того, какая система хранения используется.

Будем признательны за любые советы или предложения! :-)


person John Reilly    schedule 01.04.2010    source источник


Ответы (7)


Мне больше нравится предложение Барри, и ничто не мешает вам установить для переменной хэш, который затем можно объединить с параметрами скрепки.

В config / environment / development.rb и test.rb установите что-то вроде

PAPERCLIP_STORAGE_OPTIONS = {}

И в config / environment / production.rb

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

Наконец, в вашей модели скрепки:

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

Обновление: аналогичный подход недавно был реализован в Paperclip для Rails. 3.x приложений. Настройки для конкретной среды теперь можно установить с помощью config.paperclip_defaults = {:storage => :s3, ...}.

person runesoerensen    schedule 10.08.2010

Вы можете установить глобальные данные конфигурации по умолчанию в файлах конфигурации для конкретной среды. Например, в 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']
  }
})
person austinfromboston    schedule 11.06.2011
comment
Менее мета, более явный, определенно лучший способ. Его можно даже извлечь в файл YAML с пространством имен для каждой среды. Спасибо @austinfromboston - person Kenneth Kalmer; 15.05.2012

Поигравшись с ним некоторое время, я придумал модуль, который делает то, что я хочу.

Внутри 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

(Примечание: я использую некоторые пользовательские интерполяции скрепок выше, например :uuid_partition, :uuid и :s3_authenticated_url. Вам нужно будет изменить что-то по мере необходимости для вашего конкретного приложения)

Теперь для каждой модели, у которой есть скрепки, вам просто нужно включить этот общий модуль и вызвать метод has_attachment (вместо has_attached_file скрепки).

Пример файла модели: app/models/user.rb:

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

После этого у вас будут файлы, сохраненные в следующих местах, в зависимости от вашей среды:

Разработка:

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

Тест:

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

Производство:

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

Это именно то, что я ищу, надеюсь, это окажется полезным и для кого-то другого. :)

-Джон

person John Reilly    schedule 02.04.2010
comment
Хорошая работа. Да, требовалось гораздо больше абстракции, чем я предлагал! :) - person Barry Hess; 14.05.2010
comment
У меня были проблемы с упомянутым выше методом констант / хеширования, но он отлично работает, и мне нравится, как я могу хранить всю свою логику скрепки в одном месте. Спасибо! - person neezer; 24.11.2011

Как насчет этого:

  1. Значения по умолчанию устанавливаются в application.rb. Используется хранилище по умолчанию: filesystem, но инициализируется конфигурация для s3.
  2. Production.rb включает: хранилище s3 и изменяет путь по умолчанию

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 (раскомментируйте это, чтобы попробовать с s3 в режиме разработки)

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

Production.rb:

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

В вашей модели:

has_attached_file :avatar 
person John Naegle    schedule 22.06.2012

Не могли бы вы просто установить переменную окружения в production / test / development.rb?

PAPERCLIP_STORAGE_MECHANISM = :s3

Потом:

has_attached_file :image, :styles => {...},
                  :storage => PAPERCLIP_STORAGE_MECHANISM,
                  # ...etc...
person Barry Hess    schedule 01.04.2010
comment
Привет, Барри, это хорошее предложение, но различные параметры внутри ... и т.д ... вызывают проблемы. Я обнаружил, что параметры: path и: url должны различаться в зависимости от того, использует ли он: s3 или: хранилище файловой системы. Я дополню вопрос лучшим примером. Спасибо, - Джон - person John Reilly; 01.04.2010

Мое решение совпадает с ответом @runesoerensen:

Создаю модуль PaperclipStorageOption в config/initializers/paperclip_storage_option.rb Код очень простой:

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

и используем его в нашей модели has_attached_file :avatar, { :styles => { :medium => "1200x800>" } }.merge(PaperclipStorageOption.options)

Просто надеюсь, что это поможет

person duykhoa    schedule 17.02.2015

Используйте интерполяцию: rails_env при определении пути присоединения:

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