image_optim 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- lines = paths.map do |path|
55
- before = File.size(path)
56
- result = io.optimize_image!(path)
57
- after = File.size(path)
58
- "#{result ? '%5.2f%%' % (100.0 * after / before) : '--.--%'} #{path}"
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
- puts lines
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'image_optim'
5
- s.version = '0.2.1'
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']
@@ -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
- original.temp_path(original.dirname) do |temp|
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, returning list of results
107
- # yields path and result if block given
108
- def optimize_images(paths)
109
- apply_threading(paths).map do |path|
110
- result = optimize_image(path)
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, returning list of results
117
- # yields path and result if block given
118
- def optimize_images!(paths)
119
- apply_threading(paths).map do |path|
120
- result = optimize_image!(path)
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 && array.length > 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
@@ -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
- begin
12
- original.copy(temp_path)
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: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 1
10
- version: 0.2.1
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-10 00:00:00 Z
18
+ date: 2012-01-12 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: fspath