image_optim 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,153 @@
1
+ $:.unshift File.expand_path('../../../lib', __FILE__)
2
+ require 'rspec'
3
+ require 'image_optim/config'
4
+
5
+ class ImageOptim
6
+ describe Config do
7
+ before do
8
+ Config.stub(:global => {}, :local => {})
9
+ end
10
+
11
+ describe "assert_no_unused_options!" do
12
+ it "should not raise when no unused options" do
13
+ config = Config.new({})
14
+ config.assert_no_unused_options!
15
+ end
16
+
17
+ it "should raise when there are unused options" do
18
+ config = Config.new({:unused => true})
19
+ proc {
20
+ config.assert_no_unused_options!
21
+ }.should raise_error(ConfigurationError)
22
+ end
23
+ end
24
+
25
+ describe "nice" do
26
+ it "should be 10 by default" do
27
+ config = Config.new({})
28
+ config.nice.should == 10
29
+ end
30
+
31
+ it "should be 0 if disabled" do
32
+ config = Config.new({:nice => false})
33
+ config.nice.should == 0
34
+ end
35
+
36
+ it "should convert value to number" do
37
+ config = Config.new({:nice => '13'})
38
+ config.nice.should == 13
39
+ end
40
+ end
41
+
42
+ describe "threads" do
43
+ it "should be processor_count by default" do
44
+ config = Config.new({})
45
+ config.stub(:processor_count).and_return(13)
46
+ config.threads.should == 13
47
+ end
48
+
49
+ it "should be 1 if disabled" do
50
+ config = Config.new({:threads => false})
51
+ config.threads.should == 1
52
+ end
53
+
54
+ it "should convert value to number and limit to 1..16" do
55
+ config = Config.new({:threads => '616'})
56
+ config.threads.should == 16
57
+ end
58
+ end
59
+
60
+ describe "for_worker" do
61
+ class Abc < Worker
62
+ def image_formats; []; end
63
+ end
64
+
65
+ it "should return empty hash by default" do
66
+ config = Config.new({})
67
+ config.for_worker(Abc).should == {}
68
+ end
69
+
70
+ it "should return passed hash" do
71
+ config = Config.new({:abc => {:option => true}})
72
+ config.for_worker(Abc).should == {:option => true}
73
+ end
74
+
75
+ it "should return passed false" do
76
+ config = Config.new({:abc => false})
77
+ config.for_worker(Abc).should == false
78
+ end
79
+
80
+ it "should raise on unknown optino" do
81
+ config = Config.new({:abc => 13})
82
+ proc {
83
+ config.for_worker(Abc)
84
+ }.should raise_error(ConfigurationError)
85
+ end
86
+ end
87
+
88
+ describe 'class methods' do
89
+ before do
90
+ Config.unstub(:global)
91
+ Config.unstub(:local)
92
+ end
93
+
94
+ describe 'global' do
95
+ it "should return empty hash for global config if it does not exists" do
96
+ File.should_receive(:file?).with(Config::GLOBAL_CONFIG_PATH).and_return(false)
97
+ Config.should_not_receive(:read)
98
+
99
+ Config.global.should == {}
100
+ end
101
+
102
+ it "should read global config if it exists" do
103
+ File.should_receive(:file?).with(Config::GLOBAL_CONFIG_PATH).and_return(true)
104
+ Config.should_receive(:read).with(Config::GLOBAL_CONFIG_PATH).and_return({:config => true})
105
+
106
+ Config.global.should == {:config => true}
107
+ end
108
+ end
109
+
110
+ describe 'local' do
111
+ it "should return empty hash for local config if it does not exists" do
112
+ File.should_receive(:file?).with(Config::LOCAL_CONFIG_PATH).and_return(false)
113
+ Config.should_not_receive(:read)
114
+
115
+ Config.local.should == {}
116
+ end
117
+
118
+ it "should read local config if it exists" do
119
+ File.should_receive(:file?).with(Config::LOCAL_CONFIG_PATH).and_return(true)
120
+ Config.should_receive(:read).with(Config::LOCAL_CONFIG_PATH).and_return({:config => true})
121
+
122
+ Config.local.should == {:config => true}
123
+ end
124
+ end
125
+
126
+ describe 'read' do
127
+ it "should return hash with deep symbolised keys from yaml file reader" do
128
+ path = double(:path)
129
+ YAML.should_receive(:load_file).with(path).and_return({'config' => {'this' => true}})
130
+
131
+ Config.instance_eval{ read(path) }.should == {:config => {:this => true}}
132
+ end
133
+
134
+ it "should warn and return an empty hash if yaml file reader returns non hash" do
135
+ path = double(:path)
136
+ YAML.should_receive(:load_file).with(path).and_return([:config])
137
+ Config.should_receive(:warn)
138
+
139
+ Config.instance_eval{ read(path) }.should == {}
140
+ end
141
+
142
+ it "should warn and return an empty hash if yaml file reader raises exception" do
143
+ path = double(:path)
144
+ YAML.should_receive(:load_file).with(path).and_raise
145
+ Config.should_receive(:warn)
146
+
147
+ Config.instance_eval{ read(path) }.should == {}
148
+ end
149
+ end
150
+ end
151
+
152
+ end
153
+ end
@@ -0,0 +1,44 @@
1
+ $:.unshift File.expand_path('../../../lib', __FILE__)
2
+ require 'rspec'
3
+ require 'image_optim/handler'
4
+
5
+ describe ImageOptim::Handler do
6
+ it "should use original as source for first conversion and two temp files for further conversions" do
7
+ original = double(:original)
8
+ original.stub(:temp_path){ raise 'temp_path called unexpectedly' }
9
+
10
+ handler = ImageOptim::Handler.new(original)
11
+
12
+ original.should_receive(:temp_path).once.and_return(temp_a = double(:temp_a))
13
+ handler.process do |src, dst|
14
+ [src, dst].should == [original, temp_a]; false
15
+ end
16
+ handler.result.should == nil
17
+
18
+ handler.process do |src, dst|
19
+ [src, dst].should == [original, temp_a]; true
20
+ end
21
+ handler.result.should == temp_a
22
+
23
+ original.should_receive(:temp_path).once.and_return(temp_b = double(:temp_b))
24
+ handler.process do |src, dst|
25
+ [src, dst].should == [temp_a, temp_b]; false
26
+ end
27
+ handler.result.should == temp_a
28
+
29
+ handler.process do |src, dst|
30
+ [src, dst].should == [temp_a, temp_b]; true
31
+ end
32
+ handler.result.should == temp_b
33
+
34
+ handler.process do |src, dst|
35
+ [src, dst].should == [temp_b, temp_a]; true
36
+ end
37
+ handler.result.should == temp_a
38
+
39
+ handler.process do |src, dst|
40
+ [src, dst].should == [temp_a, temp_b]; true
41
+ end
42
+ handler.result.should == temp_b
43
+ end
44
+ end
@@ -0,0 +1,74 @@
1
+ $:.unshift File.expand_path('../../../lib', __FILE__)
2
+ require 'rspec'
3
+ require 'image_optim/hash_helpers'
4
+
5
+ describe ImageOptim::HashHelpers do
6
+ HH = ImageOptim::HashHelpers
7
+
8
+ it "should deep stringify hash keys" do
9
+ HH.deep_stringify_keys({
10
+ :a => 1,
11
+ :b => {
12
+ :c => :a,
13
+ :d => {},
14
+ },
15
+ }).should == {
16
+ 'a' => 1,
17
+ 'b' => {
18
+ 'c' => :a,
19
+ 'd' => {},
20
+ },
21
+ }
22
+ end
23
+
24
+ it "should deep symbolise hash keys" do
25
+ HH.deep_symbolise_keys({
26
+ 'a' => 1,
27
+ 'b' => {
28
+ 'c' => 'a',
29
+ 'd' => {},
30
+ },
31
+ }).should == {
32
+ :a => 1,
33
+ :b => {
34
+ :c => 'a',
35
+ :d => {},
36
+ },
37
+ }
38
+ end
39
+
40
+ it "should deep merge hashes" do
41
+ HH.deep_merge({
42
+ :a => {
43
+ :b => 1,
44
+ :c => {
45
+ :d => 2,
46
+ :e => {
47
+ :f => true
48
+ },
49
+ },
50
+ },
51
+ :y => 10,
52
+ }, {
53
+ :a => {
54
+ :b => 2,
55
+ :c => {
56
+ :d => 3,
57
+ :e => false,
58
+ },
59
+ },
60
+ :z => 20,
61
+ }).should == {
62
+ :a => {
63
+ :b => 2,
64
+ :c => {
65
+ :d => 3,
66
+ :e => false,
67
+ },
68
+ },
69
+ :y => 10,
70
+ :z => 20,
71
+ }
72
+ end
73
+
74
+ end
@@ -0,0 +1,39 @@
1
+ $:.unshift File.expand_path('../../../lib', __FILE__)
2
+ require 'rspec'
3
+ require 'image_optim/image_path'
4
+
5
+ describe ImageOptim::ImagePath do
6
+ ImagePath = ImageOptim::ImagePath
7
+
8
+ describe "convert" do
9
+ it "should return ImagePath for string" do
10
+ path = 'a'
11
+
12
+ ImagePath.convert(path).should be_a(ImageOptim::ImagePath)
13
+ ImagePath.convert(path).should eq(ImageOptim::ImagePath.new(path))
14
+
15
+ ImagePath.convert(path).should_not eq(path)
16
+ ImagePath.convert(path).should_not be(path)
17
+ end
18
+
19
+ it "should return ImagePath for Pathname" do
20
+ pathname = Pathname.new('a')
21
+
22
+ ImagePath.convert(pathname).should be_a(ImageOptim::ImagePath)
23
+ ImagePath.convert(pathname).should eq(ImageOptim::ImagePath.new(pathname))
24
+
25
+ ImagePath.convert(pathname).should eq(pathname)
26
+ ImagePath.convert(pathname).should_not be(pathname)
27
+ end
28
+
29
+ it "should return same instance for ImagePath" do
30
+ image_path = ImageOptim::ImagePath.new('a')
31
+
32
+ ImagePath.convert(image_path).should be_a(ImageOptim::ImagePath)
33
+ ImagePath.convert(image_path).should eq(ImageOptim::ImagePath.new(image_path))
34
+
35
+ ImagePath.convert(image_path).should eq(image_path)
36
+ ImagePath.convert(image_path).should be(image_path)
37
+ end
38
+ end
39
+ end
@@ -1,4 +1,4 @@
1
- $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
2
  require 'rspec'
