airbrush 0.0.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.
- data/History.txt +4 -0
- data/Manifest.txt +55 -0
- data/README.txt +39 -0
- data/Rakefile +4 -0
- data/bin/airbrush +61 -0
- data/bin/airbrush-example-client +60 -0
- data/config/hoe.rb +71 -0
- data/config/requirements.rb +17 -0
- data/lib/airbrush/client.rb +53 -0
- data/lib/airbrush/core_ext/get_args.rb +94 -0
- data/lib/airbrush/core_ext/timestamped_logger.rb +10 -0
- data/lib/airbrush/handler.rb +18 -0
- data/lib/airbrush/listeners/listener.rb +45 -0
- data/lib/airbrush/listeners/memcache.rb +53 -0
- data/lib/airbrush/listeners/socket.rb +0 -0
- data/lib/airbrush/listeners/webservice.rb +0 -0
- data/lib/airbrush/processors/image/image_processor.rb +31 -0
- data/lib/airbrush/processors/image/profiles/cmyk-profile.icc +0 -0
- data/lib/airbrush/processors/image/profiles/srgb-profile.icc +0 -0
- data/lib/airbrush/processors/image/rmagick.rb +116 -0
- data/lib/airbrush/processors/processor.rb +43 -0
- data/lib/airbrush/publishers/http.rb +13 -0
- data/lib/airbrush/publishers/memcache.rb +24 -0
- data/lib/airbrush/publishers/publisher.rb +16 -0
- data/lib/airbrush/server.rb +20 -0
- data/lib/airbrush/version.rb +9 -0
- data/lib/airbrush.rb +30 -0
- data/log/debug.log +0 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/spec/airbrush/client_spec.rb +87 -0
- data/spec/airbrush/core_ext/get_args_spec.rb +0 -0
- data/spec/airbrush/handler_spec.rb +44 -0
- data/spec/airbrush/listeners/listener_spec.rb +18 -0
- data/spec/airbrush/listeners/memcache_spec.rb +131 -0
- data/spec/airbrush/processors/image/image_processor_spec.rb +56 -0
- data/spec/airbrush/processors/image/rmagick_spec.rb +179 -0
- data/spec/airbrush/processors/processor_spec.rb +110 -0
- data/spec/airbrush/publishers/memcache_spec.rb +46 -0
- data/spec/airbrush/publishers/publisher_spec.rb +17 -0
- data/spec/airbrush/server_spec.rb +57 -0
- data/spec/airbrush_spec.rb +9 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/rspec.rake +36 -0
- data/tasks/website.rake +17 -0
- data/website/index.html +11 -0
- data/website/index.txt +39 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +138 -0
- data/website/template.rhtml +48 -0
- metadata +161 -0
@@ -0,0 +1,87 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
require 'starling'
|
3
|
+
|
4
|
+
describe Airbrush::Client, 'job management' do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@id = :id
|
8
|
+
@command = :command
|
9
|
+
@args = {}
|
10
|
+
@results = { :results => 'results' }
|
11
|
+
@queue = 'incoming'
|
12
|
+
@response_timeout = 5.minutes
|
13
|
+
@queue_validity = 15.minutes
|
14
|
+
|
15
|
+
@server = mock(Starling)
|
16
|
+
@server.stub!(:set).and_return
|
17
|
+
@server.stub!(:get).and_return(@results)
|
18
|
+
Starling.stub!(:new).and_return(@server)
|
19
|
+
|
20
|
+
@host = 'host'
|
21
|
+
@client = Airbrush::Client.new(@host, @queue, @response_timeout, @queue_validity)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'when created' do
|
25
|
+
|
26
|
+
it 'should support a configurable target memcache host' do
|
27
|
+
@client.host.should == @host
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should support a configurable inbound queue name' do
|
31
|
+
@client.incoming_queue.should == @queue
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should support a configurable target memcache host' do
|
35
|
+
@client.response_timeout.should == @response_timeout
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should support a configurable queue validity' do
|
39
|
+
@client.queue_validity.should == @queue_validity
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'job management' do
|
45
|
+
|
46
|
+
it 'should allow a job to be posted' do
|
47
|
+
@client.should respond_to(:process)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should allow access to the results returned by a job' do
|
51
|
+
@client.process(@id, @command, @args).should == @results
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'a posted job' do
|
57
|
+
|
58
|
+
it 'should raise an error if the id is not valid' do
|
59
|
+
lambda { @client.process(nil, @command, @args) }.should raise_error
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should raise an error if the command is not valid' do
|
63
|
+
lambda { @client.process(@id, nil, @args) }.should raise_error
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should raise an error if the args are not valid' do
|
67
|
+
lambda { @client.process(@id, @command, nil) }.should raise_error
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should support optional arguments' do
|
71
|
+
lambda { @client.process(@id, @command) }.should_not raise_error
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should time out after 2 minutes seconds of inactivity' do
|
75
|
+
Airbrush::Client::DEFAULT_RESPONSE_TIMEOUT.should == 2.minutes
|
76
|
+
Timeout.should_receive(:timeout).and_return
|
77
|
+
@client.process(@id, @command)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should add the job into the incoming process queue' do
|
81
|
+
@server.should_receive(:set).with(@queue, { :id => @id, :command => @command, :args => @args }, @queue_validity, false)
|
82
|
+
@client.process(@id, @command, @args)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
File without changes
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
describe Airbrush::Handler, 'during creation' do
|
4
|
+
|
5
|
+
it 'should raise an error if given a nil processor' do
|
6
|
+
lambda { Airbrush::Handler.new(nil, Airbrush::Publishers::Publisher.new) }.should raise_error
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should raise an error if given a nil publisher' do
|
10
|
+
lambda { Airbrush::Handler.new(Airbrush::Processors::Processor.new, nil) }.should raise_error
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Airbrush::Handler, 'when processing a request' do
|
16
|
+
|
17
|
+
before do
|
18
|
+
@processor = mock(Airbrush::Processors::Processor)
|
19
|
+
@processor.stub!(:dispatch).and_return(true)
|
20
|
+
|
21
|
+
@publisher = mock(Airbrush::Publishers::Publisher)
|
22
|
+
@publisher.stub!(:publish).and_return
|
23
|
+
|
24
|
+
@handler = Airbrush::Handler.new(@processor, @publisher)
|
25
|
+
|
26
|
+
@id = 'id'
|
27
|
+
@command = :command
|
28
|
+
@args = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should pass the request to the processor' do
|
32
|
+
@processor.should_receive(:dispatch).with(@command, @args).and_return(true)
|
33
|
+
@handler.process @id, @command, @args
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should publish the processors result' do
|
37
|
+
@publisher.should_receive(:publish).with(@id, true).and_return
|
38
|
+
@handler.process @id, @command, @args
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should handle errors raised from the processor gracefully'
|
42
|
+
it 'should handle errors raised from the publisher gracefully'
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
|
+
|
3
|
+
describe Airbrush::Listeners::Listener, 'when created' do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@listener = Airbrush::Listeners::Listener.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should provide access to the handler instance' do
|
10
|
+
@listener.handler = Airbrush::Handler.new(mock(Airbrush::Processors::Processor), mock(Airbrush::Publishers::Publisher))
|
11
|
+
@listener.handler.should_not be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should raise an error if start is attempted (since its an abstract class)' do
|
15
|
+
lambda { @listener.start }.should raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
|
+
require 'starling'
|
3
|
+
|
4
|
+
describe Airbrush::Listeners::Memcache, 'initialization' do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@host = 'host'
|
8
|
+
@frequency = 5
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Airbrush::Listeners::Memcache, 'when created' do
|
12
|
+
|
13
|
+
before do
|
14
|
+
@memcache = Airbrush::Listeners::Memcache.new(@host, @frequency)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should configure access to the remote memcache server host' do
|
18
|
+
@memcache.host.should == @host
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should configure the poll frequency time' do
|
22
|
+
@memcache.poll_frequency.should == @frequency
|
23
|
+
end
|
24
|
+
|
25
|
+
# future
|
26
|
+
it 'should accept a configurable incoming job queue name'
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
describe Airbrush::Listeners::Memcache, 'signal handling' do
|
31
|
+
|
32
|
+
before do
|
33
|
+
Signal.stub!(:trap).and_return
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should catch sigint and disconnect from the memcache server' do
|
37
|
+
Signal.should_receive(:trap).with('INT').and_return
|
38
|
+
Airbrush::Listeners::Memcache.new(@host, @frequency)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
describe Airbrush::Listeners::Memcache, 'when started' do
|
44
|
+
|
45
|
+
before do
|
46
|
+
@op = { :command => :command, :args => {}, :id => 'id' }
|
47
|
+
@server = mock(MemCache)
|
48
|
+
@server.stub!(:get).and_return(@op)
|
49
|
+
MemCache.stub!(:new).and_return(@server)
|
50
|
+
|
51
|
+
@handler = mock(Airbrush::Handler)
|
52
|
+
@handler.stub!(:process).and_return
|
53
|
+
|
54
|
+
@memcache = Airbrush::Listeners::Memcache.new(@host)
|
55
|
+
@memcache.handler = @handler
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should connect to the remote memcache server' # REVISIT: can't call start since it loops forever
|
59
|
+
|
60
|
+
it 'should poll the remote memcache server for jobs' # REVISIT: can't call start since it loops forever
|
61
|
+
|
62
|
+
it 'should sleep 2 seconds inbetween memcache server polls' # REVISIT: can't call start since it loops forever
|
63
|
+
|
64
|
+
it 'should ensure remote jobs are contained inside a hash' do
|
65
|
+
@server.stub!(:get).and_return("bad format")
|
66
|
+
@handler.should_not_receive(:process)
|
67
|
+
@memcache.send :process, @server
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should ensure remote jobs include a :id key' do
|
71
|
+
@server.stub!(:get).and_return(@op.except(:id))
|
72
|
+
@handler.should_not_receive(:process)
|
73
|
+
@memcache.send :process, @server
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should ensure remote jobs include a :command key' do
|
77
|
+
@server.stub!(:get).and_return(@op.except(:command))
|
78
|
+
@handler.should_not_receive(:process)
|
79
|
+
@memcache.send :process, @server
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should ensure remote jobs include an :args key' do
|
83
|
+
@server.stub!(:get).and_return(@op.except(:args))
|
84
|
+
@handler.should_not_receive(:process)
|
85
|
+
@memcache.send :process, @server
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should log invalid jobs' do
|
89
|
+
@server.stub!(:get).and_return('invalid job')
|
90
|
+
@server.stub!(:valid?).and_return(false)
|
91
|
+
@server.log.should_receive(:error).and_return
|
92
|
+
@handler.should_not_receive(:process)
|
93
|
+
@memcache.send :process, @server
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should ignore invalid jobs (ie. invalid jobs should not halt processing)' do
|
97
|
+
@server.stub!(:get).and_return(nil)
|
98
|
+
@handler.should_not_receive(:process)
|
99
|
+
|
100
|
+
lambda { @memcache.send :process, @server }.should_not raise_error
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should pass valid jobs to the handler' do
|
104
|
+
@handler.should_receive(:process).with(@op[:id], @op[:command], @op[:args]).and_return
|
105
|
+
@memcache.send :process, @op
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should log errors return from the handler' do
|
109
|
+
@server.stub!(:get).and_return(@op)
|
110
|
+
@server.log.should_receive(:error).twice.and_return
|
111
|
+
@handler.should_receive(:process).with(@op[:id], @op[:command], @op[:args]).and_raise('Error')
|
112
|
+
@memcache.send :process, @op
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should ignore errors returned from the handler (ie. errors raised from the handler should not halt processing)' do
|
116
|
+
@server.stub!(:get).and_return(@op)
|
117
|
+
@handler.stub!(:process).and_raise("Test Error")
|
118
|
+
|
119
|
+
lambda { @memcache.send :process, @server }.should_not raise_error
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should accept a configurable poll time frequency' do
|
123
|
+
@memcache = Airbrush::Listeners::Memcache.new(@host, 10)
|
124
|
+
@memcache.poll_frequency.should == 10
|
125
|
+
end
|
126
|
+
|
127
|
+
# future
|
128
|
+
it 'should automatically discover the target memcache host address via dnssd'
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper.rb'
|
2
|
+
|
3
|
+
class MyImageProcessor < Airbrush::Processors::Image::ImageProcessor
|
4
|
+
before_filter :before
|
5
|
+
after_filter :after
|
6
|
+
|
7
|
+
def command
|
8
|
+
'no op'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class MyExtraImageProcessor < Airbrush::Processors::Image::ImageProcessor
|
13
|
+
before_filter :before_extra
|
14
|
+
after_filter :after_extra
|
15
|
+
|
16
|
+
def command
|
17
|
+
'no op'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe Airbrush::Processors::Image::ImageProcessor, 'filter definitions' do
|
22
|
+
|
23
|
+
before do
|
24
|
+
@processor1 = MyImageProcessor.new
|
25
|
+
@processor2 = MyExtraImageProcessor.new
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should not override filters from other processors' do
|
29
|
+
@processor1.before_filters.should == [ :before ]
|
30
|
+
@processor2.before_filters.should == [ :before_extra ]
|
31
|
+
|
32
|
+
@processor1.after_filters.should == [ :after ]
|
33
|
+
@processor2.after_filters.should == [ :after_extra ]
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
describe Airbrush::Processors::Image::ImageProcessor, 'when dispatching' do
|
39
|
+
|
40
|
+
before do
|
41
|
+
@args = {}
|
42
|
+
@processor = MyImageProcessor.new
|
43
|
+
@processor.stub!(:filter_dispatch).and_return
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should invoke before filters before sending operation' do
|
47
|
+
@processor.should_receive(:filter_dispatch).with(:before, @args).and_return
|
48
|
+
@processor.dispatch :command, @args
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should invoke after filters after sending operation' do
|
52
|
+
@processor.should_receive(:filter_dispatch).with(:after, @args).and_return
|
53
|
+
@processor.dispatch :command, @args
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper.rb'
|
2
|
+
require 'RMagick'
|
3
|
+
|
4
|
+
describe Airbrush::Processors::Image::Rmagick do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@image = 'image'
|
8
|
+
@columns = 1500; @rows = 1000
|
9
|
+
@blob = 'blob'
|
10
|
+
|
11
|
+
@rm_image = mock(Object)
|
12
|
+
@rm_image.stub!(:change_geometry).and_return
|
13
|
+
@rm_image.stub!(:crop!).and_return
|
14
|
+
@rm_image.stub!(:crop_resized!).and_return
|
15
|
+
@rm_image.stub!(:ensure_rgb!).and_return
|
16
|
+
@rm_image.stub!(:to_blob).and_return(@blob)
|
17
|
+
@rm_image.stub!(:format=).and_return('JPEG')
|
18
|
+
@rm_image.stub!(:columns).and_return(@columns)
|
19
|
+
@rm_image.stub!(:rows).and_return(@rows)
|
20
|
+
Magick::Image.stub!(:from_blob).and_return([@rm_image])
|
21
|
+
|
22
|
+
@processor = Airbrush::Processors::Image::Rmagick.new
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'when resizing' do
|
26
|
+
|
27
|
+
it 'should auto calculate width/height when passed a single value' do
|
28
|
+
Magick::Image.should_receive(:from_blob).and_return([@rm_image])
|
29
|
+
@processor.should_receive(:calculate_dimensions).with(@image, 300).and_return([300,200])
|
30
|
+
|
31
|
+
@processor.resize @image, 300
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should preprocess images before resizing' do
|
35
|
+
@processor.resize @image, 300, 200
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should change the geometry of the image' do
|
39
|
+
@rm_image.should_receive(:change_geometry).and_return
|
40
|
+
@processor.resize @image, 300, 200
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should convert images to rgb if required' do
|
44
|
+
@rm_image.should_receive(:ensure_rgb!).and_return
|
45
|
+
@processor.resize @image, 300, 200
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should return the raw image data back to the caller' do
|
49
|
+
@rm_image.should_receive(:to_blob).and_return('blob')
|
50
|
+
@processor.resize(@image, 300, 200).should == { :image => "blob", :height => 1000, :width => 1500 }
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'when cropping' do
|
56
|
+
|
57
|
+
it 'should preprocess images before cropping' do
|
58
|
+
@processor.crop @image, 10, 10, 100, 100
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should change the geometry of the image' do
|
62
|
+
@rm_image.should_receive(:crop!).and_return
|
63
|
+
@processor.crop @image, 10, 10, 100, 100
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should convert images to rgb if required' do
|
67
|
+
@rm_image.should_receive(:ensure_rgb!).and_return
|
68
|
+
@processor.resize @image, 300, 200
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should return the raw image data back to the caller' do
|
72
|
+
@rm_image.should_receive(:to_blob).and_return('blob')
|
73
|
+
@processor.crop(@image, 10, 10, 100, 100).should == { :image => "blob", :height => 1000, :width => 1500 }
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'when performing a resized crop' do
|
79
|
+
|
80
|
+
it 'should preprocess images before resizing/cropping' do
|
81
|
+
@processor.crop_resize @image, 75, 75
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should change the geometry of the image' do
|
85
|
+
@rm_image.should_receive(:crop_resized!).and_return
|
86
|
+
@processor.crop_resize @image, 75, 75
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should convert images to rgb if required' do
|
90
|
+
@rm_image.should_receive(:ensure_rgb!).and_return
|
91
|
+
@processor.resize @image, 300, 200
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should return the raw image data back to the caller' do
|
95
|
+
@rm_image.should_receive(:to_blob).and_return('blob')
|
96
|
+
@processor.crop_resize(@image, 75, 75).should == { :image => "blob", :height => 1000, :width => 1500 }
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'when generating previews' do
|
102
|
+
|
103
|
+
it 'should change the geometry of the image' do
|
104
|
+
@rm_image.should_receive(:crop_resized!).twice.and_return
|
105
|
+
@processor.previews @image, { :small => [200,100], :large => [500,250] }
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should return the raw image data back to the caller' do
|
109
|
+
@rm_image.should_receive(:to_blob).twice.and_return('blob')
|
110
|
+
@processor.previews(@image, { :small => [200,100], :large => [500,250] }).should == { :small=> { :image => "blob", :height => 1000, :width => 1500 }, :large=> { :image => "blob", :height => 1000, :width => 1500 }, :original => [1500, 1000] }
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
describe 'dimension calculation' do
|
116
|
+
|
117
|
+
it 'should automatically recognize images in portrait mode' do
|
118
|
+
@processor.send(:calculate_dimensions, @rm_image, 300).should == [300,200]
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should automatically recognize images in landscape mode' do
|
122
|
+
@rm_image.stub!(:columns).and_return(1000)
|
123
|
+
@rm_image.stub!(:rows).and_return(1500)
|
124
|
+
@processor.send(:calculate_dimensions, @rm_image, 300).should == [200,300]
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should automatically clip resizes if they are larger than the original' do
|
128
|
+
@processor.send(:calculate_dimensions, @rm_image, 2000).should == [1500,1000]
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
describe Magick::Image, 'when created' do
|
136
|
+
|
137
|
+
before do
|
138
|
+
@image = Magick::Image.new(200, 100)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should be able to convert the profile to srgb when requested' do
|
142
|
+
@image.should respond_to(:ensure_rgb!)
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
describe Magick::Image, 'when converting images to sRGB' do
|
148
|
+
|
149
|
+
before do
|
150
|
+
@image = Magick::Image.new(200, 100)
|
151
|
+
@image.stub!(:add_profile).and_return
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should return if the image is already in sRGB colour space' do
|
155
|
+
@image.colorspace = Magick::RGBColorspace
|
156
|
+
@image.should_not_receive(:add_profile)
|
157
|
+
@image.ensure_rgb!
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'should log a warning if a non CMYK/RGB image is encountered' do
|
161
|
+
@image.colorspace = Magick::Rec709LumaColorspace
|
162
|
+
@image.should_not_receive(:add_profile)
|
163
|
+
@image.log.should_receive(:warn).and_return
|
164
|
+
@image.ensure_rgb!
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should add a CMYK profile if the image does not have a profile and is in CMYK colour space' do
|
168
|
+
@image.colorspace = Magick::CMYKColorspace
|
169
|
+
@image.should_receive(:add_profile).twice.and_return
|
170
|
+
@image.ensure_rgb!
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'should add a sRGB profile if the image is in CMYK colour space' do
|
174
|
+
@image.colorspace = Magick::CMYKColorspace
|
175
|
+
@image.should_receive(:add_profile).with(Magick::Image::SCT_SRGB_ICC).and_return
|
176
|
+
@image.ensure_rgb!
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
|
+
|
3
|
+
class MyProcessor < Airbrush::Processors::Processor
|
4
|
+
|
5
|
+
def my_action(one, two, three = '3')
|
6
|
+
[ one, two, three ]
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Airbrush::Processors::Processor, 'abstract class' do
|
12
|
+
|
13
|
+
before do
|
14
|
+
@processor = Airbrush::Processors::Processor.new
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should return an error from dispatch if the requested command is not implemented by the processor' do
|
18
|
+
@processor.dispatch(:blah, {}).should == { :exception => "ERROR: Received error during processor dispatch for command 'blah' ()", :message => 'Unknown processor operation blah ()' }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Airbrush::Processors::Processor, 'argument handling' do
|
24
|
+
|
25
|
+
before do
|
26
|
+
@processor = MyProcessor.new
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should inspect the target action for its parameters and assign them via the argument hash' do
|
30
|
+
params = @processor.send :assign, :my_action, { :one => 'a', :two => 'b', :three => 'c' }
|
31
|
+
params.should == %w( a b c )
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should respect default values when present in action signatures, but not in the argument hash' do
|
35
|
+
params = @processor.send :assign, :my_action, { :one => 'a', :two => 'b' }
|
36
|
+
params.should == %w( a b 3 )
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should raise an error if an action signature value is missing from the argument hash and does not have a default value' do
|
40
|
+
lambda { @processor.send(:assign, :my_action, { :one => 'a' }) }.should raise_error
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
describe Airbrush::Processors::Processor, 'when processing' do
|
46
|
+
|
47
|
+
before do
|
48
|
+
@processor = MyProcessor.new
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should invoke the requested target action' do
|
52
|
+
@processor.dispatch(:my_action, { :one => 'a', :two => 'b' }).should == %w( a b 3 )
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe Airbrush::Processors::Processor, 'error handling' do
|
57
|
+
|
58
|
+
before do
|
59
|
+
@error = 'an error string'
|
60
|
+
@processor = MyProcessor.new
|
61
|
+
@processor.stub!(:my_action).and_raise(@error)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should return an error from dispatch if the requested command raises an exception during processing' do
|
65
|
+
@processor.should_receive(:my_action).and_raise(@error)
|
66
|
+
@options = { :one => 'a', :two => 'b', :three => 'c' }
|
67
|
+
@processor.dispatch(:my_action, @options).should == { :exception => "ERROR: Received error during processor dispatch for command 'my_action' (#{@options.inspect})", :message => @error }
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
class FilteredProcessor < Airbrush::Processors::Processor
|
73
|
+
filter_params :password
|
74
|
+
|
75
|
+
def command(username, password); 'no op'; end
|
76
|
+
end
|
77
|
+
|
78
|
+
class ExtraFilteredProcessor < Airbrush::Processors::Processor
|
79
|
+
filter_params :username
|
80
|
+
|
81
|
+
def command(username, password); 'no_op'; end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe Airbrush::Processors::Processor, 'filter definitions' do
|
85
|
+
|
86
|
+
before do
|
87
|
+
@processor1 = FilteredProcessor.new
|
88
|
+
@processor2 = ExtraFilteredProcessor.new
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should not override filters from other processors' do
|
92
|
+
@processor1.filtered_params.should == [ :password ]
|
93
|
+
@processor2.filtered_params.should == [ :username ]
|
94
|
+
end
|
95
|
+
|
96
|
+
describe 'filtering' do
|
97
|
+
|
98
|
+
before do
|
99
|
+
@params = { :username => 'username', :password => 'password' }
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should replace market parameter keys with a new value' do
|
103
|
+
{ @processor1 => :password, @processor2 => :username }.each do |processor, filtered|
|
104
|
+
processor.send(:filter, @params).should == @params.update(filtered => '[FILTERED]')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
|
+
require 'starling'
|
3
|
+
|
4
|
+
describe Airbrush::Publishers::Memcache, 'when created' do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@host = 'host'
|
8
|
+
@memcache = Airbrush::Publishers::Memcache.new(@host)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should configure access to the remote memcache server host' do
|
12
|
+
@memcache.host.should == @host
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
describe Airbrush::Publishers::Memcache, 'when publishing' do
|
18
|
+
|
19
|
+
before do
|
20
|
+
@host = 'host'
|
21
|
+
@server = mock(MemCache)
|
22
|
+
@server.stub!(:set).and_return
|
23
|
+
MemCache.stub!(:new).and_return(@server)
|
24
|
+
@results = "results"
|
25
|
+
@id = 'id'
|
26
|
+
|
27
|
+
@memcache = Airbrush::Publishers::Memcache.new(@host)
|
28
|
+
@memcache.stub!(:unique_name).and_return('unique')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should connect to the remote memcache server' do
|
32
|
+
MemCache.should_receive(:new).and_return(@server)
|
33
|
+
@memcache.publish(@id, @results)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should calculate a unique memcache queue name for publishing results' do
|
37
|
+
@memcache.should_receive(:unique_name).with(@id).and_return('unique')
|
38
|
+
@memcache.publish(@id, @results)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should publish the given results to the remote memcache server' do
|
42
|
+
@server.should_receive(:set).with('unique', @results).and_return
|
43
|
+
@memcache.publish(@id, @results)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
|
+
|
3
|
+
describe Airbrush::Publishers::Publisher, 'when created' do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@publisher = Airbrush::Publishers::Publisher.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should be able to create a unique name given an id' do
|
10
|
+
@publisher.send(:unique_name, 'id').should == 'id'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should accept a symbol as an id to make unique' do
|
14
|
+
@publisher.send(:unique_name, :id).should == 'id'
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|