Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't overwrite original if optimized is larger #95

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## unreleased

* Use quality `0..100` by default in lossy mode of pngquant worker [#77](https://github.com/toy/image_optim/issues/77) [@toy](https://github.com/toy)
* Don't do anything when "optimized" version is larger and `:skip_bigger => true` [@R167](https://github.com/R167)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue number is missing


## v0.21.0 (2015-05-30)

Expand Down
6 changes: 5 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ gem 'image_optim_pack'
With version:

```ruby
gem 'image_optim', '~> 0.11'
gem 'image_optim', '~> 0.21'
```

If you want to check latest changes:
Expand Down Expand Up @@ -234,6 +234,9 @@ end
image_optim.optimize_images!(Dir['*.*'])

image_optim.optimize_images_data(datas)

# Pass options like the sanity check
image_optim.optimize_images!(images, :always_replace => false)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a remainder of previous commits.

```

### From rails
Expand Down Expand Up @@ -271,6 +274,7 @@ optipng:
* `:pack` — Require image\_optim\_pack or disable it, by default image\_optim\_pack will be used if available, will turn on `:skip-missing-workers` unless explicitly disabled *(defaults to `nil`)*
* `:skip_missing_workers` — Skip workers with missing or problematic binaries *(defaults to `false`)*
* `:allow_lossy` — Allow lossy workers and optimizations *(defaults to `false`)*
* `:skip_bigger` — Perform sanity check (esp. on jpg) and only return if new image is smaller *(defaults to `false`)*

Worker can be disabled by passing `false` instead of options hash or by setting option `:disable` to `true`.

Expand Down
Binary file added lib/.DS_Store
Binary file not shown.
24 changes: 23 additions & 1 deletion lib/image_optim.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ class ImageOptim
# Allow lossy workers and optimizations
attr_reader :allow_lossy

# Skip images where optimization makes them bigger
attr_reader :skip_bigger

# Initialize workers, specify options using worker underscored name:
#
# pass false to disable worker
Expand Down Expand Up @@ -67,6 +70,7 @@ def initialize(options = {})
pack
skip_missing_workers
allow_lossy
skip_bigger
].each do |name|
instance_variable_set(:"@#{name}", config.send(name))
$stderr << "#{name}: #{send(name)}\n" if verbose
Expand Down Expand Up @@ -101,11 +105,23 @@ def optimize_image(original)
end
end
return unless result
return if skip_bigger && result.size > original.size
ImagePath::Optimized.new(result, original)
end

# Optimize one file in place, return original as OptimizedImagePath or nil if
# optimization failed
#
# Normal operation will obey default logic of workers
#
# image_optim.optimize_image!(original)
#
# Setting always replace to false will perform a sanity check to see if the
# 'optimized' image is indeed smaller than the original. If it is indeed
# larger, the original will not be replaced and the code will act like
# optimization failed
#
# image_optim.optimize_image!(original, :skip_bigger => true)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This too

def optimize_image!(original)
original = ImagePath.convert(original)
return unless (result = optimize_image(original))
Expand Down Expand Up @@ -140,6 +156,10 @@ def optimize_images(paths, &block)
# if block given yields path and result for each image and returns array of
# yield results
# else return array of path and result pairs
#
# Passing optional params will pass them down to the `optimize_image!` call
#
# image_optim.optimize_images!(some_images, :skip_bigger => true)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this

def optimize_images!(paths, &block)
run_method_for(paths, :optimize_image!, &block)
end
Expand All @@ -163,7 +183,9 @@ def self.method_missing(method, *args, &block)

# Version of image_optim gem spec loaded
def self.version
Gem.loaded_specs['image_optim'].version.to_s rescue 'DEV'
Gem.loaded_specs['image_optim'].version.to_s
rescue
'DEV'
end

# Full version of image_optim
Expand Down
12 changes: 12 additions & 0 deletions lib/image_optim/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,18 @@ def pack
false
end

# Whether or not to perfom size sanity check
def skip_bigger
opt = get!(:skip_bigger)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simplified to !!get!(:skip_bigger)


case opt
when true
true
else
false
end
end

# Skip missing workers, converted to boolean
def skip_missing_workers
if key?(:skip_missing_workers)
Expand Down
7 changes: 6 additions & 1 deletion lib/image_optim/runner/option_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ def wrap_regex(width)
options[:pack] = pack
end

op.on('--[no-]skip-bigger', 'Skip files that end up becoming larger. '\
'(defaults to false)') do |skip_bigger|
options[:skip_bigger] = skip_bigger
end

op.separator nil
op.separator ' Disabling workers:'

Expand Down Expand Up @@ -174,7 +179,7 @@ def wrap_regex(width)

bin = klass.bin_sym
klass.option_definitions.each do |option_definition|
name = option_definition.name.to_s.gsub('_', '-')
name = option_definition.name.to_s.tr('_', '-')
default = option_definition.default
type = option_definition.type

Expand Down
21 changes: 21 additions & 0 deletions spec/image_optim/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@
end
end

describe :skip_bigger do
before do
allow(IOConfig).to receive(:read_options).and_return({})
end

it 'is true by default' do
config = IOConfig.new({})
expect(config.skip_bigger).to be false
end

it 'is false when false' do
config = IOConfig.new(:skip_bigger => true)
expect(config.skip_bigger).to be true
end

it 'is false when false' do
config = IOConfig.new(:skip_bigger => false)
expect(config.skip_bigger).to be false
end
end

describe :threads do
before do
allow(IOConfig).to receive(:read_options).and_return({})
Expand Down
43 changes: 40 additions & 3 deletions spec/image_optim_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,41 @@ def temp_copy(image)
end
end
end

describe 'skipping larger' do
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify goal and reduce duplication it is better to extract common things using before and let

it 'skips larger files' do
image_optim = ImageOptim.new(:skip_bigger => true)
original = double(:size => 12_345)
optimized = double(:size => 100_000)

allow(ImageOptim::ImagePath).to receive(:convert).and_return original

expect(image_optim).to receive(:workers_for_image).with(original).
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allow instead of expect and it returns either an Array or nil so and_return([]) would be better

and_return true

allow(ImageOptim::Handler).to receive(:for).and_return optimized

expect(optimized).to receive(:size).and_return 100_000
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and next line are redundant

expect(original).to receive(:size).and_return 12_345

expect(image_optim.optimize_image(original)).to be nil
end

it 'does not skips larger files when opt is off' do
image_optim = ImageOptim.new(:skip_bigger => false)
original = double(:size => 12_345)
optimized = double(:original_size => 12_345, :size => 100_000)

allow(ImageOptim::ImagePath).to receive(:convert).and_return original

expect(image_optim).to receive(:workers_for_image).with(original).
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

and_return true

allow(ImageOptim::Handler).to receive(:for).and_return optimized

expect(image_optim.optimize_image(original)).to_not be nil
end
end
end

it 'ignores text file' do
Expand Down Expand Up @@ -117,7 +152,7 @@ def temp_copy(image)
describe :optimize_image! do
it 'optimizes image and replaces original' do
original = double
optimized = double(:original_size => 12_345)
optimized = double(:original_size => 12_345, :size => 1_000)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is not needed

optimized_wrap = double
image_optim = ImageOptim.new

Expand Down Expand Up @@ -215,7 +250,8 @@ def temp_copy(image)
image_optim = ImageOptim.new
results = test_images.map do |src|
dst = double
expect(image_optim).to receive(method).with(src).and_return(dst)
expect(image_optim).to receive(method).with(src).
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to split the line

and_return(dst)
[src, dst]
end
expect(image_optim.send(list_method, test_images)).to eq(results)
Expand All @@ -228,7 +264,8 @@ def temp_copy(image)
image_optim = ImageOptim.new
results = test_images.map do |src|
dst = double
expect(image_optim).to receive(method).with(src).and_return(dst)
expect(image_optim).to receive(method).with(src).
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also no need to split the line

and_return(dst)
[src, dst, :test]
end
expect(image_optim.send(list_method, test_images) do |src, dst|
Expand Down