3
3
  require 'image_optim'
4
4
  require 'tempfile'
@@ -39,14 +39,11 @@ ImageOptim::ImagePath.class_eval do
39
39
  end
40
40
  end
41
41
 
42
- def with_env(key, value)
43
- saved, ENV[key] = ENV[key], value
44
- yield
45
- ensure
46
- ENV[key] = saved
47
- end
48
-
49
42
  describe ImageOptim do
43
+ before do
44
+ ImageOptim::Config.stub(:global => {}, :local => {})
45
+ end
46
+
50
47
  describe "isolated" do
51
48
  describe "optimize" do
52
49
  TEST_IMAGES.each do |original|
@@ -56,7 +53,7 @@ describe ImageOptim do
56
53
  Tempfile.reset_init_count
57
54
  image_optim = ImageOptim.new
58
55
  optimized_image = image_optim.optimize_image(copy)
59
- optimized_image.should be_a(ImageOptim::ImagePath)
56
+ expect(optimized_image).to be_a(ImageOptim::ImagePath::Optimized)
60
57
  optimized_image.size.should be_in_range(1...original.size)
61
58
  optimized_image.read.should_not == original.read
62
59
  copy.read.should == original.read
@@ -90,6 +87,18 @@ describe ImageOptim do
90
87
  end
