aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.lock2
-rw-r--r--lib/active_storage/controllers/variants_controller.rb22
-rw-r--r--lib/active_storage/engine.rb59
-rw-r--r--lib/active_storage/routes.rb1
-rw-r--r--lib/active_storage/variant.rb52
-rw-r--r--lib/active_storage/verified_key_with_expiration.rb2
-rw-r--r--test/fixtures/files/racecar.jpgbin0 -> 1124062 bytes
-rw-r--r--test/test_helper.rb3
-rw-r--r--test/variation_test.rb16
10 files changed, 134 insertions, 24 deletions
diff --git a/Gemfile b/Gemfile
index a757a5c793..c4ecf50bbe 100644
--- a/Gemfile
+++ b/Gemfile
@@ -10,3 +10,4 @@ gem 'httparty'
gem 'aws-sdk', '~> 2', require: false
gem 'google-cloud-storage', require: false
+gem 'mini_magick'
diff --git a/Gemfile.lock b/Gemfile.lock
index 7e4c6f78f2..8d0c4c7937 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -99,6 +99,7 @@ GEM
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
+ mini_magick (4.8.0)
mini_portile2 (2.1.0)
minitest (5.10.2)
multi_json (1.12.1)
@@ -143,6 +144,7 @@ DEPENDENCIES
byebug
google-cloud-storage
httparty
+ mini_magick
rake
sqlite3
diff --git a/lib/active_storage/controllers/variants_controller.rb b/lib/active_storage/controllers/variants_controller.rb
new file mode 100644
index 0000000000..24cee16e80
--- /dev/null
+++ b/lib/active_storage/controllers/variants_controller.rb
@@ -0,0 +1,22 @@
+require "action_controller"
+require "active_storage/blob"
+
+class ActiveStorage::Controllers::VariantsController < ActionController::Base
+ def show
+ if blob_key = decode_verified_key
+ variant = ActiveStorage::Variant.lookup(blob_key: blob_key, variation_key: params[:variation_key])
+ redirect_to variant.url
+ else
+ head :not_found
+ end
+ end
+
+ private
+ def decode_verified_key
+ ActiveStorage::VerifiedKeyWithExpiration.decode(params[:encoded_key])
+ end
+
+ def disposition_param
+ params[:disposition].presence_in(%w( inline attachment )) || 'inline'
+ end
+end
diff --git a/lib/active_storage/engine.rb b/lib/active_storage/engine.rb
index c251f522c6..8918b179e0 100644
--- a/lib/active_storage/engine.rb
+++ b/lib/active_storage/engine.rb
@@ -33,29 +33,42 @@ module ActiveStorage
end
end
- config.after_initialize do |app|
- if config_choice = app.config.active_storage.service
- config_file = Pathname.new(Rails.root.join("config/storage_services.yml"))
- raise("Couldn't find Active Storage configuration in #{config_file}") unless config_file.exist?
-
- require "yaml"
- require "erb"
-
- configs =
- begin
- YAML.load(ERB.new(config_file.read).result) || {}
- 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}"
- 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
+ initializer "active_storage.verifiers" do
+ require "active_storage/verified_key_with_expiration"
+ require "active_storage/variant"
+
+ config.after_initialize do |app|
+ ActiveStorage::VerifiedKeyWithExpiration.verifier = \
+ ActiveStorage::Variant.verifier = \
+ Rails.application.message_verifier('ActiveStorage')
+ end
+ end
+
+ initializer "active_storage.services" do
+ config.after_initialize do |app|
+ if config_choice = app.config.active_storage.service
+ config_file = Pathname.new(Rails.root.join("config/storage_services.yml"))
+ raise("Couldn't find Active Storage configuration in #{config_file}") unless config_file.exist?
+
+ require "yaml"
+ require "erb"
+
+ configs =
+ begin
+ YAML.load(ERB.new(config_file.read).result) || {}
+ 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}"
+ 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
end
end
end
diff --git a/lib/active_storage/routes.rb b/lib/active_storage/routes.rb
index 748427a776..fade234ad3 100644
--- a/lib/active_storage/routes.rb
+++ b/lib/active_storage/routes.rb
@@ -1,2 +1,3 @@
get "/rails/active_storage/disk/:encoded_key/*filename" => "active_storage/disk#show", as: :rails_disk_blob
+get "/rails/active_storage/variants/:encoded_key/:encoded_transformation/*filename" => "active_storage/controllers/variants#show", as: :rails_blob_variant
post "/rails/active_storage/direct_uploads" => "active_storage/direct_uploads#create", as: :rails_direct_uploads
diff --git a/lib/active_storage/variant.rb b/lib/active_storage/variant.rb
new file mode 100644
index 0000000000..9b9dad43da
--- /dev/null
+++ b/lib/active_storage/variant.rb
@@ -0,0 +1,52 @@
+require "active_storage/blob"
+require "mini_magick"
+
+class ActiveStorage::Variant
+ class_attribute :verifier
+
+ attr_reader :blob, :variation
+ delegate :service, to: :blob
+
+ def self.lookup(blob_key:, variation_key:)
+ new ActiveStorage::Blob.find_by!(key: blob_key), variation: verifier.verify(variation_key)
+ end
+
+ def self.encode_key(variation)
+ verifier.generate(variation)
+ end
+
+ def initialize(blob, variation:)
+ @blob, @variation = blob, variation
+ end
+
+ def url(expires_in: 5.minutes, disposition: :inline)
+ perform unless exist?
+ service.url key, expires_in: expires_in, disposition: disposition, filename: blob.filename
+ end
+
+ def key
+ verifier.generate(variation)
+ end
+
+ private
+ def perform
+ upload_variant transform(download_blob)
+ end
+
+ def download_blob
+ service.download(blob.key)
+ end
+
+ def upload_variant(variation)
+ service.upload key, variation
+ end
+
+ def transform(io)
+ # FIXME: Actually do a variant based on the variation
+ File.open MiniMagick::Image.read(io).resize("500x500").path
+ end
+
+ def exist?
+ service.exist?(key)
+ end
+end
diff --git a/lib/active_storage/verified_key_with_expiration.rb b/lib/active_storage/verified_key_with_expiration.rb
index 8708106735..4a46483db5 100644
--- a/lib/active_storage/verified_key_with_expiration.rb
+++ b/lib/active_storage/verified_key_with_expiration.rb
@@ -1,5 +1,5 @@
class ActiveStorage::VerifiedKeyWithExpiration
- class_attribute :verifier, default: defined?(Rails) ? Rails.application.message_verifier('ActiveStorage') : nil
+ class_attribute :verifier
class << self
def encode(key, expires_in: nil)
diff --git a/test/fixtures/files/racecar.jpg b/test/fixtures/files/racecar.jpg
new file mode 100644
index 0000000000..934b4caa22
--- /dev/null
+++ b/test/fixtures/files/racecar.jpg
Binary files differ
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 03593b12c7..878ce8391c 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -24,6 +24,9 @@ ActiveStorage::Service.logger = ActiveSupport::Logger.new(STDOUT)
require "active_storage/verified_key_with_expiration"
ActiveStorage::VerifiedKeyWithExpiration.verifier = ActiveSupport::MessageVerifier.new("Testing")
+require "active_storage/variant"
+ActiveStorage::Variant.verifier = ActiveSupport::MessageVerifier.new("Testing")
+
class ActiveSupport::TestCase
private
def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain")
diff --git a/test/variation_test.rb b/test/variation_test.rb
new file mode 100644
index 0000000000..3b05095292
--- /dev/null
+++ b/test/variation_test.rb
@@ -0,0 +1,16 @@
+require "test_helper"
+require "database/setup"
+require "active_storage/variant"
+
+class ActiveStorage::VariationTest < ActiveSupport::TestCase
+ test "square variation" do
+ blob = ActiveStorage::Blob.create_after_upload! \
+ io: File.open(File.expand_path("../fixtures/files/racecar.jpg", __FILE__)), filename: "racecar.jpg", content_type: "image/jpeg"
+
+ variation_key = ActiveStorage::Variant.encode_key(resize: "500x500")
+
+ variant = ActiveStorage::Variant.lookup(blob_key: blob.key, variation_key: variation_key)
+
+ assert_match /racecar.jpg/, variant.url
+ end
+end