miso 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/LICENSE +20 -0
  2. data/README +55 -0
  3. data/Rakefile +54 -0
  4. data/TODO +2 -0
  5. data/VERSION +1 -0
  6. data/html/classes/Miso.html +133 -0
  7. data/html/classes/Miso/Factory.html +278 -0
  8. data/html/classes/Miso/Image.html +394 -0
  9. data/html/classes/Miso/Processor.html +379 -0
  10. data/html/classes/Miso/Processor/CoreImage.html +246 -0
  11. data/html/classes/Miso/Processor/ImageMagick.html +345 -0
  12. data/html/classes/Miso/Processor/NotImplementedError.html +111 -0
  13. data/html/created.rid +1 -0
  14. data/html/files/LICENSE.html +133 -0
  15. data/html/files/README.html +156 -0
  16. data/html/files/lib/miso/factory_rb.html +101 -0
  17. data/html/files/lib/miso/image_rb.html +101 -0
  18. data/html/files/lib/miso/processor/core_image_rb.html +108 -0
  19. data/html/files/lib/miso/processor/image_magick_rb.html +110 -0
  20. data/html/files/lib/miso/processor_rb.html +101 -0
  21. data/html/files/lib/miso_rb.html +101 -0
  22. data/html/fr_class_index.html +33 -0
  23. data/html/fr_file_index.html +34 -0
  24. data/html/fr_method_index.html +62 -0
  25. data/html/index.html +24 -0
  26. data/html/rdoc-style.css +208 -0
  27. data/lib/miso.rb +5 -0
  28. data/lib/miso/factory.rb +53 -0
  29. data/lib/miso/image.rb +51 -0
  30. data/lib/miso/processor.rb +61 -0
  31. data/lib/miso/processor/core_image.rb +27 -0
  32. data/lib/miso/processor/image_magick.rb +59 -0
  33. data/spec/api/factory_spec.rb +31 -0
  34. data/spec/api/image_spec.rb +115 -0
  35. data/spec/api/processor/image_magick_spec.rb +22 -0
  36. data/spec/api/processor_spec.rb +41 -0
  37. data/spec/fixtures/120x100.png +0 -0
  38. data/spec/start.rb +30 -0
  39. metadata +97 -0
@@ -0,0 +1,61 @@
1
+ module Miso
2
+ class Processor
3
+ autoload :CoreImage, 'miso/processor/core_image'
4
+ autoload :ImageMagick, 'miso/processor/image_magick'
5
+
6
+ class NotImplementedError < StandardError; end
7
+
8
+ class << self
9
+ # Sets the default processor class.
10
+ attr_writer :processor_class
11
+
12
+ # Sets the default processor classes list.
13
+ attr_writer :processor_classes
14
+
15
+ # The default processor class.
16
+ #
17
+ # Returns either the assigned processor, or otherwise finds the first
18
+ # processor, that is available on the machine, from the processor_classes
19
+ # array.
20
+ def processor_class
21
+ @processor_class ||= processor_classes.find { |c| c.available? }
22
+ raise "None of the Miso::Processor classes is available." unless @processor_class
23
+ @processor_class
24
+ end
25
+
26
+ # The list of processor classes.
27
+ #
28
+ # When no explicit processor_class is set this list is iterated, from
29
+ # first to last, and the first available processor on the machine is used.
30
+ def processor_classes
31
+ @processor_classes ||= [CoreImage, ImageMagick]
32
+ end
33
+
34
+ def available?
35
+ raise NotImplementedError, "The class `#{name}' does not implement ::available?."
36
+ end
37
+ end
38
+
39
+ attr_reader :input_file
40
+
41
+ def initialize(input_file)
42
+ @input_file = input_file
43
+ end
44
+
45
+ def crop(width, height)
46
+ raise NotImplementedError, "The class `#{self.class.name}' does not implement #crop."
47
+ end
48
+
49
+ def fit(width, height)
50
+ raise NotImplementedError, "The class `#{self.class.name}' does not implement #fit."
51
+ end
52
+
53
+ def dimensions
54
+ raise NotImplementedError, "The class `#{self.class.name}' does not implement #dimensions."
55
+ end
56
+
57
+ def write(output_file)
58
+ raise NotImplementedError, "The class `#{self.class.name}' does not implement #write."
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,27 @@
1
+ module Miso
2
+ class Processor
3
+ class CoreImage < Processor
4
+ def self.available?
5
+ available = $:.any? { |path| File.exist? File.join(path, 'osx/cocoa.rb') }
6
+ require 'osx/cocoa'
7
+ available
8
+ end
9
+
10
+ def crop(width, height)
11
+ # ...
12
+ end
13
+
14
+ def fit(width, height)
15
+ # ...
16
+ end
17
+
18
+ def dimensions
19
+ # ...
20
+ end
21
+
22
+ def write(output_file)
23
+ # ...
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,59 @@
1
+ begin
2
+ require 'rubygems'
3
+ gem 'executioner', '>= 0.2'
4
+ rescue LoadError
5
+ end
6
+ require 'executioner'
7
+ require 'fileutils'
8
+
9
+ module Miso
10
+ class Processor
11
+ class ImageMagick < Processor
12
+ def self.available?
13
+ `which convert`
14
+ $?.success
15
+ end
16
+
17
+ def crop(width, height)
18
+ dimensions = "#{width}x#{height}"
19
+ operations << "-resize #{dimensions}^ -gravity center -crop #{dimensions}+0+0!"
20
+ end
21
+
22
+ def fit(width, height)
23
+ operations << "-resize #{width}x#{height}"
24
+ end
25
+
26
+ def dimensions
27
+ if info = identify(input_file) and match = /\s(\d+)x(\d+)\+/.match(info)
28
+ match.to_a[1..2].map { |d| d.to_i }
29
+ end
30
+ end
31
+
32
+ def write(output_file)
33
+ options = operations.join(' ')
34
+ operations.clear
35
+ convert(input_file, output_file, options)
36
+ end
37
+
38
+ include Executioner
39
+ executable :convert
40
+ executable :identify
41
+
42
+ alias_method :_convert, :convert
43
+ def convert(source_path, output_path, options)
44
+ ensure_output_directory(output_path)
45
+ _convert "'#{source_path}' #{options} '#{output_path}'"
46
+ end
47
+
48
+ private
49
+
50
+ def operations
51
+ @operations ||= []
52
+ end
53
+
54
+ def ensure_output_directory(path)
55
+ FileUtils.mkdir_p(File.dirname(path))
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path('../../start', __FILE__)
2
+
3
+ describe "A Miso::Factory instance" do
4
+ before do
5
+ Miso::Processor.processor_class = Miso::Processor
6
+
7
+ Miso::Processor.any_instance.stubs(:write)
8
+
9
+ @input_file = '/image.png'
10
+ @output_file = '/output_image.png'
11
+ @image = Miso::Image.new(@input_file)
12
+
13
+ Miso::Image.stubs(:new).with(@input_file, Miso::Processor).returns(@image)
14
+ Miso::Image.stubs(:new).with(@output_file, Miso::Processor)
15
+
16
+ @factory = Miso::Factory.new
17
+ end
18
+
19
+ after do
20
+ Miso::Processor.processor_class = nil
21
+ end
22
+
23
+ it "should store operations that are to be applied later on" do
24
+ @factory.crop(123, 456).fit(123, 456)
25
+
26
+ @image.expects(:crop).with(123, 456)
27
+ @image.expects(:fit).with(123, 456)
28
+
29
+ @factory.apply(@input_file, @output_file)
30
+ end
31
+ end
@@ -0,0 +1,115 @@
1
+ require File.expand_path('../../start', __FILE__)
2
+
3
+ describe "Miso::Image, concerning initialization" do
4
+ class FakeProcessor; end
5
+
6
+ before do
7
+ Miso::Processor.processor_class = nil
8
+ end
9
+
10
+ after do
11
+ Miso::Processor.processor_class = nil
12
+ end
13
+
14
+ it "should initialize with only a file argument and use the default processor class" do
15
+ Miso::Processor.processor_class = Miso::Processor::ImageMagick
16
+ image = Miso::Image.new('/image.png')
17
+
18
+ image.processor.input_file.should == '/image.png'
19
+ image.processor.class.should == Miso::Processor::ImageMagick
20
+ end
21
+
22
+ it "should initialize with a file and processor class" do
23
+ image = Miso::Image.new('/image.png', Miso::Processor::ImageMagick)
24
+
25
+ image.processor.input_file.should == '/image.png'
26
+ image.processor.class.should == Miso::Processor::ImageMagick
27
+ end
28
+ end
29
+
30
+ describe "An instance of Miso::Image, concerning forwarding calls to the processor" do
31
+ before do
32
+ Miso::Processor.any_instance.stubs(:fit)
33
+ Miso::Processor.any_instance.stubs(:crop)
34
+
35
+ @image = Miso::Image.new('/image.png', Miso::Processor)
36
+ end
37
+
38
+ it "should forward #crop to the processor" do
39
+ @image.processor.expects(:crop).with(123, 456)
40
+ @image.crop(123, 456)
41
+ end
42
+
43
+ it "should return self when calling #crop" do
44
+ @image.crop(123, 456).should.be @image
45
+ end
46
+
47
+ it "should forward #fit to the processor" do
48
+ @image.processor.expects(:fit).with(123, 456)
49
+ @image.fit(123, 456)
50
+ end
51
+
52
+ it "should return self instance when calling #fit" do
53
+ @image.fit(123, 456).should.be @image
54
+ end
55
+
56
+ it "should forward #dimensions to the processor and return the result" do
57
+ @image.processor.expects(:dimensions).returns([123, 456])
58
+ @image.dimensions.should == [123, 456]
59
+ end
60
+
61
+ it "should forward #write to the processor and forward its output file to the new instance of Miso::Image" do
62
+ @image.processor.expects(:write).with('/output_image.png')
63
+ output_image = @image.write('/output_image.png')
64
+ output_image.processor.input_file.should == '/output_image.png'
65
+ end
66
+ end
67
+
68
+ describe "An instance of Miso::Image, concerning combined methods" do
69
+ before do
70
+ Miso::Processor.any_instance.stubs(:fit)
71
+ Miso::Processor.any_instance.stubs(:crop)
72
+
73
+ @image = Miso::Image.new('/image.png', Miso::Processor)
74
+ end
75
+
76
+ it "should call #fit to scale and preserve aspect ratio, then call #crop" do
77
+ @image.expects(:fit).with(123, 456).returns(@image)
78
+ @image.expects(:crop).with(123, 456).returns(@image)
79
+
80
+ @image.crop_fitting(123, 456)
81
+ end
82
+
83
+ it "should return self when calling #crop_fitting" do
84
+ @image.crop_fitting(123, 456).should.be @image
85
+ end
86
+ end
87
+
88
+ describe "Miso::Image, concerning shortcut class methods" do
89
+ before do
90
+ @input_file = fixture_file('120x100.png')
91
+ @output_file = temp_file('cropped_to_40x30.png')
92
+ @image = Miso::Image.new(@input_file, Miso::Processor)
93
+
94
+ Miso::Image.stubs(:new).with(@input_file, nil).returns(@image)
95
+ end
96
+
97
+ it "should crop to specified dimensions" do
98
+ @image.expects(:crop).with(40, 30).returns(@image)
99
+ @image.expects(:write).with(@output_file)
100
+
101
+ Miso::Image.crop(@input_file, @output_file, 40, 30)
102
+ end
103
+
104
+ it "should fit to specified dimensions, conserving the original aspect ratio" do
105
+ @image.expects(:fit).with(40, 30).returns(@image)
106
+ @image.expects(:write).with(@output_file)
107
+
108
+ Miso::Image.fit(@input_file, @output_file, 40, 30)
109
+ end
110
+
111
+ it "should return its dimensions" do
112
+ @image.expects(:dimensions).returns([120, 100])
113
+ Miso::Image.dimensions(@input_file).should == [120, 100]
114
+ end
115
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path('../../../start', __FILE__)
2
+
3
+ describe "An instance of Miso::Processor::ImageMagick" do
4
+ before do
5
+ @image_120_x_100 = Miso::Image.new(fixture_file('120x100.png'), Miso::Processor::ImageMagick)
6
+ @output_file = temp_file('temp.png')
7
+ end
8
+
9
+ it "should crop to specified dimensions" do
10
+ @image_120_x_100.crop(40, 30).write(@output_file).dimensions.should == [40, 30]
11
+ @image_120_x_100.crop(40, 33).write(@output_file).dimensions.should == [40, 33]
12
+ end
13
+
14
+ it "should fit to specified dimensions, conserving the original aspect ratio" do
15
+ @image_120_x_100.fit(40, 30).write(@output_file).dimensions.should == [36, 30]
16
+ @image_120_x_100.fit(40, 34).write(@output_file).dimensions.should == [40, 33]
17
+ end
18
+
19
+ it "should return its dimensions" do
20
+ @image_120_x_100.dimensions.should == [120, 100]
21
+ end
22
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path('../../start', __FILE__)
2
+
3
+ describe "Miso::Processor" do
4
+ before do
5
+ Miso::Processor.processor_class = nil
6
+ end
7
+
8
+ after do
9
+ Miso::Processor.processor_class = nil
10
+ end
11
+
12
+ it "should assign the processor class to use" do
13
+ Miso::Processor.processor_class = Miso::Processor::ImageMagick
14
+ Miso::Processor.processor_class.should.be Miso::Processor::ImageMagick
15
+ end
16
+
17
+ it "should find the first available processor class" do
18
+ Miso::Processor.processor_classes = [
19
+ stub(:available? => false),
20
+ stub(:available? => true),
21
+ stub(:available? => false)
22
+ ]
23
+
24
+ Miso::Processor.processor_class.should.be Miso::Processor.processor_classes[1]
25
+ end
26
+
27
+ it "should raise if no available processor class is found" do
28
+ Miso::Processor.processor_classes = [
29
+ stub(:available? => false),
30
+ stub(:available? => false)
31
+ ]
32
+
33
+ lambda {
34
+ Miso::Processor.processor_class
35
+ }.should.raise
36
+ end
37
+
38
+ it "should initialize with an input file" do
39
+ Miso::Processor.new('/image.png').input_file.should == '/image.png'
40
+ end
41
+ end
Binary file
data/spec/start.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'rubygems' rescue LoadError
2
+ require 'test/spec'
3
+ require 'mocha'
4
+
5
+ require 'fileutils'
6
+
7
+ $:.unshift(File.expand_path('../../lib', __FILE__))
8
+
9
+ require 'miso'
10
+ require 'miso/processor'
11
+
12
+ class Test::Unit::TestCase
13
+ TMP_DIR = File.expand_path('../tmp', __FILE__)
14
+
15
+ def setup
16
+ FileUtils.mkdir_p(TMP_DIR)
17
+ end
18
+
19
+ def teardown
20
+ FileUtils.rm_rf(TMP_DIR)
21
+ end
22
+
23
+ def fixture_file(name)
24
+ File.expand_path("../fixtures/#{name}", __FILE__)
25
+ end
26
+
27
+ def temp_file(filename)
28
+ File.join(TMP_DIR, filename)
29
+ end
30
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: miso
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Manfred Stienstra
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-20 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Miso is a unified API for simple image operations commonly used on the web.
17
+ email: manfred@fngtps.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README
25
+ files:
26
+ - LICENSE
27
+ - README
28
+ - Rakefile
29
+ - TODO
30
+ - VERSION
31
+ - html/classes/Miso.html
32
+ - html/classes/Miso/Factory.html
33
+ - html/classes/Miso/Image.html
34
+ - html/classes/Miso/Processor.html
35
+ - html/classes/Miso/Processor/CoreImage.html
36
+ - html/classes/Miso/Processor/ImageMagick.html
37
+ - html/classes/Miso/Processor/NotImplementedError.html
38
+ - html/created.rid
39
+ - html/files/LICENSE.html
40
+ - html/files/README.html
41
+ - html/files/lib/miso/factory_rb.html
42
+ - html/files/lib/miso/image_rb.html
43
+ - html/files/lib/miso/processor/core_image_rb.html
44
+ - html/files/lib/miso/processor/image_magick_rb.html
45
+ - html/files/lib/miso/processor_rb.html
46
+ - html/files/lib/miso_rb.html
47
+ - html/fr_class_index.html
48
+ - html/fr_file_index.html
49
+ - html/fr_method_index.html
50
+ - html/index.html
51
+ - html/rdoc-style.css
52
+ - lib/miso.rb
53
+ - lib/miso/factory.rb
54
+ - lib/miso/image.rb
55
+ - lib/miso/processor.rb
56
+ - lib/miso/processor/core_image.rb
57
+ - lib/miso/processor/image_magick.rb
58
+ - spec/api/factory_spec.rb
59
+ - spec/api/image_spec.rb
60
+ - spec/api/processor/image_magick_spec.rb
61
+ - spec/api/processor_spec.rb
62
+ - spec/fixtures/120x100.png
63
+ - spec/start.rb
64
+ has_rdoc: true
65
+ homepage: http://github.com/Fingertips/miso
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options:
70
+ - --charset=UTF-8
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0"
84
+ version:
85
+ requirements: []
86
+
87
+ rubyforge_project:
88
+ rubygems_version: 1.3.5
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: Miso is a unified API for simple image operations commonly used on the web.
92
+ test_files:
93
+ - spec/api/factory_spec.rb
94
+ - spec/api/image_spec.rb
95
+ - spec/api/processor/image_magick_spec.rb
96
+ - spec/api/processor_spec.rb
97
+ - spec/start.rb