91
88
  end
92
89
 
90
+ describe "optimize image data" do
91
+ TEST_IMAGES.each do |original|
92
+ it "should optimize #{original}" do
93
+ image_optim = ImageOptim.new
94
+ optimized_data = image_optim.optimize_image_data(original.read)
95
+ optimized_data.should == image_optim.optimize_image(original.temp_copy).read
96
+
97
+ image_optim.optimize_image_data(optimized_data).should be_nil
98
+ end
99
+ end
100
+ end
101
+
93
102
  describe "stop optimizing" do
94
103
  TEST_IMAGES.each do |original|
95
104
  it "should stop optimizing #{original}" do
@@ -111,7 +120,7 @@ describe ImageOptim do
111
120
  copies = TEST_IMAGES.map(&:temp_copy)
112
121
  optimized_images = ImageOptim.optimize_images(copies)
113
122
  TEST_IMAGES.zip(copies, optimized_images).each do |original, copy, optimized_image|
114
- optimized_image.should be_a(ImageOptim::ImagePath)
123
+ expect(optimized_image).to be_a(ImageOptim::ImagePath::Optimized)
115
124
  optimized_image.size.should be_in_range(1...original.size)
116
125
  optimized_image.read.should_not == original.read
117
126
  copy.read.should == original.read
