Как установить механизм хранения paperclip на основе текущей среды 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
должны различаться в зависимости от используемой системы хранения.
Любые советы или предложения будут очень благодарны!: -)
Ответы
Ответ 1
Поиграв с ним некоторое время, я придумал модуль, который делает то, что я хочу.
Внутри 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
(Примечание. Я использую некоторые пользовательские интерполяции paperclip выше, такие как :uuid_partition
, :uuid
и :s3_authenticated_url
. Вам нужно будет изменить все, что необходимо для вашего конкретного приложения)
Теперь, для каждой модели, в которой есть вложения для скрепки, вам просто нужно включить этот общий модуль и вызвать метод has_attachment
(вместо paperclip 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
Это именно то, что я ищу, надеюсь, это окажется полезным и для кого-то другого.:)
-Джон
Ответ 2
Мне нравится предложение Барри лучше, и ничто не мешает вам установить переменную в хэш, которая затем может быть объединена с параметрами скрепки.
В 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)
Обновление: Недавно был применен аналогичный подход реализованный в папке "Скрепка" для приложений Rails 3.x. Теперь настройки среды могут быть установлены с помощью config.paperclip_defaults = {:storage => :s3, ...}
.
Ответ 3
Вы можете установить глобальные данные конфигурации по умолчанию в конфигурационных файлах, специфичных для среды. Например, в 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']
}
})
Ответ 4
Как насчет этого:
- По умолчанию устанавливаются в application.rb. Используется хранилище по умолчанию: файловая система, но конфигурация для s3 инициализируется
- 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
Ответ 5
Не могли бы вы просто установить переменную среды в production/test/development.rb?
PAPERCLIP_STORAGE_MECHANISM = :s3
Тогда:
has_attached_file :image, :styles => {...},
:storage => PAPERCLIP_STORAGE_MECHANISM,
# ...etc...
Ответ 6
Мое решение такое же с ответом @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)
Только это, надеюсь, эта помощь
Ответ 7
Используйте интервал: rails_env, когда вы определяете путь вложения:
has_attached_file :attachment, :path => ":rails_root/storage/:rails_env/attachments/:id/:style/:basename.:extension"