class Shrine::Storage::Linter

Checks if the storage conforms to Shrine’s specification.

Shrine::Storage::Linter.new(storage).call

If the check fails, by default it raises a ‘Shrine::LintError`, but you can also specify `action: :warn`:

Shrine::Storage::Linter.new(storage, action: :warn).call

You can also specify an IO factory which the storage will use:

Shrine::Storage::Linter.new(storage).call(->{File.open("test/fixtures/image.jpg")})

Attributes

storage[R]

Public Class Methods

call(*args) click to toggle source
# File lib/shrine/storage/linter.rb, line 27
def self.call(*args)
  new(*args).call
end
new(storage, action: :error, nonexisting: "nonexisting") click to toggle source
# File lib/shrine/storage/linter.rb, line 31
def initialize(storage, action: :error, nonexisting: "nonexisting")
  @storage     = storage
  @action      = action
  @nonexisting = nonexisting
end

Public Instance Methods

call(io_factory = default_io_factory) click to toggle source
# File lib/shrine/storage/linter.rb, line 37
def call(io_factory = default_io_factory)
  storage.upload(io_factory.call, id = "foo", shrine_metadata: { "foo" => "bar" })

  lint_open(id)
  lint_exists(id)
  lint_url(id)
  lint_delete(id)

  if storage.respond_to?(:delete_prefixed)
    storage.upload(io_factory.call, id1 = "a/a/a")
    storage.upload(io_factory.call, id2 = "a/a/b")
    storage.upload(io_factory.call, id3 = "a/aaa/a")

    lint_delete_prefixed(prefix: "a/a/",
                         expect_deleted: [id1, id2],
                         expect_remaining: [id3])

    storage.delete(id3)
  end

  if storage.respond_to?(:clear!)
    storage.upload(io_factory.call, id = "quux".dup)
    lint_clear(id)
  end

  if storage.respond_to?(:presign)
    lint_presign(id)
  end

  true
end
lint_clear(id) click to toggle source
# File lib/shrine/storage/linter.rb, line 104
def lint_clear(id)
  storage.clear!
  error :clear!, "file still #exists? after clearing" if storage.exists?(id)
end
lint_delete(id) click to toggle source
# File lib/shrine/storage/linter.rb, line 94
def lint_delete(id)
  storage.delete(id)
  error :delete, "file still #exists? after deleting" if storage.exists?(id)
  begin
    storage.delete(id)
  rescue => exception
    error :delete, "shouldn't fail if the file doesn't exist, but raised #{exception.class}"
  end
end
lint_delete_prefixed(prefix:, expect_deleted:, expect_remaining:) click to toggle source
# File lib/shrine/storage/linter.rb, line 116
def lint_delete_prefixed(prefix:, expect_deleted:, expect_remaining:)
  storage.delete_prefixed(prefix)

  expect_deleted.each do |key|
    next unless storage.exists?(key)
    error :delete_prefixed, "#{key} still #exists? after #clear_prefix('a/a/')"
  end

  expect_remaining.each do |key|
    next if storage.exists?(key)
    error :delete_prefixed, "#{key} doesn't #exists? but should after #clear_prefix('a/a/')"
  end
end
lint_exists(id) click to toggle source
# File lib/shrine/storage/linter.rb, line 84
def lint_exists(id)
  error :exists?, "returns false for a file that was uploaded" if !storage.exists?(id)
end
lint_open(id) click to toggle source
# File lib/shrine/storage/linter.rb, line 69
def lint_open(id)
  opened = storage.open(id)
  error :open, "doesn't return a valid IO object" if !io?(opened)
  error :open, "returns an empty IO object" if opened.read.empty?
  opened.close

  begin
    storage.open(@nonexisting)
    error :open, "should raise an exception on nonexisting file"
  rescue Shrine::FileNotFound
  rescue => exception
    error :open, "should raise Shrine::FileNotFound on nonexisting file"
  end
end
lint_presign(id) click to toggle source
# File lib/shrine/storage/linter.rb, line 109
def lint_presign(id)
  data = storage.presign(id)
  error :presign, "result should be a Hash" unless data.respond_to?(:to_h)
  error :presign, "result should include :method key" unless data.to_h.key?(:method)
  error :presign, "result should include :url key" unless data.to_h.key?(:url)
end
lint_url(id) click to toggle source
# File lib/shrine/storage/linter.rb, line 88
def lint_url(id)
  # just assert #url exists, it isn't required to return anything
  url = storage.url(id)
  error :url, "should return either nil or a string" if !(url.nil? || url.is_a?(String))
end

Private Instance Methods

default_io_factory() click to toggle source
# File lib/shrine/storage/linter.rb, line 159
def default_io_factory
  -> { FakeIO.new("file") }
end
error(method_name, message) click to toggle source
# File lib/shrine/storage/linter.rb, line 147
def error(method_name, message)
  if @action == :error
    raise LintError, full_message(method_name, message)
  else
    warn full_message(method_name, message)
  end
end
full_message(method_name, message) click to toggle source
# File lib/shrine/storage/linter.rb, line 155
def full_message(method_name, message)
  "#{@storage.class}##{method_name} - #{message}"
end
io?(object) click to toggle source
# File lib/shrine/storage/linter.rb, line 140
def io?(object)
  uploader.send(:_enforce_io, object)
  true
rescue Shrine::InvalidFile
  false
end
uploader() click to toggle source
# File lib/shrine/storage/linter.rb, line 134
def uploader
  shrine = Class.new(Shrine)
  shrine.storages[:storage] = storage
  shrine.new(:storage)
end