diff options
Diffstat (limited to 'activestorage/lib')
12 files changed, 72 insertions, 56 deletions
diff --git a/activestorage/lib/active_storage.rb b/activestorage/lib/active_storage.rb index d1ff6b7032..e1bd974853 100644 --- a/activestorage/lib/active_storage.rb +++ b/activestorage/lib/active_storage.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true #-- -# Copyright (c) 2017 David Heinemeier Hansson +# Copyright (c) 2017-2018 David Heinemeier Hansson, Basecamp # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -26,7 +26,11 @@ require "active_record" require "active_support" require "active_support/rails" + require "active_storage/version" +require "active_storage/errors" + +require "marcel" module ActiveStorage extend ActiveSupport::Autoload @@ -41,4 +45,7 @@ module ActiveStorage mattr_accessor :queue mattr_accessor :previewers, default: [] mattr_accessor :analyzers, default: [] + mattr_accessor :paths, default: {} + mattr_accessor :variable_content_types, default: [] + mattr_accessor :content_types_to_serve_as_binary, default: [] end diff --git a/activestorage/lib/active_storage/analyzer.rb b/activestorage/lib/active_storage/analyzer.rb index 837785a12b..7c4168c1a0 100644 --- a/activestorage/lib/active_storage/analyzer.rb +++ b/activestorage/lib/active_storage/analyzer.rb @@ -26,7 +26,7 @@ module ActiveStorage end private - def logger + def logger #:doc: ActiveStorage.logger end end diff --git a/activestorage/lib/active_storage/analyzer/image_analyzer.rb b/activestorage/lib/active_storage/analyzer/image_analyzer.rb index 25e0251e6e..5231168a7c 100644 --- a/activestorage/lib/active_storage/analyzer/image_analyzer.rb +++ b/activestorage/lib/active_storage/analyzer/image_analyzer.rb @@ -9,8 +9,7 @@ module ActiveStorage # # => { width: 4104, height: 2736 } # # This analyzer relies on the third-party {MiniMagick}[https://github.com/minimagick/minimagick] gem. MiniMagick requires - # the {ImageMagick}[http://www.imagemagick.org] system library. These libraries are not provided by Rails; you must - # install them yourself to use this analyzer. + # the {ImageMagick}[http://www.imagemagick.org] system library. class Analyzer::ImageAnalyzer < Analyzer def self.accept?(blob) blob.image? diff --git a/activestorage/lib/active_storage/analyzer/video_analyzer.rb b/activestorage/lib/active_storage/analyzer/video_analyzer.rb index 1c144baa37..aa532da201 100644 --- a/activestorage/lib/active_storage/analyzer/video_analyzer.rb +++ b/activestorage/lib/active_storage/analyzer/video_analyzer.rb @@ -16,11 +16,10 @@ module ActiveStorage # ActiveStorage::VideoAnalyzer.new(blob).metadata # # => { width: 640, height: 480, duration: 5.0, angle: 0, aspect_ratio: [4, 3] } # - # This analyzer requires the {ffmpeg}[https://www.ffmpeg.org] system library, which is not provided by Rails. You must - # install ffmpeg yourself to use this analyzer. + # When a video's angle is 90 or 270 degrees, its width and height are automatically swapped for convenience. + # + # This analyzer requires the {ffmpeg}[https://www.ffmpeg.org] system library, which is not provided by Rails. class Analyzer::VideoAnalyzer < Analyzer - class_attribute :ffprobe_path, default: "ffprobe" - def self.accept?(blob) blob.video? end @@ -89,5 +88,9 @@ module ActiveStorage logger.info "Skipping video analysis because ffmpeg isn't installed" {} end + + def ffprobe_path + ActiveStorage.paths[:ffprobe] || "ffprobe" + end end end diff --git a/activestorage/lib/active_storage/attached/macros.rb b/activestorage/lib/active_storage/attached/macros.rb index 2b38a9b887..c51efa9d6b 100644 --- a/activestorage/lib/active_storage/attached/macros.rb +++ b/activestorage/lib/active_storage/attached/macros.rb @@ -38,13 +38,13 @@ module ActiveStorage end CODE - has_one :"#{name}_attachment", -> { where(name: name) }, class_name: "ActiveStorage::Attachment", as: :record + has_one :"#{name}_attachment", -> { where(name: name) }, class_name: "ActiveStorage::Attachment", as: :record, inverse_of: :record has_one :"#{name}_blob", through: :"#{name}_attachment", class_name: "ActiveStorage::Blob", source: :blob scope :"with_attached_#{name}", -> { includes("#{name}_attachment": :blob) } if dependent == :purge_later - before_destroy { public_send(name).purge_later } + after_destroy_commit { public_send(name).purge_later } end end @@ -83,13 +83,13 @@ module ActiveStorage end CODE - has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment" + has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment", inverse_of: :record has_many :"#{name}_blobs", through: :"#{name}_attachments", class_name: "ActiveStorage::Blob", source: :blob scope :"with_attached_#{name}", -> { includes("#{name}_attachments": :blob) } if dependent == :purge_later - before_destroy { public_send(name).purge_later } + after_destroy_commit { public_send(name).purge_later } end end end diff --git a/activestorage/lib/active_storage/downloading.rb b/activestorage/lib/active_storage/downloading.rb index 3dac6b116a..a57fda49b4 100644 --- a/activestorage/lib/active_storage/downloading.rb +++ b/activestorage/lib/active_storage/downloading.rb @@ -5,7 +5,7 @@ module ActiveStorage private # Opens a new tempfile in #tempdir and copies blob data into it. Yields the tempfile. def download_blob_to_tempfile # :doc: - Tempfile.open("ActiveStorage", tempdir) do |file| + Tempfile.open([ "ActiveStorage", blob.filename.extension_with_delimiter ], tempdir) do |file| download_blob_to file yield file end diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb index b870e6d4d6..8ba32490b1 100644 --- a/activestorage/lib/active_storage/engine.rb +++ b/activestorage/lib/active_storage/engine.rb @@ -18,6 +18,18 @@ module ActiveStorage config.active_storage.analyzers = [ ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer ] config.active_storage.paths = ActiveSupport::OrderedOptions.new + config.active_storage.variable_content_types = %w( image/png image/gif image/jpg image/jpeg image/vnd.adobe.photoshop ) + config.active_storage.content_types_to_serve_as_binary = %w( + text/html + text/javascript + image/svg+xml + application/postscript + application/x-shockwave-flash + text/xml + application/xml + application/xhtml+xml + ) + config.eager_load_namespaces << ActiveStorage initializer "active_storage.configs" do @@ -26,6 +38,10 @@ module ActiveStorage ActiveStorage.queue = app.config.active_storage.queue ActiveStorage.previewers = app.config.active_storage.previewers || [] ActiveStorage.analyzers = app.config.active_storage.analyzers || [] + ActiveStorage.paths = app.config.active_storage.paths || {} + + ActiveStorage.variable_content_types = app.config.active_storage.variable_content_types || [] + ActiveStorage.content_types_to_serve_as_binary = app.config.active_storage.content_types_to_serve_as_binary || [] end end @@ -69,21 +85,5 @@ module ActiveStorage end end end - - initializer "active_storage.paths" do - config.after_initialize do |app| - if ffprobe_path = app.config.active_storage.paths.ffprobe - ActiveStorage::Analyzer::VideoAnalyzer.ffprobe_path = ffprobe_path - end - - if ffmpeg_path = app.config.active_storage.paths.ffmpeg - ActiveStorage::Previewer::VideoPreviewer.ffmpeg_path = ffmpeg_path - end - - if mutool_path = app.config.active_storage.paths.mutool - ActiveStorage::Previewer::PDFPreviewer.mutool_path = mutool_path - end - end - end end end diff --git a/activestorage/lib/active_storage/errors.rb b/activestorage/lib/active_storage/errors.rb new file mode 100644 index 0000000000..f099b13f5b --- /dev/null +++ b/activestorage/lib/active_storage/errors.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module ActiveStorage + class InvariableError < StandardError; end + class UnpreviewableError < StandardError; end + class UnrepresentableError < StandardError; end +end diff --git a/activestorage/lib/active_storage/previewer.rb b/activestorage/lib/active_storage/previewer.rb index 3d485988e9..dacab1e7df 100644 --- a/activestorage/lib/active_storage/previewer.rb +++ b/activestorage/lib/active_storage/previewer.rb @@ -42,20 +42,22 @@ module ActiveStorage # end # # The output tempfile is opened in the directory returned by ActiveStorage::Downloading#tempdir. - def draw(*argv) # :doc: - Tempfile.open("ActiveStorage", tempdir) do |file| - capture(*argv, to: file) - yield file + def draw(*argv) #:doc: + ActiveSupport::Notifications.instrument("preview.active_storage") do + Tempfile.open("ActiveStorage", tempdir) do |file| + capture(*argv, to: file) + yield file + end end end def capture(*argv, to:) to.binmode - IO.popen(argv) { |out| IO.copy_stream(out, to) } + IO.popen(argv, err: File::NULL) { |out| IO.copy_stream(out, to) } to.rewind end - def logger + def logger #:doc: ActiveStorage.logger end end diff --git a/activestorage/lib/active_storage/previewer/pdf_previewer.rb b/activestorage/lib/active_storage/previewer/pdf_previewer.rb index b84aefcc9c..426ff51eb1 100644 --- a/activestorage/lib/active_storage/previewer/pdf_previewer.rb +++ b/activestorage/lib/active_storage/previewer/pdf_previewer.rb @@ -2,8 +2,6 @@ module ActiveStorage class Previewer::PDFPreviewer < Previewer - class_attribute :mutool_path, default: "mutool" - def self.accept?(blob) blob.content_type == "application/pdf" end @@ -20,5 +18,9 @@ module ActiveStorage def draw_first_page_from(file, &block) draw mutool_path, "draw", "-F", "png", "-o", "-", file.path, "1", &block end + + def mutool_path + ActiveStorage.paths[:mutool] || "mutool" + end end end diff --git a/activestorage/lib/active_storage/previewer/video_previewer.rb b/activestorage/lib/active_storage/previewer/video_previewer.rb index 5d06e33f44..2f28a3d341 100644 --- a/activestorage/lib/active_storage/previewer/video_previewer.rb +++ b/activestorage/lib/active_storage/previewer/video_previewer.rb @@ -2,8 +2,6 @@ module ActiveStorage class Previewer::VideoPreviewer < Previewer - class_attribute :ffmpeg_path, default: "ffmpeg" - def self.accept?(blob) blob.video? end @@ -21,5 +19,9 @@ module ActiveStorage draw ffmpeg_path, "-i", file.path, "-y", "-vcodec", "png", "-vf", "thumbnail", "-vframes", "1", "-f", "image2", "-", &block end + + def ffmpeg_path + ActiveStorage.paths[:ffmpeg] || "ffmpeg" + end end end diff --git a/activestorage/lib/active_storage/service/disk_service.rb b/activestorage/lib/active_storage/service/disk_service.rb index a8728c5bc3..d17eea9046 100644 --- a/activestorage/lib/active_storage/service/disk_service.rb +++ b/activestorage/lib/active_storage/service/disk_service.rb @@ -9,10 +9,10 @@ module ActiveStorage # Wraps a local disk path as an Active Storage service. See ActiveStorage::Service for the generic API # documentation that applies to all services. class Service::DiskService < Service - attr_reader :root + attr_reader :root, :host - def initialize(root:) - @root = root + def initialize(root:, host: "http://localhost:3000") + @root, @host = root, host end def upload(key, io, checksum: nil) @@ -69,14 +69,13 @@ module ActiveStorage verified_key_with_expiration = ActiveStorage.verifier.generate(key, expires_in: expires_in, purpose: :blob_key) generated_url = - if defined?(Rails.application) - Rails.application.routes.url_helpers.rails_disk_service_path \ - verified_key_with_expiration, - filename: filename, disposition: content_disposition_with(type: disposition, filename: filename), content_type: content_type - else - "/rails/active_storage/disk/#{verified_key_with_expiration}/#{filename}?content_type=#{content_type}" \ - "&disposition=#{content_disposition_with(type: disposition, filename: filename)}" - end + Rails.application.routes.url_helpers.rails_disk_service_url( + verified_key_with_expiration, + filename: filename, + disposition: content_disposition_with(type: disposition, filename: filename), + content_type: content_type, + host: host + ) payload[:url] = generated_url @@ -97,12 +96,7 @@ module ActiveStorage purpose: :blob_token } ) - generated_url = - if defined?(Rails.application) - Rails.application.routes.url_helpers.update_rails_disk_service_path verified_token_with_expiration - else - "/rails/active_storage/disk/#{verified_token_with_expiration}" - end + generated_url = Rails.application.routes.url_helpers.update_rails_disk_service_url(verified_token_with_expiration, host: host) payload[:url] = generated_url |