image_genie 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest ADDED
@@ -0,0 +1,12 @@
1
+ Manifest
2
+ README.rdoc
3
+ Rakefile
4
+ lib/image_genie.rb
5
+ lib/image_genie/base.rb
6
+ lib/image_genie/command.rb
7
+ lib/image_genie/convert.rb
8
+ lib/image_genie/identify.rb
9
+ lib/image_genie/montage.rb
10
+ lib/image_genie/verify.rb
11
+ test/test_helper.rb
12
+ test/unit/base_test.rb
data/README.rdoc ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+ require 'rake/testtask'
5
+
6
+ Echoe.new('image_genie', '0.2.0') do |p|
7
+ p.description = "ImageGenie - Simple Wrapper for command line ImageMagick"
8
+ p.url = "https://github.com/bkimble"
9
+ p.author = "Billy Kimble"
10
+ p.email = "basslines@gmail.com"
11
+ p.ignore_pattern = ["tmp/*", "script/*"]
12
+ p.development_dependencies = ["open4"]
13
+ end
14
+
15
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
16
+
17
+ Rake::TestTask.new(:test) do |t|
18
+ t.libs << 'test'
19
+ t.test_files = FileList['test/**/*_test.rb']
20
+ t.verbose = true
21
+ end
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{image_genie}
5
+ s.version = "0.2.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Billy Kimble"]
9
+ s.date = %q{2011-02-08}
10
+ s.description = %q{ImageGenie - Simple Wrapper for command line ImageMagick}
11
+ s.email = %q{basslines@gmail.com}
12
+ s.extra_rdoc_files = ["README.rdoc", "lib/image_genie.rb", "lib/image_genie/base.rb", "lib/image_genie/command.rb", "lib/image_genie/convert.rb", "lib/image_genie/identify.rb", "lib/image_genie/montage.rb", "lib/image_genie/verify.rb"]
13
+ s.files = ["Manifest", "README.rdoc", "Rakefile", "lib/image_genie.rb", "lib/image_genie/base.rb", "lib/image_genie/command.rb", "lib/image_genie/convert.rb", "lib/image_genie/identify.rb", "lib/image_genie/montage.rb", "lib/image_genie/verify.rb", "test/test_helper.rb", "test/unit/base_test.rb", "image_genie.gemspec"]
14
+ s.homepage = %q{https://github.com/bkimble}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Image_genie", "--main", "README"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{image_genie}
18
+ s.rubygems_version = %q{1.4.2}
19
+ s.summary = %q{ImageGenie - Simple Wrapper for command line ImageMagick}
20
+ s.test_files = ["test/test_helper.rb", "test/unit/base_test.rb"]
21
+
22
+ if s.respond_to? :specification_version then
23
+ s.specification_version = 3
24
+
25
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
26
+ s.add_development_dependency(%q<open4>, [">= 0"])
27
+ else
28
+ s.add_dependency(%q<open4>, [">= 0"])
29
+ end
30
+ else
31
+ s.add_dependency(%q<open4>, [">= 0"])
32
+ end
33
+ end
@@ -0,0 +1,49 @@
1
+ require 'open4'
2
+
3
+ # ImageGenie -- a simple wrapper for ImageMagick.
4
+ # Billy Kimble 2011
5
+
6
+ # Usage:
7
+ #
8
+ # ImageGenie.convert(src=<File>, options)
9
+ # converts a file at src.path with command line options given as options, and returns a handle to a tempfile containing the
10
+ # contents of the converted file. Raises an exception with the errors if it can't find the file or has problems converting it.
11
+ #
12
+ # ImageGenie.montage(filenames=[], options)
13
+ # combines all files contained in filenames into a montage (mosaic) image, and returns a tempfile handle to the contents of the montage.
14
+ # Raises an exception with the errors if any 1 of the files in filenames has errors.
15
+ #
16
+ # ImageGenie.identify(filename=<String>, options)
17
+ # identifies the file and returns a hash containing:
18
+ # :filename, :format, :dimensions, :geometry, :bit, :image_class, :colors, :filesize, :usertime, :identify_time
19
+ # Raises an exception with the errors if it can't find the file or has problems identfying it.
20
+ #
21
+ # ImageGenie.verify(filename=<String>, options)
22
+ # verifies that the file pointed to by filename is valid. Returns true on success, or an an exception with the errors
23
+ # if it can't find the file or has problems identfying it
24
+
25
+ module ImageGenie
26
+ class UnableToLocateBinaryError < StandardError; end;
27
+ # Autoload any modules defined in classes in the image_genie directory.
28
+ module_path = [File.dirname(__FILE__),self.name.underscore,'*'].join('/')
29
+ Dir[module_path].each do |file|
30
+ constant_name = File.basename(file,'.rb').camelcase.to_sym
31
+ autoload constant_name, file
32
+ end
33
+
34
+ module ClassMethods
35
+ # Make helper methods
36
+ [:convert,:montage,:identify,:verify].each do |method|
37
+ define_method(method) do |*splat|
38
+ module_name = "#{self.name}::#{method.to_s.capitalize}"
39
+ target = module_name.constantize
40
+ target.run(*splat)
41
+ end
42
+ end
43
+ end
44
+
45
+ class << self
46
+ include ClassMethods
47
+ end
48
+ end
49
+
@@ -0,0 +1,63 @@
1
+ module ImageGenie
2
+ class Base
3
+
4
+ # Discover ImageMagick paths on the current system
5
+ def self.paths
6
+ if !@paths
7
+ @paths = {:identify => "",:montage => "",:convert => ""}
8
+ @paths.keys.each do |program|
9
+ command = "which #{program}"
10
+ pid, stdin, stdout, stderr = Open4::popen4(command)
11
+ path = stdout.read.strip
12
+ @paths[program] = path
13
+ end
14
+ end
15
+ @paths
16
+ end
17
+
18
+ # Eventually I want this method to accept a model that encapsulates its command execution logic.
19
+ def self.execute(command)
20
+ logger.info("Executing #{command}")
21
+ pid, stdin, stdout, stderr = Open4::popen4(command)
22
+ yield pid,stdin,stdout,stderr
23
+ end
24
+
25
+ def self.path_for(program)
26
+ paths[program.to_sym]
27
+ end
28
+
29
+ def self.make_command(command,*args)
30
+ command.strip!
31
+ components = [command]
32
+ args.each do |component|
33
+ components << case (component.class.name)
34
+ when 'Hash': make_flags(component)
35
+ when 'Array': make_args(component)
36
+ else " #{component}"
37
+ end
38
+ end
39
+ components.compact.join(' ')
40
+ end
41
+
42
+ def self.make_args(args)
43
+ args = [args] if !args.is_a?(Array)
44
+ return nil if args.empty?
45
+ args.join(' ')
46
+ end
47
+
48
+ # There is probably a better library to handle creation of command line flags,
49
+ # but this works for now.
50
+ def self.make_flags(options={})
51
+ return nil if options.empty?
52
+ options.collect{|k,v| "-#{k} #{v}"}.join(' ')
53
+ end
54
+
55
+ def self.logger
56
+ @log ||= TimestampedBufferedLogger.new(Rails.root.join('log','image_genie.log'))
57
+ end
58
+
59
+ def logger
60
+ self.class.logger
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,11 @@
1
+ module ImageGenie
2
+ class Command < Base
3
+ def self.handle_errors(stderr,status,exception)
4
+ unless status.exitstatus.zero?
5
+ errors = stderr.read.strip
6
+ logger.error("#{self.name} #{errors}")
7
+ raise(exception, errors)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ module ImageGenie
2
+ class ConvertError < StandardError; end;
3
+ class Convert < Command
4
+ def self.run(src, options={})
5
+ raise(UnableToLocateBinaryError, 'convert') if path_for(:convert).blank?
6
+
7
+ logger.info("#{self.name} Attempting to convert #{src.path}")
8
+
9
+ begin
10
+ raise Errno::ENOENT,"#{src.path}" unless File.exists?(src.path)
11
+ rescue Errno::ENOENT => e
12
+ logger.error("#{self.name} #{e.message}")
13
+ raise
14
+ end
15
+
16
+ format = File.extname(src.path).downcase.gsub(/\.|\?/,'')
17
+
18
+ command = "#{path_for(:convert)} #{make_flags(options)} #{format}:#{src.path} #{format}:-"
19
+ temp_image_file = Tempfile.new([src.original_filename,".#{format}"])
20
+
21
+ execute(command) do |pid,stdin,stdout,stderr|
22
+ while line = stdout.gets
23
+ temp_image_file.puts line
24
+ end
25
+ ignored, status = Process::waitpid2 pid
26
+ handle_errors(stderr,status,ConvertError)
27
+ end
28
+
29
+ temp_image_file.rewind
30
+ return temp_image_file
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # Verify an image is valid by calling 'identify' on it and testing for errors on the return.
2
+ module ImageGenie
3
+ class IdentifyError < StandardError; end;
4
+ class Identify < Command
5
+ def self.run(filename, options={})
6
+ raise(UnableToLocateBinaryError, 'identify') if path_for(:identify).blank?
7
+
8
+ begin
9
+ raise Errno::ENOENT,"#{filename}" unless File.exists?(filename)
10
+ rescue Errno::ENOENT => e
11
+ logger.error("#{self.name} #{e.message}")
12
+ raise
13
+ end
14
+
15
+ command = "#{path_for(:identify)} #{filename}"
16
+ output = ''
17
+
18
+ # We test for STDERR content
19
+ execute(command) do |pid,stdin,stdout,stderr|
20
+ while !stdout.eof?
21
+ output += stdout.readline
22
+ end
23
+
24
+ ignored, status = Process::waitpid2 pid
25
+ handle_errors(stderr,status,IdentifyError)
26
+ end
27
+
28
+ fields = [:filename,:format,:dimensions,:geometry,:bit,:image_class,:colors,:filesize,:usertime,:identify_time]
29
+ return Hash[fields.zip(stdout.read.split(' '))]
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,57 @@
1
+ module ImageGenie
2
+ class MontageError < StandardError; end;
3
+ class Montage < Command
4
+ attr_accessor :filenames,:filename,:options
5
+
6
+ def initialize(options={})
7
+ self.filenames = []
8
+ self.options = options
9
+ end
10
+
11
+
12
+ def self.run(filenames, options={})
13
+ raise(UnableToLocateBinaryError, 'montage') if path_for(:montage).blank?
14
+ # Montage hates when you attempt to give it files with a path in it, so we
15
+ # need to change to the system specified temp directory to do our work.
16
+ Dir.chdir(Dir::tmpdir)
17
+ # Create a temp file that will be all component files of this montage
18
+ temp_image_file = Tempfile.new(['montage','.jpg'])
19
+ temp_image_list_file = Tempfile.new(['montage_image_filenames','.txt'])
20
+ temp_image_list_file.puts filenames.join("\n")
21
+ temp_image_list_file.rewind
22
+
23
+ logger.info("#{self.name} Attempting to make a montage of #{filenames.size} files")
24
+
25
+ command = make_command(path_for(:montage), "@\"#{File.basename(temp_image_list_file.path)}\"", options, "jpeg:-")
26
+
27
+ begin
28
+ execute(command) do |pid,stdin,stdout,stderr|
29
+ while !stdout.eof?
30
+ temp_image_file.puts stdout.readline
31
+ end
32
+ temp_image_file.rewind
33
+
34
+ # Still foggy on if we need to wait until we read from STDOUT before waiting, or if
35
+ # we wait until we read STDOUT and STDERR.
36
+ ignored, status = Process::waitpid2 pid
37
+ handle_errors(stderr,status,MontageError)
38
+ end
39
+ rescue Exception => e
40
+ # Close the tempfile before raising
41
+ temp_image_file.close
42
+ raise
43
+ ensure
44
+ logger.info("#{self.name} Cleaning up files")
45
+ temp_image_list_file.close
46
+ end
47
+
48
+ return temp_image_file
49
+
50
+ end
51
+ end
52
+ end
53
+
54
+
55
+
56
+
57
+
@@ -0,0 +1,27 @@
1
+ # Verify an image is valid by calling 'identify' on it with verbose turned on ,
2
+ # and testing for errors on the return.
3
+ module ImageGenie
4
+ class VerifyError < StandardError; end;
5
+ class Verify < Command
6
+
7
+ def self.run(filename, options={})
8
+ raise(UnableToLocateBinaryError, 'identify') if path_for(:identify).blank?
9
+
10
+ begin
11
+ raise Errno::ENOENT,"#{filename}" unless File.exists?(filename)
12
+ rescue Errno::ENOENT => e
13
+ logger.error("#{self.name} #{e.message}")
14
+ raise
15
+ end
16
+
17
+ command = "#{path_for(:identify)} #{filename}"
18
+ # We test for STDERR content
19
+ execute(command) do |pid,stdin,stdout,stderr|
20
+ ignored, status = Process::waitpid2 pid
21
+ handle_errors(stderr,status,VerifyError)
22
+ end
23
+
24
+ return true
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'tempfile'
3
+ require 'test/unit'
4
+ require 'active_support'
5
+ require 'active_support/inflector' # needed for string methods like underscore
6
+ require 'open4'
7
+ require 'mocha'
8
+ require 'image_genie'
9
+
10
+ require 'active_support/testing/declarative'
11
+ include ActiveSupport::Testing::Declarative # for syntactic sugar of "test 'foo' do .."
@@ -0,0 +1,61 @@
1
+ require 'test_helper'
2
+
3
+ class BaseTest < Test::Unit::TestCase
4
+ test "path for when path exists" do
5
+ ImageGenie::Base.expects(:paths).returns({:convert => "/some/dir/convert", :montage => "/some/dir/montage"}).times(2)
6
+ assert_equal("/some/dir/convert", ImageGenie::Base.path_for(:convert))
7
+ assert_equal("/some/dir/montage", ImageGenie::Base.path_for(:montage))
8
+ end
9
+
10
+ test "path for when path doesnt exist" do
11
+ ImageGenie::Base.expects(:paths).returns({:convert => "/some/dir/convert", :montage => "/some/dir/montage"})
12
+ assert_equal(nil, ImageGenie::Base.path_for(:identify))
13
+ end
14
+
15
+ test "make command with args only" do
16
+ command = "some_command"
17
+ args = ["abc","def"]
18
+ assert_equal("some_command abc def", ImageGenie::Base.make_command(command,args))
19
+ end
20
+
21
+ test "make command with flags only" do
22
+ command = "some_command"
23
+ flags = {:foo => 1, :bar => 2}
24
+ assert_equal("some_command -foo 1 -bar 2", ImageGenie::Base.make_command(command,flags))
25
+ end
26
+
27
+ test "make command" do
28
+ command = "some_command"
29
+ args = ["abc","def"]
30
+ flags = {:foo => 1, :bar => 2}
31
+ assert_equal("some_command -foo 1 -bar 2 abc def", ImageGenie::Base.make_command(command,flags,args))
32
+ end
33
+
34
+ test "make command with no flags or args" do
35
+ command = "some_command"
36
+ args = []
37
+ flags = {}
38
+ assert_equal("some_command", ImageGenie::Base.make_command(command))
39
+ end
40
+
41
+ test "make flags" do
42
+ flags = {:foo => 1, :bar => 2}
43
+ assert_equal("-foo 1 -bar 2", ImageGenie::Base.make_flags(flags))
44
+ end
45
+
46
+ test "make flags with nothing" do
47
+ flags = {}
48
+ assert_equal(nil, ImageGenie::Base.make_flags(flags))
49
+ end
50
+
51
+ test "make args" do
52
+ args = ["abc","def"]
53
+ assert_equal("abc def", ImageGenie::Base.make_args(args))
54
+ end
55
+
56
+ test "make args with nothing" do
57
+ args = []
58
+ assert_equal(nil, ImageGenie::Base.make_args(args))
59
+ end
60
+ end
61
+
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: image_genie
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
+ platform: ruby
12
+ authors:
13
+ - Billy Kimble
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-02-08 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: open4
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ description: ImageGenie - Simple Wrapper for command line ImageMagick
36
+ email: basslines@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README.rdoc
43
+ - lib/image_genie.rb
44
+ - lib/image_genie/base.rb
45
+ - lib/image_genie/command.rb
46
+ - lib/image_genie/convert.rb
47
+ - lib/image_genie/identify.rb
48
+ - lib/image_genie/montage.rb
49
+ - lib/image_genie/verify.rb
50
+ files:
51
+ - Manifest
52
+ - README.rdoc
53
+ - Rakefile
54
+ - lib/image_genie.rb
55
+ - lib/image_genie/base.rb
56
+ - lib/image_genie/command.rb
57
+ - lib/image_genie/convert.rb
58
+ - lib/image_genie/identify.rb
59
+ - lib/image_genie/montage.rb
60
+ - lib/image_genie/verify.rb
61
+ - test/test_helper.rb
62
+ - test/unit/base_test.rb
63
+ - image_genie.gemspec
64
+ has_rdoc: true
65
+ homepage: https://github.com/bkimble
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options:
70
+ - --line-numbers
71
+ - --inline-source
72
+ - --title
73
+ - Image_genie
74
+ - --main
75
+ - README
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ hash: 3
84
+ segments:
85
+ - 0
86
+ version: "0"
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ hash: 11
93
+ segments:
94
+ - 1
95
+ - 2
96
+ version: "1.2"
97
+ requirements: []
98
+
99
+ rubyforge_project: image_genie
100
+ rubygems_version: 1.4.2
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: ImageGenie - Simple Wrapper for command line ImageMagick
104
+ test_files:
105
+ - test/test_helper.rb
106
+ - test/unit/base_test.rb