@@ -126,6 +135,13 @@ describe ImageOptim do
126
135
  copy.read.should_not == original.read
127
136
  end
128
137
  end
138
+
139
+ it "should optimize datas" do
140
+ optimized_images_datas = ImageOptim.optimize_images_data(TEST_IMAGES.map(&:read))
141
+ TEST_IMAGES.zip(optimized_images_datas).each do |original, optimized_image_data|
142
+ optimized_image_data.should == ImageOptim.optimize_image(original.temp_copy).read
143
+ end
144
+ end
129
145
  end
130
146
 
131
147
  describe "unsupported" do
@@ -192,90 +208,6 @@ describe ImageOptim do
192
208
  end
193
209
  end
194
210
 
195
- describe "resolve bin" do
196
- it "should resolve bin in path" do
197
- with_env 'LS_BIN', nil do
198
- image_optim = ImageOptim.new
199
- image_optim.should_receive(:bin_accessible?).with(:ls).once.and_return(true)
200
- FSPath.should_not_receive(:temp_dir)
201
-
202
- 5.times do
203
- image_optim.resolve_bin!(:ls).should be_true
204
- end
205
- end
206
- end
207
-
208
- it "should resolve bin specified in ENV" do
209
- path = (FSPath(__FILE__).dirname / '../bin/image_optim').relative_path_from(Dir.pwd).to_s
210
- with_env 'IMAGE_OPTIM_BIN', path do
211
- tmpdir = double(:tmpdir)
212
- symlink = double(:symlink)
213
-
214
- image_optim = ImageOptim.new
215
- image_optim.should_receive(:bin_accessible?).with(symlink).once.and_return(true)
216
- FSPath.should_receive(:temp_dir).once.and_return(tmpdir)
217
- tmpdir.should_receive(:/).with(:image_optim).once.and_return(symlink)
218
- symlink.should_receive(:make_symlink).with(File.expand_path(path)).once
219
-
220
- at_exit_blocks = []
221
- image_optim.should_receive(:at_exit).twice do |&block|
222
- at_exit_blocks.unshift(block)
223
- end
224
-
225
- 5.times do
226
- image_optim.resolve_bin!(:image_optim).should be_true
227
- end
228
-
229
- FileUtils.should_receive(:remove_entry_secure).with(tmpdir)
230
- symlink.should_receive(:unlink)
231
- at_exit_blocks.each(&:call)
232
- end
233
- end
234
-
235
- it "should raise on failure to resolve bin" do
236
- with_env 'SHOULD_NOT_EXIST_BIN', nil do
237
- image_optim = ImageOptim.new
238
- image_optim.should_receive(:bin_accessible?).with(:should_not_exist).once.and_return(false)
239
- FSPath.should_not_receive(:temp_dir)
240
-
241
- 5.times do
242
- expect do
243
- image_optim.resolve_bin!(:should_not_exist)
244
- end.to raise_error ImageOptim::BinNotFoundError
245
- end
246
- end
247
- end
248
-
249
- it "should raise on failure to resolve bin specified in ENV" do
250
- path = (FSPath(__FILE__).dirname / '../bin/should_not_exist_bin').relative_path_from(Dir.pwd).to_s
251
- with_env 'SHOULD_NOT_EXIST_BIN', path do
252
- tmpdir = double(:tmpdir)
253
- symlink = double(:symlink)
254
-
255
- image_optim = ImageOptim.new
256
- image_optim.should_receive(:bin_accessible?).with(symlink).once.and_return(false)
257
- FSPath.should_receive(:temp_dir).once.and_return(tmpdir)
258
- tmpdir.should_receive(:/).with(:should_not_exist).once.and_return(symlink)
259
- symlink.should_receive(:make_symlink).with(File.expand_path(path)).once
260
-
261
- at_exit_blocks = []
262
- image_optim.should_receive(:at_exit).twice do |&block|
263
- at_exit_blocks.unshift(block)
264
- end
265
-
266
- 5.times do
267
- expect do
268
- image_optim.resolve_bin!(:should_not_exist)
269
- end.to raise_error ImageOptim::BinNotFoundError
270
- end
271
-
272
- FileUtils.should_receive(:remove_entry_secure).with(tmpdir)
273
- symlink.should_receive(:unlink)
274
- at_exit_blocks.each(&:call)
275
- end
276
- end
277
- end
278
-
279
211
  describe "auto orienting" do
