Як я можу встановити механізм зберігання скріпки на основі поточного середовища Rails?


75

У мене є програма для рейок, яка має кілька моделей із кріпленнями для скріпок, які всі завантажені на 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параметри повинні відрізнятися залежно від того, яка система зберігання використовується.

Будь-яка порада чи пропозиція будуть вдячні! :-)

Відповіді:


78

Мені більше подобається пропозиція Баррі, і ніщо не заважає вам встановити для змінної хеш, який потім можна об’єднати з опціями скріпки.

У 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 for Rails 3.x. Тепер можна встановити налаштування для певного середовища config.paperclip_defaults = {:storage => :s3, ...}.


32

Ви можете встановити глобальні дані конфігурації за замовчуванням у файлах конфігурації, що стосуються конкретного середовища. Наприклад, у 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']
  }
})

Менше мета, більш явна, безумовно, шлях. Це можна навіть витягти у файл YAML із простором імен для кожного середовища. Спасибі @austinfromboston
Кеннет Калмер

27

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

Всередині 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

Це робить саме те, що я шукаю, сподіваюся, це виявиться корисним і для когось іншого. :)

-Джон


Хороша робота. Так, потрібно було набагато більше абстракції, ніж я надав! :)
Barry Hess

У мене були проблеми з методом константа / хеш, згаданим вище, але це чудово працює, і мені подобається, як я можу тримати всю свою логіку скріпки в одному місці. Дякую!
niezer

5

Як щодо цього:

  1. За замовчуванням встановлюються в application.rb. За замовчуванням використовується файлова система: файлова система, але конфігурація для 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 

2

Чи не могли б ви просто встановити змінну середовища у production / test / development.rb?

PAPERCLIP_STORAGE_MECHANISM = :s3

Тоді:

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

1
Ей, Баррі, це гарна пропозиція, але різні варіанти всередині "... і т.д. ..." викликають проблеми. Я виявив, що параметри: path та: url повинні відрізнятися залежно від того, чи використовується він: s3 або: сховище файлової системи. Я оновлю запитання на кращому прикладі. Дякую,
Джон

0

Моє рішення те саме, що відповідь @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)

Просто, сподіваюся, це допоможе


-4

Використовуйте інтерполяцію: rails_env, коли ви визначаєте шлях вкладення:

has_attached_file :attachment, :path => ":rails_root/storage/:rails_env/attachments/:id/:style/:basename.:extension"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.