mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Configure services that reference other services
* Move service configuration from the Engine to Service * Delegate configuration mechanics to internal Service::Configurator * Delegate service building to the concrete Service classes, allowing them to configure composed services. * Implement for the Mirror service.
This commit is contained in:
parent
6116313da4
commit
e5503399c0
10 changed files with 90 additions and 44 deletions
|
@ -25,31 +25,28 @@ module ActiveStorage
|
||||||
end
|
end
|
||||||
|
|
||||||
config.after_initialize do |app|
|
config.after_initialize do |app|
|
||||||
config_choice = app.config.active_storage.service
|
if config_choice = app.config.active_storage.service
|
||||||
config_file = Pathname.new(Rails.root.join("config/storage_services.yml"))
|
config_file = Pathname.new(Rails.root.join("config/storage_services.yml"))
|
||||||
|
|
||||||
if config_choice
|
|
||||||
raise("Couldn't find Active Storage configuration in #{config_file}") unless config_file.exist?
|
raise("Couldn't find Active Storage configuration in #{config_file}") unless config_file.exist?
|
||||||
|
|
||||||
begin
|
require "yaml"
|
||||||
require "yaml"
|
require "erb"
|
||||||
require "erb"
|
|
||||||
configs = YAML.load(ERB.new(config_file.read).result) || {}
|
|
||||||
|
|
||||||
if service_configuration = configs[config_choice.to_s].symbolize_keys
|
configs =
|
||||||
service_name = service_configuration.delete(:service)
|
begin
|
||||||
|
YAML.load(ERB.new(config_file.read).result) || {}
|
||||||
ActiveStorage::Blob.service = ActiveStorage::Service.configure(service_name, service_configuration)
|
rescue Psych::SyntaxError => e
|
||||||
else
|
raise "YAML syntax error occurred while parsing #{config_file}. " \
|
||||||
raise "Couldn't configure Active Storage as #{config_choice} was not found in #{config_file}"
|
"Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
|
||||||
|
"Error: #{e.message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveStorage::Blob.service =
|
||||||
|
begin
|
||||||
|
ActiveStorage::Service.configure config_choice, configs
|
||||||
|
rescue => e
|
||||||
|
raise e, "Cannot load `Rails.config.active_storage.service`:\n#{e.message}", e.backtrace
|
||||||
end
|
end
|
||||||
rescue Psych::SyntaxError => e
|
|
||||||
raise "YAML syntax error occurred while parsing #{config_file}. " \
|
|
||||||
"Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
|
|
||||||
"Error: #{e.message}"
|
|
||||||
rescue => e
|
|
||||||
raise e, "Cannot load `Rails.config.active_storage.service`:\n#{e.message}", e.backtrace
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,13 +32,15 @@
|
||||||
class ActiveStorage::Service
|
class ActiveStorage::Service
|
||||||
class ActiveStorage::IntegrityError < StandardError; end
|
class ActiveStorage::IntegrityError < StandardError; end
|
||||||
|
|
||||||
def self.configure(service, **options)
|
def self.configure(service_name, configurations)
|
||||||
begin
|
require 'active_storage/service/configurator'
|
||||||
require "active_storage/service/#{service.to_s.downcase}_service"
|
Configurator.new(service_name, configurations).build
|
||||||
ActiveStorage::Service.const_get(:"#{service}Service").new(**options)
|
end
|
||||||
rescue LoadError => e
|
|
||||||
puts "Couldn't configure service: #{service} (#{e.message})"
|
# Override in subclasses that stitch together multiple services and hence
|
||||||
end
|
# need to do additional lookups from configurations. See MirrorService.
|
||||||
|
def self.build(config, configurations) #:nodoc:
|
||||||
|
new(config)
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload(key, io, checksum: nil)
|
def upload(key, io, checksum: nil)
|
||||||
|
|
31
lib/active_storage/service/configurator.rb
Normal file
31
lib/active_storage/service/configurator.rb
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
class ActiveStorage::Service::Configurator #:nodoc:
|
||||||
|
def initialize(service_name, configurations)
|
||||||
|
@service_name, @configurations = service_name.to_sym, configurations.symbolize_keys
|
||||||
|
end
|
||||||
|
|
||||||
|
def build
|
||||||
|
service_class.build(service_config.except(:service), @configurations)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def service_class
|
||||||
|
resolve service_class_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def service_class_name
|
||||||
|
service_config.fetch :service do
|
||||||
|
raise "Missing Active Storage `service: …` configuration for #{service_config.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def service_config
|
||||||
|
@configurations.fetch @service_name do
|
||||||
|
raise "Missing configuration for the #{@service_name.inspect} Active Storage service. Configurations available for #{@configurations.keys.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve(service_class_name)
|
||||||
|
require "active_storage/service/#{service_class_name.to_s.downcase}_service"
|
||||||
|
ActiveStorage::Service.const_get(:"#{service_class_name}Service")
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,6 +5,17 @@ class ActiveStorage::Service::MirrorService < ActiveStorage::Service
|
||||||
|
|
||||||
delegate :download, :exist?, :url, to: :primary
|
delegate :download, :exist?, :url, to: :primary
|
||||||
|
|
||||||
|
# Stitch together from named configuration.
|
||||||
|
def self.build(mirror_config, all_configurations) #:nodoc:
|
||||||
|
primary = ActiveStorage::Service.configure(mirror_config.fetch(:primary), all_configurations)
|
||||||
|
|
||||||
|
mirrors = mirror_config.fetch(:mirrors).collect do |service_name|
|
||||||
|
ActiveStorage::Service.configure(service_name.to_sym, all_configurations)
|
||||||
|
end
|
||||||
|
|
||||||
|
new primary: primary, mirrors: mirrors
|
||||||
|
end
|
||||||
|
|
||||||
def initialize(primary:, mirrors:)
|
def initialize(primary:, mirrors:)
|
||||||
@primary, @mirrors = primary, mirrors
|
@primary, @mirrors = primary, mirrors
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,4 +24,4 @@ google:
|
||||||
mirror:
|
mirror:
|
||||||
service: Mirror
|
service: Mirror
|
||||||
primary: local
|
primary: local
|
||||||
secondaries: [ amazon, google ]
|
mirrors: [ amazon, google ]
|
||||||
|
|
|
@ -2,7 +2,7 @@ require "tmpdir"
|
||||||
require "service/shared_service_tests"
|
require "service/shared_service_tests"
|
||||||
|
|
||||||
class ActiveStorage::Service::DiskServiceTest < ActiveSupport::TestCase
|
class ActiveStorage::Service::DiskServiceTest < ActiveSupport::TestCase
|
||||||
SERVICE = ActiveStorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "active_storage"))
|
SERVICE = ActiveStorage::Service.configure(:test, test: { service: "Disk", root: File.join(Dir.tmpdir, "active_storage") })
|
||||||
|
|
||||||
include ActiveStorage::Service::SharedServiceTests
|
include ActiveStorage::Service::SharedServiceTests
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@ require "service/shared_service_tests"
|
||||||
|
|
||||||
if SERVICE_CONFIGURATIONS[:gcs]
|
if SERVICE_CONFIGURATIONS[:gcs]
|
||||||
class ActiveStorage::Service::GCSServiceTest < ActiveSupport::TestCase
|
class ActiveStorage::Service::GCSServiceTest < ActiveSupport::TestCase
|
||||||
SERVICE = ActiveStorage::Service.configure(:GCS, SERVICE_CONFIGURATIONS[:gcs])
|
SERVICE = ActiveStorage::Service.configure(:gcs, SERVICE_CONFIGURATIONS)
|
||||||
|
|
||||||
include ActiveStorage::Service::SharedServiceTests
|
include ActiveStorage::Service::SharedServiceTests
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,17 @@ require "tmpdir"
|
||||||
require "service/shared_service_tests"
|
require "service/shared_service_tests"
|
||||||
|
|
||||||
class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase
|
class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase
|
||||||
PRIMARY_DISK_SERVICE = ActiveStorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "active_storage"))
|
mirror_config = (1..3).map do |i|
|
||||||
MIRROR_SERVICES = (1..3).map do |i|
|
[ "mirror_#{i}",
|
||||||
ActiveStorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "active_storage_mirror_#{i}"))
|
service: "Disk",
|
||||||
end
|
root: File.join(Dir.tmpdir, "active_storage_mirror_#{i}") ]
|
||||||
|
end.to_h
|
||||||
|
|
||||||
SERVICE = ActiveStorage::Service.configure :Mirror, primary: PRIMARY_DISK_SERVICE, mirrors: MIRROR_SERVICES
|
config = mirror_config.merge \
|
||||||
|
mirror: { service: "Mirror", primary: 'primary', mirrors: mirror_config.keys },
|
||||||
|
primary: { service: "Disk", root: File.join(Dir.tmpdir, "active_storage") }
|
||||||
|
|
||||||
|
SERVICE = ActiveStorage::Service.configure :mirror, config
|
||||||
|
|
||||||
include ActiveStorage::Service::SharedServiceTests
|
include ActiveStorage::Service::SharedServiceTests
|
||||||
|
|
||||||
|
@ -16,8 +21,8 @@ class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase
|
||||||
data = "Something else entirely!"
|
data = "Something else entirely!"
|
||||||
key = upload(data, to: @service)
|
key = upload(data, to: @service)
|
||||||
|
|
||||||
assert_equal data, PRIMARY_DISK_SERVICE.download(key)
|
assert_equal data, SERVICE.primary.download(key)
|
||||||
MIRROR_SERVICES.each do |mirror|
|
SERVICE.mirrors.each do |mirror|
|
||||||
assert_equal data, mirror.download(key)
|
assert_equal data, mirror.download(key)
|
||||||
end
|
end
|
||||||
ensure
|
ensure
|
||||||
|
@ -27,22 +32,22 @@ class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
test "downloading from primary service" do
|
test "downloading from primary service" do
|
||||||
data = "Something else entirely!"
|
data = "Something else entirely!"
|
||||||
key = upload(data, to: PRIMARY_DISK_SERVICE)
|
key = upload(data, to: SERVICE.primary)
|
||||||
|
|
||||||
assert_equal data, @service.download(key)
|
assert_equal data, @service.download(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "deleting from all services" do
|
test "deleting from all services" do
|
||||||
@service.delete FIXTURE_KEY
|
@service.delete FIXTURE_KEY
|
||||||
assert_not PRIMARY_DISK_SERVICE.exist?(FIXTURE_KEY)
|
assert_not SERVICE.primary.exist?(FIXTURE_KEY)
|
||||||
MIRROR_SERVICES.each do |mirror|
|
SERVICE.mirrors.each do |mirror|
|
||||||
assert_not mirror.exist?(FIXTURE_KEY)
|
assert_not mirror.exist?(FIXTURE_KEY)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "URL generation in primary service" do
|
test "URL generation in primary service" do
|
||||||
travel_to Time.now do
|
travel_to Time.now do
|
||||||
assert_equal PRIMARY_DISK_SERVICE.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt"),
|
assert_equal SERVICE.primary.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt"),
|
||||||
@service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt")
|
@service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@ require "service/shared_service_tests"
|
||||||
|
|
||||||
if SERVICE_CONFIGURATIONS[:s3]
|
if SERVICE_CONFIGURATIONS[:s3]
|
||||||
class ActiveStorage::Service::S3ServiceTest < ActiveSupport::TestCase
|
class ActiveStorage::Service::S3ServiceTest < ActiveSupport::TestCase
|
||||||
SERVICE = ActiveStorage::Service.configure(:S3, SERVICE_CONFIGURATIONS[:s3])
|
SERVICE = ActiveStorage::Service.configure(:s3, SERVICE_CONFIGURATIONS)
|
||||||
|
|
||||||
include ActiveStorage::Service::SharedServiceTests
|
include ActiveStorage::Service::SharedServiceTests
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ require "byebug"
|
||||||
require "active_storage"
|
require "active_storage"
|
||||||
|
|
||||||
require "active_storage/service"
|
require "active_storage/service"
|
||||||
ActiveStorage::Blob.service = ActiveStorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "active_storage"))
|
ActiveStorage::Blob.service = ActiveStorage::Service.configure(:test, test: { service: 'Disk', root: File.join(Dir.tmpdir, "active_storage") })
|
||||||
|
|
||||||
require "active_storage/verified_key_with_expiration"
|
require "active_storage/verified_key_with_expiration"
|
||||||
ActiveStorage::VerifiedKeyWithExpiration.verifier = ActiveSupport::MessageVerifier.new("Testing")
|
ActiveStorage::VerifiedKeyWithExpiration.verifier = ActiveSupport::MessageVerifier.new("Testing")
|
||||||
|
|
Loading…
Reference in a new issue