Skip to content

Commit

Permalink
added cache_dir and cache_workers options to cache results (2)
Browse files Browse the repository at this point in the history
  • Loading branch information
gpakosz committed Apr 8, 2016
1 parent 6f99395 commit c27e363
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 159 deletions.
18 changes: 9 additions & 9 deletions lib/image_optim.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,19 @@ def optimize_image(original)
original = ImagePath.convert(original)
return unless (workers = workers_for_image(original))

cached = @cache.read(original)
return ImagePath::Optimized.new(cached, original) unless cached.nil?

optimized = Handler.for(original) do |handler|
workers.each do |worker|
handler.process do |src, dst|
worker.optimize(src, dst)
optimized = @cache.read(original)
if optimized.nil?
optimized = Handler.for(original) do |handler|
workers.each do |worker|
handler.process do |src, dst|
worker.optimize(src, dst)
end
end
end
@cache.write(original, optimized)
end
return unless optimized

@cache.write(optimized, original)
return unless optimized
ImagePath::Optimized.new(optimized, original)
end

Expand Down
6 changes: 5 additions & 1 deletion lib/image_optim/bin_resolver/bin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ class Bin
class UnknownVersion < Error; end
class BadVersion < Error; end

attr_reader :name, :path, :version, :digest
attr_reader :name, :path, :version
def initialize(name, path)
@name = name.to_sym
@path = path.to_s
@version = detect_version
end

def digest
return @digest if defined?(@digest)
@digest = File.exist?(@path) && Digest::SHA1.file(@path).hexdigest
end

Expand Down
48 changes: 19 additions & 29 deletions lib/image_optim/cache.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
require 'digest/sha1'

class ImageOptim
# Handles image cache
class Cache
def initialize(image_optim, workers_by_format)
return unless (@cache_dir = image_optim.cache_dir)
@cache_workers = image_optim.cache_workers

FileUtils.mkdir_p(@cache_dir)

