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.
Files changed (56) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +55 -0
  3. data/README.txt +39 -0
  4. data/Rakefile +4 -0
  5. data/bin/airbrush +61 -0
  6. data/bin/airbrush-example-client +60 -0
  7. data/config/hoe.rb +71 -0
  8. data/config/requirements.rb +17 -0
  9. data/lib/airbrush/client.rb +53 -0
  10. data/lib/airbrush/core_ext/get_args.rb +94 -0
  11. data/lib/airbrush/core_ext/timestamped_logger.rb +10 -0
  12. data/lib/airbrush/handler.rb +18 -0
  13. data/lib/airbrush/listeners/listener.rb +45 -0
  14. data/lib/airbrush/listeners/memcache.rb +53 -0
  15. data/lib/airbrush/listeners/socket.rb +0 -0
  16. data/lib/airbrush/listeners/webservice.rb +0 -0
  17. data/lib/airbrush/processors/image/image_processor.rb +31 -0
  18. data/lib/airbrush/processors/image/profiles/cmyk-profile.icc +0 -0
  19. data/lib/airbrush/processors/image/profiles/srgb-profile.icc +0 -0
  20. data/lib/airbrush/processors/image/rmagick.rb +116 -0
  21. data/lib/airbrush/processors/processor.rb +43 -0
  22. data/lib/airbrush/publishers/http.rb +13 -0
  23. data/lib/airbrush/publishers/memcache.rb +24 -0
  24. data/lib/airbrush/publishers/publisher.rb +16 -0
  25. data/lib/airbrush/server.rb +20 -0
  26. data/lib/airbrush/version.rb +9 -0
  27. data/lib/airbrush.rb +30 -0
  28. data/log/debug.log +0 -0
  29. data/script/destroy +14 -0
  30. data/script/generate +14 -0
  31. data/script/txt2html +74 -0
  32. data/setup.rb +1585 -0
  33. data/spec/airbrush/client_spec.rb +87 -0
  34. data/spec/airbrush/core_ext/get_args_spec.rb +0 -0
  35. data/spec/airbrush/handler_spec.rb +44 -0
  36. data/spec/airbrush/listeners/listener_spec.rb +18 -0
  37. data/spec/airbrush/listeners/memcache_spec.rb +131 -0
  38. data/spec/airbrush/processors/image/image_processor_spec.rb +56 -0
  39. data/spec/airbrush/processors/image/rmagick_spec.rb +179 -0
  40. data/spec/airbrush/processors/processor_spec.rb +110 -0
  41. data/spec/airbrush/publishers/memcache_spec.rb +46 -0
  42. data/spec/airbrush/publishers/publisher_spec.rb +17 -0
  43. data/spec/airbrush/server_spec.rb +57 -0
  44. data/spec/airbrush_spec.rb +9 -0
  45. data/spec/spec.opts +1 -0
  46. data/spec/spec_helper.rb +10 -0
  47. data/tasks/deployment.rake +34 -0
  48. data/tasks/environment.rake +7 -0
  49. data/tasks/rspec.rake +36 -0
  50. data/tasks/website.rake +17 -0
  51. data/website/index.html +11 -0
  52. data/website/index.txt +39 -0
  53. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  54. data/website/stylesheets/screen.css +138 -0
  55. data/website/template.rhtml +48 -0
  56. 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