smusher 0.3.8 → 0.4.2

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.
@@ -1,42 +1,26 @@
1
- Problem
2
- =======
3
- - Images are too large because they are not optimized
4
- - Users & your bandwidth is wasted for useless metadata
5
- - local image optimization requires tons of programs / libaries / knowledge
1
+ *LOSSLESS* image size reduction for jpg, png and gif in the cloud
6
2
 
7
- Solution
8
- ========
9
- - *LOSSLESS* size reduction (10-97% size reduction) in the cloud
10
- - optmizes all images(jpg+png+[gif]) from a given folder
3
+ - no image libraries needed, everything done in the interwebs
4
+ - less size (up 97% saving) = faster downloads = less bandwidth + happy users
11
5
 
12
6
  Install
13
7
  =======
14
8
  install ruby + rubygems
15
- sudo gem install httpclient smusher
9
+ sudo gem install smusher
16
10
 
17
11
  Usage
18
12
  =====
19
- Optimize a single image or a whole folder in the cloud.
20
-
21
- converting gif-s to png-s:
13
+ Optimize a single image or a whole folder:
22
14
 
23
- - called with a folder gif-s will not be converted
24
- - called on a single .gif or wildcard, image(s) will be converted if optimizeable
25
-
26
- Usage:
27
15
  smusher /apps/x/public/images [options]
28
16
  smusher /apps/x/public/images/x.png [options]
29
17
  smusher /apps/x/public/images/*.png [options]
30
18
 
31
- Options are:
19
+ Options:
32
20
  -q, --quiet no output
33
- -c, --convert-gifs convert all .gif`s in the given folder
34
-
35
-
36
- Protection
37
- ==========
38
- Any image that returns a failure code, is larger than before,
39
- or is empty will not be saved.
21
+ -c, --convert-gifs convert gifs to PNGs
22
+ --service PunyPng use PunyPng for image optimizing, instead of SmushIt
23
+ -v, --version display current version
40
24
 
41
25
  Example
42
26
  ======
@@ -52,14 +36,18 @@ Example
52
36
  reverted!
53
37
  ...
54
38
 
39
+ Protection
40
+ ==========
41
+ Any image that returns a failure code, is larger than before,
42
+ or is empty will not be saved.
43
+
55
44
  TODO
56
45
  ====
57
- - only optimize 'new' images -> save time when doing on each deploy
58
- - convert gifs to png, even if the new size is the same, for consistency (atm only those which get smaller are converted)
46
+ - only optimize 'new' images -> save time when doing on already optimized folder
59
47
 
60
- ALTERNATIVES
48
+ JS + CSS
61
49
  ============
62
- If you want to lossless reduce images and minify css + js, try [reduce](http://github.com/grosser/reduce).
50
+ reduce images and minify css + js -> try [reduce](http://github.com/grosser/reduce).
63
51
 
64
52
  Authors
65
53
  ======
data/Rakefile CHANGED
@@ -1,10 +1,6 @@
1
- desc "Run all specs in spec directory"
2
- task :default do |t|
3
- require 'spec'
4
- options = "--colour --format progress --loadby --reverse"
5
- files = FileList['spec/**/*_spec.rb']
6
- system("spec #{options} #{files}")
7
- end
1
+ task :default => :spec
2
+ require 'spec/rake/spectask'
3
+ Spec::Rake::SpecTask.new {|t| t.spec_opts = ['--color']}
8
4
 
9
5
  begin
10
6
  require 'jeweler'
@@ -16,16 +12,9 @@ begin
16
12
  gem.homepage = "http://github.com/grosser/#{project_name}"
17
13
  gem.authors = ["Michael Grosser"]
18
14
  %w[rake json httpclient].each{|d| gem.add_dependency d}
19
- gem.rubyforge_project = 'smusher'
20
15
  end
21
16
 
22
- # fake task so that rubyforge:release works
23
- task :rdoc do
24
- `mkdir rdoc`
25
- `echo documentation is at http://github.com/grosser/#{project_name} > rdoc/README.rdoc`
26
- end
27
-
28
- Jeweler::RubyforgeTasks.new
17
+ Jeweler::GemcutterTasks.new
29
18
  rescue LoadError
30
19
  puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
31
20
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.8
1
+ 0.4.2
@@ -1,35 +1,33 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'rubygems'
3
3
  require 'optparse'
4
+ require 'smusher'
4
5
 