@options_by_format = Hash[workers_by_format.map do |k, v|
[k, Marshal.dump(v.map(&:inspect))]
end]
@workers_by_format = Hash[workers_by_format.map do |k, v|
@bins_by_format = Hash[workers_by_format.map do |k, v|
[k, Marshal.dump(
v.map do |worker|
worker.used_bins.map! do |sym|
Expand All @@ -24,36 +24,18 @@ def initialize(image_optim, workers_by_format)
def read(original)
return unless @cache_dir

cached = File.join(@cache_dir, original.digest.to_s)
cached = File.join(@cache_dir, digest(original))
return unless File.exist?(cached)

return unless File.exist?("#{cached}.options")
options = options_by_format(original.format)
options_ = File.read("#{cached}.options")
return unless options_ == options

if @cache_workers
workers = workers_by_format(original.format)
return unless File.exist?("#{cached}.workers")
workers_ = File.read("#{cached}.workers")
return unless workers_ == workers
end

cached
end

def write(optimized, original)
return optimized unless @cache_dir
def write(original, optimized)
return unless @cache_dir && optimized

cached = File.join(@cache_dir, original.digest.to_s)
FileUtils.cp(optimized, cached)

options = options_by_format(original.format)
File.write("#{cached}.options", options)

return unless @cache_workers
workers = workers_by_format(original.format)
File.write("#{cached}.workers", workers)
cached = File.join(@cache_dir, digest(original))
FileUtils.mkdir_p(File.dirname(cached))
FileUtils.copy_file(optimized, cached, true)
end

private
Expand All @@ -62,8 +44,16 @@ def options_by_format(format)
@options_by_format[format]
end

def workers_by_format(format)
@workers_by_format[format]
def bins_by_format(format)
@bins_by_format[format]
end

def digest(original)
digest = Digest::SHA1.file(original)
digest.update options_by_format(original.format)
digest.update bins_by_format(original.format) if @cache_workers
s = digest.hexdigest
"#{s[0..1]}/#{s[2..-1]}"
end
end
end
6 changes: 0 additions & 6 deletions lib/image_optim/image_path.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
require 'fspath'
require 'image_optim/image_meta'
require 'digest/sha1'

class ImageOptim
# FSPath with additional helpful methods
Expand Down Expand Up @@ -65,10 +64,5 @@ def binread
def self.convert(path)
path.is_a?(self) ? path : new(path)
end

# Returns the file digest
def digest
@digest ||= Digest::SHA1.file(self).hexdigest
end
end
end
178 changes: 64 additions & 114 deletions spec/image_optim/cache_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,21 @@
describe ImageOptim::Cache do
before do
stub_const('Cache', ImageOptim::Cache)
stub_const('ImagePath', ImageOptim::ImagePath)
end

let(:original){ double('/somewhere/original', :format => :ext) }
let(:cache_dir) do
dir = '/somewhere/cache'
allow(FileUtils).to receive(:mkdir_p).with(Regexp.new(Regexp.escape(dir)))
dir
end

let(:original) do
original = double('/somewhere/original', :format => :ext)
allow(Digest::SHA1).to receive(:file).with(original) do
Digest::SHA1.new << 'some content!'
end
original
end
let(:optimized){ '/somewhere/optimized' }

context 'when cache is disabled (default)' do
Expand All @@ -32,167 +43,106 @@
end

context 'when cache is enabled (without worker digests)' do
let(:cache_dir) do
dir = '/somewhere/cache'
allow(FileUtils).to receive(:mkdir_p).with(dir)
dir
end
let(:image_optim) do
double(:image_optim,
:cache_dir => cache_dir, :cache_workers => false)
end
let(:cache){ Cache.new(image_optim, {}) }
let(:cache) do
cache = Cache.new(image_optim, {})
allow(cache).
to receive(:options_by_format).
with(original.format).
and_return('some options!')
allow(cache).
to receive(:bins_by_format).
with(original.format).
and_return('some bins!')
cache
end
let(:digest){ cache.send(:digest, original) }

describe :read do
it 'returns nil when cached file does not exist' do
allow(original).to receive(:digest).and_return(0xc0ffee1eaf.to_s)
cached = File.join(cache_dir, original.digest)
allow(File).to receive(:exist?).with(cached){ false }

expect(cache.read(original)).to be nil
end

it 'returns nil when cached file exists but options differ' do
allow(original).to receive(:digest).and_return(0xc0ffee1eaf.to_s)
cached = File.join(cache_dir, original.digest)
allow(File).to receive(:exist?).with(cached){ true }

options = double
cached_options = double
allow(File).to receive(:exist?).with("#{cached}.options"){ true }
allow(File).to receive(:read).with("#{cached}.options") do
cached_options
end
allow(cache).to receive(:options_by_format).with(original.format) do
options
end
cached = File.join(cache_dir, digest)
allow(File).to receive(:exist?).and_return(true)
allow(File).to receive(:exist?).with(cached).and_return(false)

expect(cache.read(original)).to be nil
end

it 'returns cached file when it exists and options match' do
allow(original).to receive(:digest).and_return(0xc0ffee1eaf.to_s)
cached = File.join(cache_dir, original.digest)
allow(File).to receive(:exist?).with(cached){ true }

options = double
allow(File).to receive(:exist?).with("#{cached}.options"){ true }
allow(File).to receive(:read).with("#{cached}.options") do
options
end
allow(cache).to receive(:options_by_format).with(original.format) do
options
end
cached = File.join(cache_dir, digest)
allow(File).to receive(:exist?).with(cached).and_return(true)

expect(cache.read(original)).to eq(cached)
end
end

describe :write do
it 'writes optimized to cache along with options' do
allow(original).to receive(:digest).and_return(0xc0ffee1eaf.to_s)
cached = File.join(cache_dir, original.digest)
cached = File.join(cache_dir, digest)
allow(FileUtils).to receive(:copy_file).with(optimized, cached, true)

options = double
allow(FileUtils).to receive(:cp).with(optimized, cached)
allow(File).to receive(:write).with("#{cached}.options", options){ 0 }
allow(cache).to receive(:options_by_format).with(original.format) do
options
end

expect(cache.write(optimized, original))
expect(cache.write(original, optimized))
end
end
end

context 'when cache is enabled (with worker digests)' do
let(:cache_dir) do
dir = '/somewhere/cache'
allow(FileUtils).to receive(:mkdir_p).with(dir)
dir
end
let(:image_optim) do
double(:image_optim, :cache_dir => cache_dir, :cache_workers => true)
end
let(:cache){ Cache.new(image_optim, {}) }
let(:cache) do
cache = Cache.new(image_optim, {})
allow(cache).
to receive(:options_by_format).
with(original.format).
and_return('some options!')
allow(cache).
to receive(:bins_by_format).
with(original.format).
and_return('some bins!')
cache
end
let(:digest){ cache.send(:digest, original) }

describe :read do
it 'returns nil when cached file does not exist' do
allow(original).to receive(:digest).and_return(0xc0ffee1eaf.to_s)
cached = File.join(cache_dir, original.digest)
allow(File).to receive(:exist?).with(cached){ false }
cached = File.join(cache_dir, digest)
allow(File).to receive(:exist?).with(cached).and_return(false)

expect(cache.read(original)).to be nil
end

it 'returns nil when cached file exists but worker digests differ' do
allow(original).to receive(:digest).and_return(0xc0ffee1eaf.to_s)
cached = File.join(cache_dir, original.digest)
allow(File).to receive(:exist?).with(cached){ true }

options = double
allow(File).to receive(:exist?).with("#{cached}.options"){ true }
allow(File).to receive(:read).with("#{cached}.options") do
options
end
allow(cache).to receive(:options_by_format).with(original.format) do
options
end

workers = double
cached_workers = double
allow(File).to receive(:exist?).with("#{cached}.workers"){ true }
allow(File).to receive(:read).with("#{cached}.workers"){ workers }
allow(cache).to receive(:workers_by_format).with(original.format) do
cached_workers
end
cached = File.join(cache_dir, digest)
allow(File).to receive(:exist?).and_return(false)
allow(File).to receive(:exist?).with(cached).and_return(true)

allow(cache).
to receive(:bins_by_format).
with(original.format).
and_return('some other bins!')

expect(cache.read(original)).to be nil
end

it 'returns cached file when it exists and options match' do
allow(original).to receive(:digest).and_return(0xc0ffee1eaf.to_s)
cached = File.join(cache_dir, original.digest)
allow(File).to receive(:exist?).with(cached){ true }

options = double
allow(File).to receive(:exist?).with("#{cached}.options"){ true }
allow(File).to receive(:read).with("#{cached}.options"){ options }
allow(cache).to receive(:options_by_format).with(original.format) do
options
end

workers = double
allow(File).to receive(:exist?).with("#{cached}.workers"){ true }
allow(File).to receive(:read).with("#{cached}.workers"){ workers }
allow(cache).to receive(:workers_by_format).with(original.format) do
workers
end
it 'returns cached file when it exists and both options+workers match' do
cached = File.join(cache_dir, digest)
allow(File).to receive(:exist?).and_return(false)
allow(File).to receive(:exist?).with(cached).and_return(true)

expect(cache.read(original)).to eq(cached)
end
end

describe :write do
it 'writes optimized to cache along with options and worker digests' do
allow(original).to receive(:digest).and_return(0xc0ffee1eaf.to_s)
cached = File.join(cache_dir, original.digest)

allow(FileUtils).to receive(:cp).with(optimized, cached)
cached = File.join(cache_dir, digest)

options = double
allow(File).to receive(:write).with("#{cached}.options", options){ 0 }
allow(cache).to receive(:options_by_format).with(original.format) do
options
end
allow(FileUtils).to receive(:copy_file).with(optimized, cached, true)

workers = double
allow(File).to receive(:write).with("#{cached}.workers", workers){ 0 }
allow(cache).to receive(:workers_by_format).with(original.format) do
workers
end

expect(cache.write(optimized, original))
expect(cache.write(original, optimized))
end
end
end
Expand Down

0 comments on commit c27e363

Please sign in to comment.