image_optim 0.2.1 → 0.3.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.
- data/bin/image_optim +54 -7
- data/image_optim.gemspec +1 -1
- data/lib/image_optim.rb +25 -22
- data/lib/image_optim/image_path.rb +10 -0
- data/spec/image_optim_spec.rb +43 -6
- metadata +5 -5
data/bin/image_optim
CHANGED
@@ -48,14 +48,61 @@ if ARGV.empty?
|
|
48
48
|
abort "specify image paths to optimize\n\n#{option_parser.help}"
|
49
49
|
else
|
50
50
|
io = ImageOptim.new(options)
|
51
|
-
paths = ARGV
|
51
|
+
paths = ARGV.reject do |arg|
|
52
|
+
unless File.file?(arg)
|
53
|
+
$stderr << "WARN: #{arg} is not a file\n"
|
54
|
+
end
|
55
|
+
end
|
52
56
|
paths = paths.with_progress('optimizing') if paths.length > 1
|
53
57
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
58
|
+
module Space
|
59
|
+
SIZE_SYMBOLS = %w[B K M G T P E Z Y].freeze
|
60
|
+
PRECISION = 1
|
61
|
+
LENGTH = 4 + PRECISION + 1
|
62
|
+
COEF = 1 / Math.log(10)
|
63
|
+
|
64
|
+
EMPTY_SPACE = ' ' * LENGTH
|
65
|
+
NOT_COUNTED_SPACE = '!' * LENGTH
|
66
|
+
|
67
|
+
class << self
|
68
|
+
attr_writer :base10
|
69
|
+
def denominator
|
70
|
+
@denominator ||= @base10 ? 1000.0 : 1024.0
|
71
|
+
end
|
72
|
+
|
73
|
+
def space(size, options = {})
|
74
|
+
case size
|
75
|
+
when false
|
76
|
+
NOT_COUNTED_SPACE.bold.red
|
77
|
+
when 0, nil
|
78
|
+
EMPTY_SPACE
|
79
|
+
else
|
80
|
+
number, degree = size, 0
|
81
|
+
while number.abs >= 1000 && degree < SIZE_SYMBOLS.length - 1
|
82
|
+
number /= denominator
|
83
|
+
degree += 1
|
84
|
+
end
|
85
|
+
|
86
|
+
"#{degree == 0 ? number.to_s : "%.#{PRECISION}f" % number}#{SIZE_SYMBOLS[degree]}".rjust(LENGTH)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def size_percent(src_size, dst_size)
|
93
|
+
'%5.2f%% %s' % [100 - 100.0 * dst_size / src_size, Space.space(src_size - dst_size)]
|
94
|
+
end
|
95
|
+
|
96
|
+
results = io.optimize_images(paths) do |src, dst|
|
97
|
+
if dst
|
98
|
+
src_size, dst_size = src.size, dst.size
|
99
|
+
percent = size_percent(src_size, dst_size)
|
100
|
+
dst.replace(src)
|
101
|
+
["#{percent} #{src}", src_size, dst_size]
|
102
|
+
else
|
103
|
+
["------ #{Space::EMPTY_SPACE} #{src}", src.size, src.size]
|
104
|
+
end
|
59
105
|
end
|
60
|
-
|
106
|
+
lines, src_sizes, dst_sizes = results.transpose
|
107
|
+
$stdout.puts lines, "Total: #{size_percent(src_sizes.inject(:+), dst_sizes.inject(:+))}\n"
|
61
108
|
end
|
data/image_optim.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'image_optim'
|
5
|
-
s.version = '0.
|
5
|
+
s.version = '0.3.0'
|
6
6
|
s.summary = %q{Optimize images (jpeg, png, gif) using external utilities (advpng, gifsicle, jpegoptim, jpegtran, optipng, pngcrush, pngout)}
|
7
7
|
s.homepage = "http://github.com/toy/#{s.name}"
|
8
8
|
s.authors = ['Ivan Kuchin']
|
data/lib/image_optim.rb
CHANGED
@@ -94,33 +94,23 @@ class ImageOptim
|
|
94
94
|
def optimize_image!(original)
|
95
95
|
original = ImagePath.new(original)
|
96
96
|
if result = optimize_image(original)
|
97
|
-
|
98
|
-
original.copy(temp)
|
99
|
-
temp.write(result.read)
|
100
|
-
temp.rename(original)
|
101
|
-
end
|
97
|
+
result.replace(original)
|
102
98
|
true
|
103
99
|
end
|
104
100
|
end
|
105
101
|
|
106
|
-
# Optimize multiple images
|
107
|
-
# yields path and result
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
yield path, result if block_given?
|
112
|
-
result
|
113
|
-
end
|
102
|
+
# Optimize multiple images
|
103
|
+
# if block given yields path and result for each image and returns array of yield results
|
104
|
+
# else return array of results
|
105
|
+
def optimize_images(paths, &block)
|
106
|
+
run_method_for(paths, :optimize_image, &block)
|
114
107
|
end
|
115
108
|
|
116
|
-
# Optimize multiple images in place
|
117
|
-
# yields path and result
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
yield path, result if block_given?
|
122
|
-
result
|
123
|
-
end
|
109
|
+
# Optimize multiple images in place
|
110
|
+
# if block given yields path and result for each image and returns array of yield results
|
111
|
+
# else return array of results
|
112
|
+
def optimize_images!(paths, &block)
|
113
|
+
run_method_for(paths, :optimize_image!, &block)
|
124
114
|
end
|
125
115
|
|
126
116
|
# Optimization methods with default options
|
@@ -138,8 +128,21 @@ class ImageOptim
|
|
138
128
|
|
139
129
|
private
|
140
130
|
|
131
|
+
def run_method_for(paths, method_name, &block)
|
132
|
+
method = method(method_name)
|
133
|
+
apply_threading(paths).map do |path|
|
134
|
+
path = ImagePath.new(path)
|
135
|
+
result = method.call(path)
|
136
|
+
if block
|
137
|
+
block.call(path, result)
|
138
|
+
else
|
139
|
+
result
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
141
144
|
def apply_threading(array)
|
142
|
-
if threads > 1
|
145
|
+
if threads > 1
|
143
146
|
array.in_threads(threads)
|
144
147
|
else
|
145
148
|
array
|
@@ -14,6 +14,16 @@ class ImageOptim
|
|
14
14
|
FileUtils.copy_file(self, dst, true)
|
15
15
|
end
|
16
16
|
|
17
|
+
# Atomic replace src with self
|
18
|
+
def replace(src)
|
19
|
+
src = self.class.new(src)
|
20
|
+
src.temp_path(src.dirname) do |temp|
|
21
|
+
src.copy(temp)
|
22
|
+
temp.write(read)
|
23
|
+
temp.rename(src)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
17
27
|
# Get format using ImageSize
|
18
28
|
def format
|
19
29
|
open{ |f| ImageSize.new(f) }.format
|
data/spec/image_optim_spec.rb
CHANGED
@@ -8,12 +8,8 @@ image_dir = spec_dir / 'images'
|
|
8
8
|
def temp_copy_path(original)
|
9
9
|
original.class.temp_dir do |dir|
|
10
10
|
temp_path = dir / original.basename
|
11
|
-
|
12
|
-
|
13
|
-
yield temp_path
|
14
|
-
ensure
|
15
|
-
temp_path.unlink if temp_path.exist?
|
16
|
-
end
|
11
|
+
original.copy(temp_path)
|
12
|
+
yield temp_path
|
17
13
|
end
|
18
14
|
end
|
19
15
|
|
@@ -117,4 +113,45 @@ describe ImageOptim do
|
|
117
113
|
end
|
118
114
|
end
|
119
115
|
end
|
116
|
+
|
117
|
+
describe "optimize multiple" do
|
118
|
+
let(:srcs){ ('a'..'z').to_a }
|
119
|
+
|
120
|
+
before do
|
121
|
+
srcs.each do |src|
|
122
|
+
ImageOptim::ImagePath.should_receive(:new).with(src).and_return(src)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
%w[optimize_images optimize_images!].each do |list_method|
|
127
|
+
single_method = list_method.sub('images', 'image')
|
128
|
+
describe "without block" do
|
129
|
+
it "should optimize images and return array of results" do
|
130
|
+
io = ImageOptim.new
|
131
|
+
dsts = []
|
132
|
+
srcs.each do |src|
|
133
|
+
dst = "#{src}_"
|
134
|
+
io.should_receive(single_method).with(src).and_return(dst)
|
135
|
+
dsts << dst
|
136
|
+
end
|
137
|
+
io.send(list_method, srcs).should == dsts
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "given block" do
|
142
|
+
it "should optimize images, yield path and result for each and return array of yield results" do
|
143
|
+
io = ImageOptim.new
|
144
|
+
srcs.each do |src|
|
145
|
+
io.should_receive(single_method).with(src).and_return("#{src}_")
|
146
|
+
end
|
147
|
+
results = []
|
148
|
+
io.send(list_method, srcs) do |src, dst|
|
149
|
+
result = "#{src} #{dst}"
|
150
|
+
results << "#{src} #{dst}"
|
151
|
+
result
|
152
|
+
end.should == results
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
120
157
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: image_optim
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ivan Kuchin
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-01-
|
18
|
+
date: 2012-01-12 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: fspath
|