280
212
  original = ImageOptim::ImagePath.new(__FILE__).dirname / 'images/orient/original.jpg'
281
213
  ImageOptim::ImagePath.new(__FILE__).dirname.glob('images/orient/?.jpg').each do |jpg|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: image_optim
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Kuchin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-20 00:00:00.000000000 Z
11
+ date: 2013-12-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fspath
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.5
19
+ version: 2.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.5
26
+ version: 2.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: image_size
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -106,13 +106,21 @@ files:
106
106
  - Gemfile
107
107
  - LICENSE.txt
108
108
  - README.markdown
109
- - TODO
110
109
  - bin/image_optim
111
110
  - image_optim.gemspec
112
111
  - lib/image_optim.rb
112
+ - lib/image_optim/bin_not_found_error.rb
113
+ - lib/image_optim/bin_resolver.rb
114
+ - lib/image_optim/config.rb
115
+ - lib/image_optim/configuration_error.rb
116
+ - lib/image_optim/handler.rb
117
+ - lib/image_optim/hash_helpers.rb
113
118
  - lib/image_optim/image_path.rb
114
119
  - lib/image_optim/option_definition.rb
115
120
  - lib/image_optim/option_helpers.rb
121
+ - lib/image_optim/railtie.rb
122
+ - lib/image_optim/runner.rb
123
+ - lib/image_optim/true_false_nil.rb
116
124
  - lib/image_optim/worker.rb
117
125
  - lib/image_optim/worker/advpng.rb
118
126
  - lib/image_optim/worker/gifsicle.rb
@@ -122,7 +130,12 @@ files:
122
130
  - lib/image_optim/worker/optipng.rb
123
131
  - lib/image_optim/worker/pngcrush.rb
124
132
  - lib/image_optim/worker/pngout.rb
125
- - script/options_for_readme
133
+ - script/update_worker_options_in_readme
134
+ - spec/image_optim/bin_resolver_spec.rb
135
+ - spec/image_optim/config_spec.rb
136
+ - spec/image_optim/handler_spec.rb
137
+ - spec/image_optim/hash_helpers_spec.rb
138
+ - spec/image_optim/image_path_spec.rb
126
139
  - spec/image_optim_spec.rb
127
140
  - spec/images/comparison.png
128
141
  - spec/images/decompressed.jpeg
@@ -170,6 +183,11 @@ specification_version: 4
170
183
  summary: Optimize (lossless compress) images (jpeg, png, gif) using external utilities
171
184
  (advpng, gifsicle, jpegoptim, jpegtran, optipng, pngcrush, pngout)
172
185
  test_files:
186
+ - spec/image_optim/bin_resolver_spec.rb
187
+ - spec/image_optim/config_spec.rb
188
+ - spec/image_optim/handler_spec.rb
189
+ - spec/image_optim/hash_helpers_spec.rb
190
+ - spec/image_optim/image_path_spec.rb
173
191
  - spec/image_optim_spec.rb
174
192
  - spec/images/comparison.png
175
193
  - spec/images/decompressed.jpeg
data/TODO DELETED
@@ -1,12 +0,0 @@
1
- preserve color (leave_color branch)
2
- preserve all extra stuff
3
- global level of optimization #10
4
- timeout workers?
5
- based on file size?
6
- fail worker instead of process on bin not present?
7
- preserve time/attrs option?
8
- file based config
9
- http://www.erickcantwell.com/code/config.rb
10
- https://github.com/derks/ruby-parseconfig
11
- http://stick.gk2.sk/2009/03/the-ugly-duckling-called-xdg_config_home/
12
- http://css-ig.net/png-tools-overview
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: UTF-8
3
-
4
- $: << File.expand_path('../../lib', __FILE__)
5
-
6
- require 'image_optim'
7
-
8
- ImageOptim::Worker.klasses.each_with_index do |klass, i|
9
- puts "### #{klass.bin_sym}"
10
- klass.option_definitions.each do |option_definition|
11
- puts "* `:#{option_definition.name}` — #{option_definition.description} *(defaults to #{option_definition.default})*"
12
- end
13
- puts
14
- end