ryankshaw-image_crush 0.0.1

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Ryan Shaw
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ = image_crush
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but
13
+ bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2009 Ryan Shaw. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "image_crush"
8
+ gem.summary = %Q{crush your images}
9
+ gem.description = %Q{crush your images}
10
+ gem.email = "ryankshaw@gmail.com"
11
+ gem.homepage = "http://github.com/ryankshaw/image_crush"
12
+ gem.authors = ["Ryan Shaw"]
13
+ gem.add_development_dependency "rspec"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
18
+ end
19
+
20
+ require 'spec/rake/spectask'
21
+ Spec::Rake::SpecTask.new(:spec) do |spec|
22
+ spec.libs << 'lib' << 'spec'
23
+ spec.spec_files = FileList['spec/**/*_spec.rb']
24
+ end
25
+
26
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.pattern = 'spec/**/*_spec.rb'
29
+ spec.rcov = true
30
+ end
31
+
32
+ task :spec => :check_dependencies
33
+
34
+ task :default => :spec
35
+
36
+ require 'rake/rdoctask'
37
+ Rake::RDocTask.new do |rdoc|
38
+ if File.exist?('VERSION')
39
+ version = File.read('VERSION')
40
+ else
41
+ version = ""
42
+ end
43
+
44
+ rdoc.rdoc_dir = 'rdoc'
45
+ rdoc.title = "image_crush #{version}"
46
+ rdoc.rdoc_files.include('README*')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/bin/image_crush ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'image_crush'
4
+
5
+ if ( ARGV.empty? || (%w(--help -h).include? ARGV.first) )
6
+ puts "Usage: image_crush [directory_or_file_name] \n"
7
+ puts "ex: \n"
8
+ puts "image_crush bloatedimage.png \n"
9
+ puts "image_crush public/images <-- for a rails app, at the RAILS_ROOT directory"
10
+ exit(0)
11
+ end
12
+ # if 1 then just image crush that thing, it could be a directory or a file
13
+ if ARGV.length == 1
14
+ ic = ImageCrush::Base.new
15
+ ic.extend ImageCrush::Messages
16
+ ic.crush(ARGV[0])
17
+ elsif
18
+ puts "Sorry, something went wrong"
19
+ end
@@ -0,0 +1,67 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{image_crush}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ryan Shaw"]
12
+ s.date = %q{2009-08-06}
13
+ s.default_executable = %q{image_crush}
14
+ s.description = %q{crush your images}
15
+ s.email = %q{ryankshaw@gmail.com}
16
+ s.executables = ["image_crush"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".gitignore",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "bin/image_crush",
29
+ "image_crush.gemspec",
30
+ "lib/image_crush.rb",
31
+ "lib/image_crush/base.rb",
32
+ "lib/image_crush/jpegtran.rb",
33
+ "lib/image_crush/messages.rb",
34
+ "lib/image_crush/pngcrush.rb",
35
+ "spec/fixtures/README.txt",
36
+ "spec/fixtures/dice.png",
37
+ "spec/fixtures/happy-mailman.jpg",
38
+ "spec/fixtures/subdir/cc-by-icon.png",
39
+ "spec/image_crush/messages_spec.rb",
40
+ "spec/image_crush_spec.rb",
41
+ "spec/spec_helper.rb",
42
+ "tasks/image_crush.rake"
43
+ ]
44
+ s.homepage = %q{http://github.com/ryankshaw/image_crush}
45
+ s.rdoc_options = ["--charset=UTF-8"]
46
+ s.require_paths = ["lib"]
47
+ s.rubygems_version = %q{1.3.5}
48
+ s.summary = %q{crush your images}
49
+ s.test_files = [
50
+ "spec/image_crush/messages_spec.rb",
51
+ "spec/image_crush_spec.rb",
52
+ "spec/spec_helper.rb"
53
+ ]
54
+
55
+ if s.respond_to? :specification_version then
56
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
57
+ s.specification_version = 3
58
+
59
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
60
+ s.add_development_dependency(%q<rspec>, [">= 0"])
61
+ else
62
+ s.add_dependency(%q<rspec>, [">= 0"])
63
+ end
64
+ else
65
+ s.add_dependency(%q<rspec>, [">= 0"])
66
+ end
67
+ end
@@ -0,0 +1,61 @@
1
+ module ImageCrush
2
+
3
+ class Base
4
+
5
+ def crush(path)
6
+ raise ImageCrush::InputFileNotFound unless File.exist?(path)
7
+
8
+ if File.file?(path)
9
+ crush_file(path)
10
+ elsif File.directory?(path)
11
+ crush_dir(path)
12
+ else
13
+ raise "I don't know what to do with #{path}"
14
+ end
15
+ end
16
+
17
+ def crush_file(path)
18
+ processor = select_processor(path) or return nil
19
+ source_path = copy_to_tempdir(path)
20
+ crushed_path = source_path + '.crushed'
21
+ processor.crush(source_path, crushed_path)
22
+ FileUtils.cp(crushed_path, path) if File.exist?(crushed_path)
23
+ end
24
+
25
+ def crush_dir(path)
26
+ Dir.foreach(path) do |entry|
27
+ next if %w{. ..}.include?(entry)
28
+ crush(File.join(path, entry))
29
+ end
30
+ end
31
+
32
+ def select_processor(path)
33
+ case path
34
+ when /\.png$/i
35
+ Pngcrush
36
+ when /\.jpe?g$/i
37
+ Jpegtran
38
+ end
39
+ end
40
+
41
+ def copy_to_tempdir(path)
42
+ wdir = tmpdir
43
+ FileUtils.cp_r path, wdir
44
+ File.join(wdir, File.basename(path))
45
+ end
46
+
47
+ def tmpdir
48
+ ret = File.join(Dir.tmpdir, 'image_crush')
49
+ FileUtils.mkdir_p(ret)
50
+ ret
51
+ end
52
+
53
+ end # Base
54
+
55
+ class CrushToolNotAvailable < StandardError
56
+ end
57
+
58
+ class InputFileNotFound < StandardError
59
+ end
60
+
61
+ end
@@ -0,0 +1,15 @@
1
+ module ImageCrush
2
+
3
+ module Jpegtran
4
+ def self.available?
5
+ @@available ||= ! %x{which jpegtran}.empty?
6
+ end
7
+
8
+ def self.crush(inpath, outpath)
9
+ raise ImageCrush::CrushToolNotAvailable unless available?
10
+ %x{jpegtran -copy none -optimize #{inpath} > #{outpath}}
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,33 @@
1
+ module ImageCrush
2
+
3
+ module Messages
4
+
5
+ def self.extended(base)
6
+ class << base
7
+ def crush_file_with_messages(path)
8
+ size_before = File.open(path){|f| f.stat.size}
9
+ print "#{path}: #{human_size size_before}"
10
+ crush_file_without_messages(path)
11
+ size_after = File.open(path){|f| f.stat.size}
12
+ ratio = sprintf('%0.2f', size_after.to_f*100/size_before)
13
+ puts " -> #{human_size size_after} (#{ratio}%)"
14
+ end
15
+ alias :crush_file_without_messages :crush_file
16
+ alias :crush_file :crush_file_with_messages
17
+
18
+
19
+ private
20
+
21
+ def human_size(bytes)
22
+ if bytes < 1024
23
+ bytes.to_s + 'B'
24
+ else
25
+ sprintf("%0.1fK", bytes.to_f/1024).gsub('.0K', 'K')
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,15 @@
1
+ module ImageCrush
2
+
3
+ module Pngcrush
4
+ def self.available?
5
+ @@available ||= ! %x{which pngcrush}.empty?
6
+ end
7
+
8
+ def self.crush(inpath, outpath)
9
+ raise ImageCrush::CrushToolNotAvailable unless available?
10
+ %x{pngcrush -rem alla -brute -reduce #{inpath} #{outpath}}
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,14 @@
1
+ module ImageCrush
2
+ LIB_ROOT = File.dirname(__FILE__)
3
+ end
4
+
5
+ base_dir = File.join(ImageCrush::LIB_ROOT, 'image_crush')
6
+ require File.join(base_dir, 'base')
7
+ require File.join(base_dir, 'pngcrush')
8
+ require File.join(base_dir, 'jpegtran')
9
+ require File.join(base_dir, 'messages')
10
+ load File.join(File.dirname(ImageCrush::LIB_ROOT), 'tasks', 'image_crush.rake')
11
+
12
+ def ImageCrush(path)
13
+ ImageCrush::Base.new.crush(path)
14
+ end
@@ -0,0 +1,16 @@
1
+ *** dice.png ***
2
+ retrieved from http://en.wikipedia.org/wiki/File:PNG_transparency_demonstration_1.png
3
+ on 27-07-2009
4
+ licensed under CC-BY-SA (http://creativecommons.org/licenses/by-sa/3.0)
5
+
6
+ *** subdir/cc-by-icon.png ***
7
+ retrieved from http://commons.wikimedia.org/wiki/File:Cc-by-icon.png
8
+ on 28-07-2009
9
+ licensed under CC-BY (http://creativecommons.org/licenses/by/3.0)
10
+
11
+ *** happy-mailman.jpg ***
12
+ original title: http://www.eastmanhouse.org/flickr/statement.php
13
+ by Nickolas Muray
14
+ retrieved from http://www.flickr.com/photos/george_eastman_house/3122865907/
15
+ on 05-07-2009
16
+ no known copyright restrictions, see http://www.eastmanhouse.org/flickr/statement.php
Binary file
Binary file
Binary file
@@ -0,0 +1,20 @@
1
+ require File.join(File.dirname(File.dirname(__FILE__)), 'spec_helper')
2
+
3
+ require 'tmpdir'
4
+ require 'fileutils'
5
+
6
+ describe ImageCrush::Messages do
7
+
8
+ it 'prints a message to stdout after crushing a file' do
9
+ mock_stdout = ''
10
+ ic = ImageCrush::Base.new
11
+ ic.extend ImageCrush::Messages
12
+ ic.stub!(:print).and_return{|str| mock_stdout << str }
13
+ ic.stub!(:puts).and_return{|str| mock_stdout << str << "\n" }
14
+ path = copy_file_to_tmpdir(DICE_PATH)
15
+ ic.crush(path)
16
+ mock_stdout.should == "#{path}: 9K -> 8.9K (98.88%)\n"
17
+ end
18
+
19
+ end
20
+
@@ -0,0 +1,90 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ require 'tmpdir'
4
+ require 'fileutils'
5
+
6
+ describe "ImageCrush" do
7
+
8
+ before :each do
9
+ remove_any_work_dirs
10
+ end
11
+
12
+ after :all do
13
+ remove_any_work_dirs
14
+ end
15
+
16
+ describe 'exception conditions' do
17
+ it 'should fail when the input file does not exist' do
18
+ lambda{ImageCrush('/path/to/nowhere/fast')}.should raise_error(ImageCrush::InputFileNotFound)
19
+ end
20
+
21
+ it 'should fail when the crush tool is not available' do
22
+ ImageCrush::Pngcrush.should_receive(:available?).and_return(false)
23
+ lambda{ImageCrush(DICE_PATH)}.should raise_error(ImageCrush::CrushToolNotAvailable)
24
+ end
25
+ end
26
+
27
+ describe 'individual files' do
28
+ it 'should crush PNG files' do
29
+ image_path = copy_file_to_tmpdir(DICE_PATH)
30
+ should_reduce_size_of_image(image_path)
31
+ end
32
+
33
+ it 'should crush JPEG files' do
34
+ image_path = copy_file_to_tmpdir(MMAN_PATH)
35
+ should_reduce_size_of_image(image_path)
36
+ end
37
+
38
+ it 'should ignore other files' do
39
+ path = copy_file_to_tmpdir(README_PATH)
40
+ ImageCrush(path)
41
+ File.exist?('/tmp/image_crush/README.txt').should == false
42
+ end
43
+
44
+
45
+ private
46
+
47
+ def should_reduce_size_of_image(path)
48
+ f = File.open(path)
49
+ size_before = f.stat.size
50
+ f.close
51
+ ImageCrush(path)
52
+ f = File.open(path)
53
+ size_after = f.stat.size
54
+ f.close
55
+ size_after.should < size_before
56
+ end
57
+ end
58
+
59
+
60
+ describe 'recursive crush' do
61
+ before do
62
+ create_work_dir
63
+ FileUtils.cp_r Dir.glob(File.join(FIXTURES_ROOT, '**')), @work_dir
64
+ @dice_path = File.join(@work_dir, File.basename(DICE_PATH))
65
+ @ccby_path = File.join(@work_dir, 'subdir', File.basename(CCBY_PATH))
66
+ end
67
+
68
+ it 'should recursively crush all images in a directory' do
69
+ f1 = File.open(@dice_path)
70
+ size1_before = f1.stat.size
71
+ f1.close
72
+ f2 = File.open(@ccby_path)
73
+ size2_before = f2.stat.size
74
+ f2.close
75
+
76
+ ImageCrush(@work_dir)
77
+
78
+ f1 = File.open(@dice_path)
79
+ size1_after = f1.stat.size
80
+ f1.close
81
+ f2 = File.open(@ccby_path)
82
+ size2_after = f2.stat.size
83
+ f2.close
84
+
85
+ size1_after.should < size1_before
86
+ size2_after.should < size2_before
87
+ end
88
+ end
89
+
90
+ end
@@ -0,0 +1,30 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'image_crush'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
10
+
11
+ FIXTURES_ROOT = File.join(File.dirname(__FILE__), 'fixtures')
12
+ DICE_PATH = File.join(FIXTURES_ROOT, 'dice.png')
13
+ CCBY_PATH = File.join(FIXTURES_ROOT, 'subdir', 'cc-by-icon.png')
14
+ MMAN_PATH = File.join(FIXTURES_ROOT, 'happy-mailman.jpg')
15
+ README_PATH = File.join(FIXTURES_ROOT, 'README.txt')
16
+
17
+ def copy_file_to_tmpdir(path)
18
+ create_work_dir
19
+ FileUtils.cp path, @work_dir
20
+ File.join(@work_dir, path.gsub(FIXTURES_ROOT, ''))
21
+ end
22
+
23
+ def remove_any_work_dirs
24
+ Dir.glob(File.join(Dir.tmpdir, 'image_crush.spec.*')).each{|path| FileUtils.rm_rf(path)}
25
+ end
26
+
27
+ def create_work_dir
28
+ @work_dir = File.join(Dir.tmpdir, 'image_crush.spec.' + $$.to_s)
29
+ FileUtils.mkdir_p(@work_dir)
30
+ end
@@ -0,0 +1,16 @@
1
+ require 'rake'
2
+ require 'tmpdir'
3
+
4
+ desc 'Reduce the size of PNG and JPEG files where possible'
5
+ task :crush => 'crush:all'
6
+
7
+ namespace :crush do
8
+ task :all do
9
+ require File.join(File.dirname(__FILE__), "lib/image_crush.rb")
10
+ ic = ImageCrush::Base.new
11
+ ic.extend ImageCrush::Messages
12
+ ic.crush(File.join(RAILS_ROOT, 'public', 'images'))
13
+ end
14
+
15
+ task :default => :all
16
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ryankshaw-image_crush
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Shaw
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-06 00:00:00 -07:00
13
+ default_executable: image_crush
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: crush your images
26
+ email: ryankshaw@gmail.com
27
+ executables:
28
+ - image_crush
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.rdoc
39
+ - Rakefile
40
+ - VERSION
41
+ - bin/image_crush
42
+ - image_crush.gemspec
43
+ - lib/image_crush.rb
44
+ - lib/image_crush/base.rb
45
+ - lib/image_crush/jpegtran.rb
46
+ - lib/image_crush/messages.rb
47
+ - lib/image_crush/pngcrush.rb
48
+ - spec/fixtures/README.txt
49
+ - spec/fixtures/dice.png
50
+ - spec/fixtures/happy-mailman.jpg
51
+ - spec/fixtures/subdir/cc-by-icon.png
52
+ - spec/image_crush/messages_spec.rb
53
+ - spec/image_crush_spec.rb
54
+ - spec/spec_helper.rb
55
+ - tasks/image_crush.rake
56
+ has_rdoc: false
57
+ homepage: http://github.com/ryankshaw/image_crush
58
+ licenses:
59
+ post_install_message:
60
+ rdoc_options:
61
+ - --charset=UTF-8
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ requirements: []
77
+
78
+ rubyforge_project:
79
+ rubygems_version: 1.3.5
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: crush your images
83
+ test_files:
84
+ - spec/image_crush/messages_spec.rb
85
+ - spec/image_crush_spec.rb
86
+ - spec/spec_helper.rb