5
6
  options = {}
6
7
  OptionParser.new do |opts|
7
8
  opts.banner = <<BANNER
8
9
  Optimize a single image or a whole folder in the cloud.
9
10
 
10
- gif`s:
11
- - called with a folder gif`s will not be optimized
12
- - called on a singe .gif, it will be optimized if it is optimizeable
13
-
14
11
  Usage:
15
12
  smusher /apps/x/public/images [options]
16
13
  smusher /apps/x/public/images/x.png [options]
17
14
  smusher /apps/x/public/images/*.png [options]
18
15
 
19
- Options are:
16
+ Options:
20
17
  BANNER
21
18
  opts.on("-q", "--quiet","no output") { options[:quiet]=true }
19
+ opts.on("--service S", String, "use service: PunyPng or default SmushIt") {|x| options[:service]=x }
22
20
  opts.on("-c", "--convert-gifs","convert all .gif`s in the given folder") { options[:convert_gifs]=true }
23
- opts.on("-h", "--help","Show this.") { puts opts;exit }
21
+ opts.on("-h", "--help","Show this.") { puts opts; exit }
22
+ opts.on('-v', '--version','Show Version'){ puts Smusher::VERSION; exit}
24
23
  end.parse!
25
24
 
26
25
  path = ARGV.first
27
26
  if path.to_s.empty? or not File.exist?(path)
28
- puts "Usage instructions: autotest --help"
27
+ puts "Usage instructions: smusher --help"
29
28
  exit
30
29
  end
31
30
 
32
- require 'smusher'
33
31
  if File.directory?(path)
34
32
  Smusher.optimize_images_in_folder(path,options)
35
33
  else
@@ -4,26 +4,34 @@ require 'json'
4
4
  require 'open-uri'
5
5
  require 'httpclient'
6
6
 
7
+ require 'smusher/smush_it'
8
+ require 'smusher/puny_png'
9
+
7
10
  module Smusher
8
11
  extend self
9
12
 
10
13
  MINIMUM_IMAGE_SIZE = 20#byte
11
14
 
15
+ VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
16
+
12
17
  # optimize the given image
13
18
  # converts gif to png, if size is lower
14
19
  # can be called with a file-path or an array of files-paths
15
20
  def optimize_image(files,options={})
21
+ service = options[:service] || 'SmushIt'
22
+ service = eval(service)
23
+
16
24
  files.each do |file|
17
25
  check_options(options)
18
26
  puts "THIS FILE IS EMPTY!!! #{file}" and return if size(file).zero?
19
27
  success = false
20
28
 
21
29
  with_logging(file,options[:quiet]) do
22
- write_optimized_data(file)
30
+ write_optimized_data(file, service)
23
31
  success = true
24
32
  end
25
33
 
26
- if success
34
+ if success and service.converts_gif_to_png?
27
35
  gif = /\.gif$/
28
36
  `mv #{file} #{file.sub(gif,'.png')}` if file =~ gif
29
37
  end
@@ -31,30 +39,30 @@ module Smusher
31
39
  end
32
40
 
33
41
  # fetch all jpg/png images from given folder and optimize them
34
- def optimize_images_in_folder(folder,options={})
42
+ def optimize_images_in_folder(folder, options={})
35
43
  check_options(options)
36
- images_in_folder(folder,options[:convert_gifs]).each do |file|
37
- optimize_image(file)
44
+ images_in_folder(folder, options[:convert_gifs]).each do |file|
45
+ optimize_image(file, options)
38
46
  end
39
47
  end
40
48
 
41
- private
49
+ private
42
50
 
43
51
  def check_options(options)
44
- known_options = [:convert_gifs,:quiet]
52
+ known_options = [:convert_gifs, :quiet, :service]
45
53
  if options.detect{|k,v| not known_options.include?(k)}
46
54
  raise "Known options: #{known_options*' '}"
47
55
  end
48
56
  end
49
57
 
50
- def write_optimized_data(file)
51
- optimized = optimized_image_data_for(file)
58
+ def write_optimized_data(file, service)
59
+ optimized = service.optimized_image_data_for(file)
52
60
 
53
61
  raise "Error: got larger" if size(file) < optimized.size
54
62
  raise "Error: empty file downloaded" if optimized.size < MINIMUM_IMAGE_SIZE
55
63
  raise "cannot be optimized further" if size(file) == optimized.size
56
64
 
57
- File.open(file,'w') {|f| f.puts optimized}
65
+ File.open(file,'w') {|f| f.write optimized}
58
66
  end
59
67
 
60
68
  def sanitize_folder(folder)
@@ -86,19 +94,4 @@ private
86
94
  puts ''
87
95
  end
88
96
  end
89
-
90
- def optimized_image_data_for(file)
91
- #I leave these urls here, just in case it stops working again...
92
- # url = "http://smush.it/ws.php" # original, redirects to somewhere else..
93
- url = 'http://ws1.adq.ac4.yahoo.com/ysmush.it/ws.php'
94
- # url = "http://developer.yahoo.com/yslow/smushit/ws.php" # official but does not work
95
- # url = "http://smushit.com/ysmush.it/ws.php" # used at the new page but does not hande uploads
96
- # url = "http://smushit.eperf.vip.ac4.yahoo.com/ysmush.it/ws.php" # used at the new page but does not hande uploads
97
- response = HTTPClient.post url, { 'files[]' => File.new(file) }
98
- response = JSON.parse(response.body.content)
99
- raise "smush.it: #{response['error']}" if response['error']
100
- image_url = response['dest']
101
- raise "no dest path found" unless image_url
102
- open(image_url) { |source| source.read() }
103
- end
104
- end
97
+ end
@@ -0,0 +1,17 @@
1
+ module Smusher
2
+ class PunyPng
3
+ def self.converts_gif_to_png?
4
+ false
5
+ end
6
+
7
+ def self.optimized_image_data_for(file)
8
+ url = 'http://www.gracepointafterfive.com/punypng_staging/api/optimize'
9
+ response = HTTPClient.post url, { 'img' => File.new(file), 'key' => 'd1b72ab4813da6b69e1d6018303ac690c014599d'}
10
+ response = JSON.parse(response.body.content)
11
+ raise "puny_png: #{response['error']}" if response['error']
12
+ image_url = response['optimized_url']
13
+ raise "no optimized_url found" unless image_url
14
+ open(image_url) { |source| source.read() }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Smusher
2
+ class SmushIt
3
+ def self.converts_gif_to_png?
4
+ true
5
+ end
6
+
7
+ def self.optimized_image_data_for(file)
8
+ #I leave these urls here, just in case it stops working again...
9
+ # url = "http://smush.it/ws.php" # original, redirects to somewhere else..
10
+ # url = "http://developer.yahoo.com/yslow/smushit/ws.php" # official but does not work
11
+ # url = "http://smushit.com/ysmush.it/ws.php" # used at the new page but does not hande uploads
12
+ # url = "http://smushit.eperf.vip.ac4.yahoo.com/ysmush.it/ws.php" # used at the new page but does not hande uploads
13
+ url = 'http://ws1.adq.ac4.yahoo.com/ysmush.it/ws.php'
14
+ response = HTTPClient.post url, { 'files[]' => File.new(file)}
15
+ response = JSON.parse(response.body.content)
16
+ raise "smush.it: #{response['error']}" if response['error']
17
+ image_url = response['dest']
18
+ raise "no dest path found" unless image_url
19
+ open(image_url) { |source| source.read() }
20
+ end
21
+ end
22
+ end
@@ -1,15 +1,15 @@
1
1
  # Generated by jeweler
2
- # DO NOT EDIT THIS FILE
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{smusher}
8
- s.version = "0.3.8"
8
+ s.version = "0.4.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Michael Grosser"]
12
- s.date = %q{2009-10-05}
12
+ s.date = %q{2009-11-22}
13
13
  s.default_executable = %q{smusher}
14
14
  s.email = %q{grosser.michael@gmail.com}
15
15
  s.executables = ["smusher"]
@@ -23,6 +23,8 @@ Gem::Specification.new do |s|
23
23
  "VERSION",
24
24
  "bin/smusher",
25
25
  "lib/smusher.rb",
26
+ "lib/smusher/puny_png.rb",
27
+ "lib/smusher/smush_it.rb",
26
28
  "smusher.gemspec",
27
29
  "spec/empty/.gitignore",
28
30
  "spec/images/ad.gif",
@@ -35,19 +37,23 @@ Gem::Specification.new do |s|
35
37
  "spec/out/ad.gif",
36
38
  "spec/out/people.jpg",
37
39
  "spec/reduced/add.png",
40
+ "spec/reduced/add_puny.png",
38
41
  "spec/reduced/fam.png",
42
+ "spec/smusher/puny_png_spec.rb",
43
+ "spec/smusher/smush_it_spec.rb",
39
44
  "spec/smusher_spec.rb",
40
45
  "spec/spec_helper.rb"
41
46
  ]
42
47
  s.homepage = %q{http://github.com/grosser/smusher}
43
48
  s.rdoc_options = ["--charset=UTF-8"]
44
49
  s.require_paths = ["lib"]
45
- s.rubyforge_project = %q{smusher}
46
50
  s.rubygems_version = %q{1.3.5}
47
51
  s.summary = %q{Automatic Lossless Reduction Of All Your Images}
48
52
  s.test_files = [
49
53
  "spec/spec_helper.rb",
50
- "spec/smusher_spec.rb"
54
+ "spec/smusher_spec.rb",
55
+ "spec/smusher/puny_png_spec.rb",
56
+ "spec/smusher/smush_it_spec.rb"
51
57
  ]
52
58
 
53
59
  if s.respond_to? :specification_version then
@@ -69,3 +75,4 @@ Gem::Specification.new do |s|
69
75
  s.add_dependency(%q<httpclient>, [">= 0"])
70
76
  end
71
77
  end
78
+
Binary file
@@ -0,0 +1,12 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe Smusher::PunyPng do
4
+ describe :optimized_image_data_for do
5
+ it "loads the reduced image" do
6
+ original = File.join(ROOT,'images','add.png')
7
+ reduced = File.read(File.join(ROOT, 'reduced', 'add_puny.png'))
8
+ received = Smusher::PunyPng.optimized_image_data_for(original)
9
+ received.should == reduced
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe Smusher::SmushIt do
4
+ describe :optimized_image_data_for do
5
+ it "loads the reduced image" do
6
+ original = File.join(ROOT,'images','add.png')
7
+ reduced = File.open(File.join(ROOT,'reduced','add.png')).read
8
+ received = Smusher::SmushIt.optimized_image_data_for(original)
9
+ received.should == reduced
10
+ end
11
+ end
12
+ end
@@ -1,5 +1,4 @@
1
- ROOT = File.expand_path(File.dirname(__FILE__))
2
- require File.join(ROOT,"spec_helper")
1
+ require 'spec/spec_helper'
3
2
 
4
3
  describe :smusher do
5
4
  def copy(image_name)
@@ -28,6 +27,14 @@ describe :smusher do
28
27
  size.should < original_size
29
28
  end
30
29
 
30
+ it "does not append newline" do
31
+ copy 'add.png'
32
+ file = File.join(@out, 'add.png')
33
+ Smusher.optimize_image file
34
+ # pure File.read() will omit trailing \n
35
+ File.readlines(file).last.split('').last.should_not == "\n"
36
+ end
37
+
31
38
  it "can be called with an array of files" do
32
39
  original_size = size
33
40
  Smusher.optimize_image([@file])
@@ -36,21 +43,21 @@ describe :smusher do
36
43
 
37
44
  it "it does nothing if size stayed the same" do
38
45
  original_size = size
39
- Smusher.expects(:optimized_image_data_for).returns File.read(@file)
46
+ Smusher::SmushIt.should_receive(:optimized_image_data_for).and_return File.read(@file)
40
47
  Smusher.optimize_image(@file)
41
48
  size.should == original_size
42
49
  end
43
50
 
44
51
  it "does not save images whoes size got larger" do
45
52
  original_size = size
46
- Smusher.expects(:optimized_image_data_for).returns File.read(@file)*2
53
+ Smusher::SmushIt.should_receive(:optimized_image_data_for).and_return File.read(@file)*2
47
54
  Smusher.optimize_image(@file)
48
55
  size.should == original_size
49
56
  end
50
57
 
51
58
  it "does not save images if their size is error-sugesting-small" do
52
59
  original_size = size
53
- Smusher.expects(:optimized_image_data_for).returns 'oops...'
60
+ Smusher::SmushIt.should_receive(:optimized_image_data_for).and_return 'oops...'
54
61
  Smusher.optimize_image(@file)
55
62
  size.should == original_size
56
63
  end
@@ -62,7 +69,8 @@ describe :smusher do
62
69
  @file_png = File.join(@out,'logo.png')
63
70
  end
64
71
 
65
- pending_it "converts gifs to png even if they have the same size" do
72
+ it "converts gifs to png even if they have the same size" do
73
+ pending
66
74
  copy 'ad.gif'
67
75
  file = File.join(@out,'ad.gif')
68
76
  original_size = size
@@ -77,7 +85,7 @@ describe :smusher do
77
85
  end
78
86
 
79
87
  it "does not rename gifs, if optimizing failed" do
80
- Smusher.expects(:optimized_image_data_for).returns File.read(@file)
88
+ Smusher::SmushIt.should_receive(:optimized_image_data_for).and_return File.read(@file)
81
89
  Smusher.optimize_image(@file)
82
90
  File.exist?(@file).should == true
83
91
  File.exist?(@file_png).should == false
@@ -86,7 +94,7 @@ describe :smusher do
86
94
 
87
95
  describe 'options' do
88
96
  it "does not produce output when :quiet is given" do
89
- $stdout.expects(:write).never
97
+ $stdout.should_receive(:write).never
90
98
  Smusher.optimize_image(@file,:quiet=>true)
91
99
  end
92
100
 
@@ -154,7 +162,7 @@ describe :smusher do
154
162
  Smusher.send(:size,@file).should == File.size(@file)
155
163
  end
156
164
 
157
- it "returns 0 for missing file" do
165
+ it "and_return 0 for missing file" do
158
166
  Smusher.send(:size,File.join(ROOT,'xxxx','dssdfsddfs')).should == 0
159
167
  end
160
168
  end
@@ -167,12 +175,7 @@ describe :smusher do
167
175
  end
168
176
  end
169
177
 
170
- describe :optimized_image_data_for do
171
- it "loads the reduced image" do
172
- original = File.join(ROOT,'images','add.png')
173
- reduced = File.open(File.join(ROOT,'reduced','add.png')).read
174
- received = (Smusher.send(:optimized_image_data_for,original))
175
- received.should == reduced
176
- end
178
+ it "has a VERSION" do
179
+ Smusher::VERSION.should =~ /^\d+\.\d+\.\d+$/
177
180
  end
178
- end
181
+ end
@@ -1,19 +1,8 @@
1
1
  # ---- requirements
2
2
  require 'rubygems'
3
3
  require 'spec'
4
- require 'mocha'
5
- require 'lib/smusher'
6
4
 
5
+ $LOAD_PATH << 'lib'
6
+ require 'smusher'
7
7
 
8
- # ---- rspec
9
- Spec::Runner.configure do |config|
10
- config.mock_with :mocha
11
- end
12
-
13
-
14
- # ---- Helpers
15
- def pending_it(text,&block)
16
- it text do
17
- pending(&block)
18
- end
19
- end
8
+ ROOT = File.expand_path(File.dirname(__FILE__))
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smusher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-05 00:00:00 +02:00
12
+ date: 2009-11-22 00:00:00 +01:00
13
13
  default_executable: smusher
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -57,6 +57,8 @@ files:
57
57
  - VERSION
58
58
  - bin/smusher
59
59
  - lib/smusher.rb
60
+ - lib/smusher/puny_png.rb
61
+ - lib/smusher/smush_it.rb
60
62
  - smusher.gemspec
61
63
  - spec/empty/.gitignore
62
64
  - spec/images/ad.gif
@@ -69,7 +71,10 @@ files:
69
71
  - spec/out/ad.gif
70
72
  - spec/out/people.jpg
71
73
  - spec/reduced/add.png
74
+ - spec/reduced/add_puny.png
72
75
  - spec/reduced/fam.png
76
+ - spec/smusher/puny_png_spec.rb
77
+ - spec/smusher/smush_it_spec.rb
73
78
  - spec/smusher_spec.rb
74
79
  - spec/spec_helper.rb
75
80
  has_rdoc: true
@@ -95,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
100
  version:
96
101
  requirements: []
97
102
 
98
- rubyforge_project: smusher
103
+ rubyforge_project:
99
104
  rubygems_version: 1.3.5
100
105
  signing_key:
101
106
  specification_version: 3
@@ -103,3 +108,5 @@ summary: Automatic Lossless Reduction Of All Your Images
103
108
  test_files:
104
109
  - spec/spec_helper.rb
105
110
  - spec/smusher_spec.rb
111
+ - spec/smusher/puny_png_spec.rb
112
+ - spec/smusher/smush_it_spec.rb