Skip to content

Instantly share code, notes, and snippets.

@develop7
Forked from kryzhovnik/static_missing_middleware.md
Last active December 18, 2015 22:59
Show Gist options
  • Save develop7/5858310 to your computer and use it in GitHub Desktop.
Save develop7/5858310 to your computer and use it in GitHub Desktop.

Когда мы хотим скопировать данные из production окружения Ruby on Rails приложения в development или staging, обычно, нам нужно скопировать дамп базы данных и статические файлы (например, изображения загруженные пользователями). Копирование базы может не представляет проблем (например, ее можно копировать из бэкапов или резервных серверов БД). А вот копирование статических файлов занимает много времени и ресурсов сервера с которого копируют (и на который копируются) файлы.

В рассылке ror2ru Макс Лапшин предложил копировать статические файлы с production в текущее окружение по запросу, обработку производить в middleware.

Ниже, моя реализация такой middleware.

Подключение.

Поместите следующий код в файл соответствующего окружения (например config/environments/development.rb).

    public_path = Rails.root.join('public')
    # регулярные выражения запросов, которые будут обрабатываться
    matching_paths = [/^\/uploads/, /^\/system/] # localhost:300/uploads/pic.jpg
    # сервер с которого будем пытаться подгрузить файлы, если они отсутствуют
    # на текущем сервере
    remote_host = 'http://production-example.com'
    config.middleware.insert_after ActionDispatch::Static,
      StaticMissing::Middleware, public_path, matching_paths, remote_host

Наше middleware подключается после ActionDispatch::Static - это middleware, которое отдает статические файлы в Ruby on Rails. Если ActionDispatch::Static не найдет файла соответствующего запросу в текущем окружении, то оно передает обработку запроса ниже по цепи middleware. Вот тут-то мы и должны вклиниться!

В нашей StaticMissing::Middleware мы проверим, есть ли файл соответствующий запросу на production-сервере. Если есть - то загрузим, сохраним в текущем окружении и передадим обработку запроса Rack::File middleware.

module StaticMissing
class Middleware
def initialize(app, public_path, matching_paths, remote_host)
@app = app
@root = public_path.to_s.chomp('/')
@file_server = ::Rack::File.new(@root)
@file_handler = FileHandler.new(@root, matching_paths, remote_host)
end
def call(env)
case env['REQUEST_METHOD']
when 'GET', 'HEAD'
path = env['PATH_INFO'].chomp('/')
if @file_handler.load_if_static_path(path)
env['PATH_INFO'] = path
return @file_server.call(env)
end
end
@app.call(env)
end
end
class FileHandler
def initialize(root, matching_paths, remote_host)
@root, @matching_paths, @remote_host = root, matching_paths, remote_host
end
def load_if_static_path(path)
static_path?(path) && load_file(path) == '200'
end
protected
def static_path?(path)
@matching_paths.any? do |matching_path|
path.match matching_path
end
end
def load_file(path)
uri = URI.join(@remote_host, path)
response = Net::HTTP.get_response(uri)
if response.code == '200'
full_path = File.join(@root, path)
FileUtils.mkdir_p File.dirname(full_path)
open(full_path, 'wb') do |file|
file.write response.body
end
Rails.logger.info("Static file downloaded from #{uri} to #{full_path}")
end
response.code
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment