miso 0.1.0

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.
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