httpimagestore 0.5.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/Gemfile +10 -12
  2. data/Gemfile.lock +57 -55
  3. data/README.md +829 -0
  4. data/VERSION +1 -1
  5. data/bin/httpimagestore +114 -180
  6. data/features/cache-control.feature +26 -90
  7. data/features/compatibility.feature +129 -0
  8. data/features/error-reporting.feature +207 -0
  9. data/features/health-check.feature +30 -0
  10. data/features/s3-store-and-thumbnail.feature +65 -0
  11. data/features/step_definitions/httpimagestore_steps.rb +66 -26
  12. data/features/support/env.rb +32 -5
  13. data/features/support/test.empty +0 -0
  14. data/httpimagestore.gemspec +60 -47
  15. data/lib/httpimagestore/aws_sdk_regions_hack.rb +23 -0
  16. data/lib/httpimagestore/configuration/file.rb +120 -0
  17. data/lib/httpimagestore/configuration/handler.rb +239 -0
  18. data/lib/httpimagestore/configuration/output.rb +119 -0
  19. data/lib/httpimagestore/configuration/path.rb +77 -0
  20. data/lib/httpimagestore/configuration/s3.rb +194 -0
  21. data/lib/httpimagestore/configuration/thumbnailer.rb +244 -0
  22. data/lib/httpimagestore/configuration.rb +126 -29
  23. data/lib/httpimagestore/error_reporter.rb +36 -0
  24. data/lib/httpimagestore/ruby_string_template.rb +26 -0
  25. data/load_test/load_test.1k.23a022f6e.m1.small-comp.csv +3 -0
  26. data/load_test/load_test.1k.ec9bde794.m1.small.csv +4 -0
  27. data/load_test/load_test.jmx +344 -0
  28. data/load_test/thumbnail_specs.csv +11 -0
  29. data/spec/configuration_file_spec.rb +309 -0
  30. data/spec/configuration_handler_spec.rb +124 -0
  31. data/spec/configuration_output_spec.rb +338 -0
  32. data/spec/configuration_path_spec.rb +92 -0
  33. data/spec/configuration_s3_spec.rb +571 -0
  34. data/spec/configuration_spec.rb +80 -105
  35. data/spec/configuration_thumbnailer_spec.rb +417 -0
  36. data/spec/ruby_string_template_spec.rb +43 -0
  37. data/spec/spec_helper.rb +61 -0
  38. data/spec/support/compute.jpg +0 -0
  39. data/spec/support/cuba_response_env.rb +40 -0
  40. data/spec/support/full.cfg +49 -0
  41. metadata +138 -84
  42. data/README.rdoc +0 -23
  43. data/features/httpimagestore.feature +0 -167
  44. data/lib/httpimagestore/image_path.rb +0 -54
  45. data/lib/httpimagestore/s3_service.rb +0 -37
  46. data/lib/httpimagestore/thumbnail_class.rb +0 -13
  47. data/spec/image_path_spec.rb +0 -72
  48. data/spec/test.cfg +0 -8
@@ -1,116 +1,91 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require_relative 'spec_helper'
2
2
  require 'httpimagestore/configuration'
3
- require 'sinatra/base'
4
3
 
5
- describe Configuration do
6
- it "should provide thumbnail classes" do
7
- c = Configuration.new do
8
- thumbnail_class 'small', 'crop', 128, 128, 'JPEG', :magick => 'option', :number => 42
9
- thumbnail_class 'tiny', 'pad', 32, 48, 'PNG'
10
- thumbnail_class 'test', 'pad', 32, 48
11
- end.get
12
-
13
- tc = c.thumbnail_classes['small']
14
- tc.name.should == 'small'
15
- tc.method.should == 'crop'
16
- tc.width.should == 128
17
- tc.height.should == 128
18
- tc.format.should == 'JPEG'
19
- tc.options.should == { :magick => 'option', :number => 42}
20
-
21
- tc = c.thumbnail_classes['tiny']
22
- tc.name.should == 'tiny'
23
- tc.method.should == 'pad'
24
- tc.width.should == 32
25
- tc.height.should == 48
26
- tc.format.should == 'PNG'
27
- tc.options.should == {}
28
-
29
- tc = c.thumbnail_classes['test']
30
- tc.name.should == 'test'
31
- tc.method.should == 'pad'
32
- tc.width.should == 32
33
- tc.height.should == 48
34
- tc.format.should == 'JPEG'
35
- tc.options.should == {}
36
- end
37
-
38
- it "should provide S3 key id and secret" do
39
- c = Configuration.new do
40
- s3_key 'abc', 'xyz'
41
- end.get
42
-
43
- c.s3_key_id.should == 'abc'
44
- c.s3_key_secret.should == 'xyz'
45
- end
46
-
47
- it "should provide S3 bucket" do
48
- c = Configuration.new do
49
- s3_bucket 'test'
50
- end.get
4
+ Configuration::Scope.logger = Logger.new('/dev/null')
51
5
 
52
- c.s3_bucket.should == 'test'
53
- end
54
-
55
- it "should provide thumbnailer_url defaulting to http://localhost:3100" do
56
- c = Configuration.new do
57
- end.get
58
-
59
- c.thumbnailer_url.should == 'http://localhost:3100'
60
-
61
- c = Configuration.new do
62
- thumbnailer_url 'http://test'
63
- end.get
6
+ require 'pathname'
7
+ Pathname.glob('lib/httpimagestore/configuration/*.rb').each do |conf|
8
+ require conf.relative_path_from(Pathname.new 'lib').to_s
9
+ end
64
10
 
65
- c.thumbnailer_url.should == 'http://test'
11
+ describe Configuration do
12
+ it 'should parse configuration file' do
13
+ Configuration.from_file(support_dir + 'full.cfg')
66
14
  end
67
15
 
68
- it "can set sinatra settings with configured keys" do
69
- sinatra = Sinatra.new
70
- c = Configuration.new do
71
- s3_key 'abc', 'xyz'
72
- s3_bucket 'test'
73
- thumbnailer_url 'http://test'
74
- end.put(sinatra)
75
-
76
- sinatra.settings.s3_key_id.should == 'abc'
77
- sinatra.settings.s3_key_secret.should == 'xyz'
78
- sinatra.settings.s3_bucket.should == 'test'
79
- sinatra.settings.thumbnailer_url.should == 'http://test'
16
+ it 'should read configuration from string' do
17
+ Configuration.read (support_dir + 'full.cfg').read
80
18
  end
81
19
 
82
- it "should load configuration from file" do
83
- Dir.chdir(File.dirname(__FILE__))
84
- c = Configuration.from_file('test.cfg').get
85
-
86
- c.s3_key_id.should == 'abc'
87
- c.s3_key_secret.should == 'xyz'
88
- c.s3_bucket.should == 'test'
89
- c.thumbnailer_url.should == 'http://test'
90
-
91
- tc = c.thumbnail_classes['small']
92
- tc.name.should == 'small'
93
- tc.method.should == 'crop'
94
- tc.width.should == 128
95
- tc.height.should == 128
96
- tc.format.should == 'JPEG'
97
- tc.options.should == { :magick => 'option', :number => 42}
98
-
99
- tc = c.thumbnail_classes['tiny']
100
- tc.name.should == 'tiny'
101
- tc.method.should == 'pad'
102
- tc.width.should == 32
103
- tc.height.should == 48
104
- tc.format.should == 'PNG'
105
- tc.options.should == {}
106
-
107
- tc = c.thumbnail_classes['test']
108
- tc.name.should == 'test'
109
- tc.method.should == 'pad'
110
- tc.width.should == 32
111
- tc.height.should == 48
112
- tc.format.should == 'JPEG'
113
- tc.options.should == {}
20
+ describe Configuration::SDL4RTagExtensions do
21
+ let :configuration do
22
+ SDL4R::read <<-'EOF'
23
+ test "hello" "world" abc=123 xyz=321 qwe=true asd="fd"
24
+ EOF
25
+ end
26
+
27
+ subject do
28
+ configuration.children.first
29
+ end
30
+
31
+ describe '#required_attributes' do
32
+ it 'should raise NoAttributeError if there is missing required attribute' do
33
+ subject.required_attributes('abc', 'xyz')
34
+
35
+ expect {
36
+ subject.required_attributes('abc', 'xyz', 'blah')
37
+ }.to raise_error Configuration::NoAttributeError, %{syntax error while parsing 'test "hello" "world" abc=123 asd="fd" qwe=true xyz=321': expected 'blah' attribute to be set}
38
+ end
39
+ end
40
+
41
+ describe '#grab_attributes' do
42
+ it 'should grab list of attributes form node' do
43
+ list = subject.grab_attributes('abc', 'xyz', 'qwe', 'opt', 'asd')
44
+ list.should == [123, 321, true, nil, 'fd']
45
+ end
46
+
47
+ it 'should raise UnexpectedAttributesError if unlisted attribut is on closed attribute list' do
48
+ expect {
49
+ subject.grab_attributes('abc', 'qwe')
50
+ }.to raise_error Configuration::UnexpectedAttributesError, %{syntax error while parsing 'test "hello" "world" abc=123 asd="fd" qwe=true xyz=321': unexpected attributes: 'xyz', 'asd'}
51
+ end
52
+ end
53
+
54
+ describe '#grab_attributes_with_remaining' do
55
+ it 'should grab list of attributes form node and all other remaining attributes' do
56
+ *list, remaining = *subject.grab_attributes_with_remaining('abc', 'xyz')
57
+ list.should == [123, 321]
58
+ remaining.should == {'qwe' => true, 'asd' => 'fd'}
59
+ end
60
+ end
61
+
62
+ describe '#valid_attribute_values' do
63
+ it 'should rise BadAttributeValueError if attribute value is not on defined value list' do
64
+ subject.valid_attribute_values('qwe', true, false)
65
+
66
+ expect {
67
+ subject.valid_attribute_values('qwe', 'hello', 'world')
68
+ }.to raise_error Configuration::BadAttributeValueError, %{syntax error while parsing 'test "hello" "world" abc=123 asd="fd" qwe=true xyz=321': expected 'qwe' attribute value to be "hello" or "world"; got: true}
69
+ end
70
+ end
71
+
72
+ describe '#grab_values' do
73
+ it 'should grab list of values' do
74
+ subject.grab_values('value 1', 'value 2').should == ['hello', 'world']
75
+ end
76
+
77
+ it 'should raise NoValueError if there is not enought values' do
78
+ expect {
79
+ subject.grab_values('value 1', 'value 2', 'value 3')
80
+ }.to raise_error Configuration::NoValueError, %{syntax error while parsing 'test "hello" "world" abc=123 asd="fd" qwe=true xyz=321': expected value 3}
81
+ end
82
+
83
+ it 'should raise UnexpectedValueError if there is too many values' do
84
+ expect {
85
+ subject.grab_values('value 1')
86
+ }.to raise_error Configuration::UnexpectedValueError, %{syntax error while parsing 'test "hello" "world" abc=123 asd="fd" qwe=true xyz=321': unexpected values: "world"}
87
+ end
88
+ end
114
89
  end
115
90
  end
116
91
 
@@ -0,0 +1,417 @@
1
+ require_relative 'spec_helper'
2
+ require 'httpimagestore/configuration'
3
+ Configuration::Scope.logger = Logger.new('/dev/null')
4
+
5
+ require 'httpimagestore/configuration/thumbnailer'
6
+ MemoryLimit.logger = Logger.new('/dev/null')
7
+
8
+ describe Configuration do
9
+ describe 'thumbnailer' do
10
+ subject do
11
+ Configuration.read('')
12
+ end
13
+
14
+ it 'should provide HTTPThumbnailerClient' do
15
+ subject.thumbnailer.should be_a HTTPThumbnailerClient
16
+ end
17
+
18
+ it 'should use default URL' do
19
+ subject.thumbnailer.server_url.should == 'http://localhost:3100'
20
+ end
21
+
22
+ it 'should allow to override default URL' do
23
+ subject = Configuration.read(<<-EOF, thumbnailer_url: 'http://1.1.1.1:8080')
24
+ EOF
25
+
26
+ subject.thumbnailer.server_url.should == 'http://1.1.1.1:8080'
27
+ end
28
+
29
+ it 'should get thumbnailer URL from configuration' do
30
+ subject = Configuration.read(<<-EOF, thumbnailer_url: 'http://1.1.1.1:8080')
31
+ thumbnailer url="http://2.2.2.2:1000"
32
+ EOF
33
+
34
+ subject.thumbnailer.server_url.should == 'http://2.2.2.2:1000'
35
+ end
36
+
37
+ describe 'error handling' do
38
+ it 'should raise StatementCollisionError on duplicate thumbnailer statement' do
39
+ expect {
40
+ Configuration.read(<<-EOF)
41
+ thumbnailer url="http://2.2.2.2:1000"
42
+ thumbnailer url="http://2.2.2.2:1000"
43
+ EOF
44
+ }.to raise_error Configuration::StatementCollisionError, %{syntax error while parsing 'thumbnailer url="http://2.2.2.2:1000"': only one thumbnailer type statement can be specified within context}
45
+ end
46
+
47
+ it 'should raise NoAttributeError on missing url attribute' do
48
+ expect {
49
+ Configuration.read(<<-EOF)
50
+ thumbnailer
51
+ EOF
52
+ }.to raise_error Configuration::NoAttributeError, %{syntax error while parsing 'thumbnailer': expected 'url' attribute to be set}
53
+ end
54
+ end
55
+ end
56
+
57
+ describe Configuration::Thumbnail::ThumbnailSpec do
58
+ describe 'should provide thumbnail spec array based on given locals' do
59
+ it 'where image name is hash key pointing to array where all values are string and options is hash' do
60
+ Configuration::Thumbnail::ThumbnailSpec.new('small', 'pad', 100, 100, 'jpeg', 'background-color' => 'red').render.should == {
61
+ 'small' =>
62
+ ['pad', '100', '100', 'jpeg', {'background-color' => 'red'}]
63
+ }
64
+ end
65
+
66
+ describe 'that are templated' do
67
+ it 'with local values for operation, width, height and values of options' do
68
+ locals = {
69
+ operation: 'fit',
70
+ width: 99,
71
+ height: 66,
72
+ format: 'png',
73
+ bg: 'white'
74
+ }
75
+
76
+ Configuration::Thumbnail::ThumbnailSpec.new('small', '#{operation}', '#{width}', '#{height}', '#{format}', 'background-color' => '#{bg}').render(locals).should == {
77
+ 'small' =>
78
+ ['fit', '99', '66', 'png', {'background-color' => 'white'}]
79
+ }
80
+ end
81
+
82
+ it 'with nested options provided by options key in <key>:<value>[,<key>:<value>]* format but not overwriting predefined options' do
83
+ locals = {
84
+ operation: 'fit',
85
+ width: 99,
86
+ height: 66,
87
+ format: 'png',
88
+ opts: 'background-color:blue,quality:100'
89
+ }
90
+
91
+ Configuration::Thumbnail::ThumbnailSpec.new('small', '#{operation}', '#{width}', '#{height}', '#{format}', 'options' => '#{opts}', 'background-color' => 'red').render(locals).should == {
92
+ 'small' =>
93
+ ['fit', '99', '66', 'png', {'background-color' => 'red', 'quality' => '100'}]
94
+ }
95
+ end
96
+ end
97
+
98
+ describe 'error handling' do
99
+ it 'should raise NoValueForSpecTemplatePlaceholerError on missing spec template value' do
100
+ locals = {
101
+ width: 99,
102
+ height: 66,
103
+ format: 'png'
104
+ }
105
+
106
+ expect {
107
+ Configuration::Thumbnail::ThumbnailSpec.new('small', '#{operation}', '#{width}', '#{height}', '#{format}').render(locals)
108
+ }.to raise_error Configuration::NoValueForSpecTemplatePlaceholerError, %q{cannot generate specification for thumbnail 'small': cannot generate value for attribute 'method' from template '#{operation}': no value for #{operation}}
109
+ end
110
+
111
+ it 'should raise NoValueForSpecTemplatePlaceholerError on missing option template value' do
112
+ locals = {
113
+ width: 99,
114
+ height: 66,
115
+ format: 'png',
116
+ }
117
+
118
+ expect {
119
+ Configuration::Thumbnail::ThumbnailSpec.new('small', '#{operation}', '#{width}', '#{height}', '#{format}', 'background-color' => '#{bg}').render(locals)
120
+ }.to raise_error Configuration::NoValueForSpecTemplatePlaceholerError, %q{cannot generate specification for thumbnail 'small': cannot generate value for attribute 'background-color' from template '#{bg}': no value for #{bg}}
121
+ end
122
+ end
123
+ end
124
+
125
+ describe 'thumbnail source image' do
126
+ before :all do
127
+ log = support_dir + 'server.log'
128
+ start_server(
129
+ "httpthumbnailer -f -d -l #{log}",
130
+ '/tmp/httpthumbnailer.pid',
131
+ log,
132
+ 'http://localhost:3100/'
133
+ )
134
+ end
135
+
136
+ let :state do
137
+ Configuration::RequestState.new(
138
+ (support_dir + 'compute.jpg').read,
139
+ operation: 'pad',
140
+ width: '10',
141
+ height: '10',
142
+ options: 'background-color:green',
143
+ path: nil
144
+ )
145
+ end
146
+
147
+ before :each do
148
+ subject.handlers[0].image_sources[0].realize(state)
149
+ end
150
+
151
+ describe 'thumbnailing to single spec' do
152
+ subject do
153
+ Configuration.read(<<-'EOF')
154
+ put ":operation" ":width" ":height" ":options" {
155
+ thumbnail "input" "original" operation="#{operation}" width="#{width}" height="#{height}" options="#{options}" quality=84 format="jpeg"
156
+ }
157
+ EOF
158
+ end
159
+
160
+ before :each do
161
+ state.images['input'].source_path = 'test.in'
162
+ state.images['input'].source_url = 'file://test.in'
163
+ end
164
+
165
+ it 'should provide thumbnail data' do
166
+ subject.handlers[0].image_sources[1].realize(state)
167
+ state.images['original'].data.should_not be_nil
168
+ end
169
+
170
+ it 'should set thumbnail mime type' do
171
+ subject.handlers[0].image_sources[1].realize(state)
172
+ state.images['original'].mime_type.should == 'image/jpeg'
173
+ end
174
+
175
+ it 'should use input image source path and url' do
176
+ subject.handlers[0].image_sources[1].realize(state)
177
+ state.images['original'].source_path.should == 'test.in'
178
+ state.images['original'].source_url.should == 'file://test.in'
179
+ end
180
+
181
+ it 'should set input image mime type' do
182
+ subject.handlers[0].image_sources[1].realize(state)
183
+ state.images['input'].mime_type.should == 'image/jpeg'
184
+ end
185
+
186
+ describe 'memory limit' do
187
+ let :state do
188
+ Configuration::RequestState.new(
189
+ (support_dir + 'compute.jpg').read,
190
+ {operation: 'pad',
191
+ width: '10',
192
+ height: '10',
193
+ options: 'background-color:green',
194
+ path: nil},
195
+ MemoryLimit.new(10)
196
+ )
197
+ end
198
+
199
+ it 'should raise MemoryLimit::MemoryLimitedExceededError when limit is exceeded' do
200
+ expect {
201
+ subject.handlers[0].image_sources[1].realize(state)
202
+ }.to raise_error MemoryLimit::MemoryLimitedExceededError
203
+ end
204
+ end
205
+
206
+ describe 'error handling' do
207
+ it 'should raise Thumbnail::ThumbnailingError on realization of bad thumbnail sepc' do
208
+ state = Configuration::RequestState.new(
209
+ (support_dir + 'compute.jpg').read,
210
+ operation: 'pad',
211
+ width: '0',
212
+ height: '10',
213
+ options: 'background-color:green',
214
+ path: nil
215
+ )
216
+
217
+ expect {
218
+ subject.handlers[0].image_sources[0].realize(state)
219
+ subject.handlers[0].image_sources[1].realize(state)
220
+ }.to raise_error Configuration::Thumbnail::ThumbnailingError # WTF?, "thumbnailing of 'input' into 'original' failed: at least one image dimension is zero: 0x10"
221
+ end
222
+
223
+ it 'should raise NoValueError on missing source image name' do
224
+ expect {
225
+ Configuration.read(<<-EOF)
226
+ put {
227
+ thumbnail
228
+ }
229
+ EOF
230
+ }.to raise_error Configuration::NoValueError, %{syntax error while parsing 'thumbnail': expected source image name}
231
+ end
232
+
233
+ it 'should raise NoValueError on missing source image name' do
234
+ expect {
235
+ Configuration.read(<<-EOF)
236
+ put {
237
+ thumbnail "input"
238
+ }
239
+ EOF
240
+ }.to raise_error Configuration::NoValueError, %{syntax error while parsing 'thumbnail "input"': expected thumbnail image name}
241
+ end
242
+ end
243
+ end
244
+
245
+ describe 'thumbnailing to multiple specs' do
246
+ subject do
247
+ Configuration.read(<<-'EOF')
248
+ put ":operation" ":width" ":height" ":options" {
249
+ thumbnail "input" {
250
+ "original" operation="#{operation}" width="#{width}" height="#{height}" options="#{options}" quality=84 format="jpeg"
251
+ "small" operation="crop" width=128 height=128 format="jpeg"
252
+ "padded" operation="pad" width=128 height=128 format="png" background-color="gray"
253
+ }
254
+ }
255
+ EOF
256
+ end
257
+
258
+ before :each do
259
+ state.images['input'].source_path = 'test.in'
260
+ state.images['input'].source_url = 'file://test.in'
261
+ end
262
+
263
+ it 'should provide thumbnail data' do
264
+ subject.handlers[0].image_sources[1].realize(state)
265
+ state.images['original'].data.should_not be_nil
266
+ state.images['small'].data.should_not be_nil
267
+ state.images['padded'].data.should_not be_nil
268
+ end
269
+
270
+ it 'should set thumbnail mime type' do
271
+ subject.handlers[0].image_sources[1].realize(state)
272
+ state.images['original'].mime_type.should == 'image/jpeg'
273
+ state.images['small'].mime_type.should == 'image/jpeg'
274
+ state.images['padded'].mime_type.should == 'image/png'
275
+ end
276
+
277
+ it 'should set input image mime type' do
278
+ subject.handlers[0].image_sources[1].realize(state)
279
+ state.images['input'].mime_type.should == 'image/jpeg'
280
+ end
281
+
282
+ it 'should use input image source path and url' do
283
+ subject.handlers[0].image_sources[1].realize(state)
284
+ state.images['original'].source_path.should == 'test.in'
285
+ state.images['original'].source_url.should == 'file://test.in'
286
+ state.images['small'].source_path.should == 'test.in'
287
+ state.images['small'].source_url.should == 'file://test.in'
288
+ state.images['padded'].source_path.should == 'test.in'
289
+ state.images['padded'].source_url.should == 'file://test.in'
290
+ end
291
+
292
+ describe 'memory limit' do
293
+ let :state do
294
+ Configuration::RequestState.new(
295
+ (support_dir + 'compute.jpg').read,
296
+ {operation: 'pad',
297
+ width: '10',
298
+ height: '10',
299
+ options: 'background-color:green',
300
+ path: nil},
301
+ MemoryLimit.new(10)
302
+ )
303
+ end
304
+
305
+ it 'should raise MemoryLimit::MemoryLimitedExceededError when limit is exceeded' do
306
+ expect {
307
+ subject.handlers[0].image_sources[1].realize(state)
308
+ }.to raise_error MemoryLimit::MemoryLimitedExceededError
309
+ end
310
+ end
311
+
312
+ describe 'conditional inclusion support' do
313
+ subject do
314
+ Configuration.read(<<-'EOF')
315
+ put {
316
+ thumbnail "input" {
317
+ "original" if-image-name-on="#{list}"
318
+ "small" if-image-name-on="#{list}"
319
+ "padded" if-image-name-on="#{list}"
320
+ }
321
+ }
322
+ EOF
323
+ end
324
+
325
+ let :state do
326
+ Configuration::RequestState.new(
327
+ (support_dir + 'compute.jpg').read,
328
+ operation: 'pad',
329
+ width: '10',
330
+ height: '10',
331
+ options: 'background-color:green',
332
+ path: nil,
333
+ list: 'small,padded'
334
+ )
335
+ end
336
+
337
+ it 'should provide thumbnails that name match if-image-name-on list' do
338
+ subject.handlers[0].image_sources[1].realize(state)
339
+ state.images.should_not include 'original'
340
+ state.images['small'].data.should_not be_nil
341
+ state.images['padded'].data.should_not be_nil
342
+ end
343
+ end
344
+
345
+ describe 'error handling' do
346
+ it 'should raise Thumbnail::ThumbnailingError on realization of bad thumbnail sepc' do
347
+ state = Configuration::RequestState.new(
348
+ (support_dir + 'compute.jpg').read,
349
+ operation: 'pad',
350
+ width: '0',
351
+ height: '10',
352
+ options: 'background-color:green',
353
+ path: nil
354
+ )
355
+
356
+ subject.handlers[0].image_sources[0].realize(state)
357
+
358
+ expect {
359
+ subject.handlers[0].image_sources[1].realize(state)
360
+ }.to raise_error Configuration::Thumbnail::ThumbnailingError, "thumbnailing of 'input' into 'original' failed: at least one image dimension is zero: 0x10"
361
+ end
362
+
363
+ it 'should raise NoValueError on missing source image name' do
364
+ expect {
365
+ Configuration.read(<<-EOF)
366
+ put {
367
+ thumbnail {
368
+ }
369
+ }
370
+ EOF
371
+ }.to raise_error Configuration::NoValueError, %{syntax error while parsing 'thumbnail': expected source image name}
372
+ end
373
+ end
374
+ end
375
+ end
376
+
377
+ describe 'conditional inclusion support' do
378
+ let :state do
379
+ Configuration::RequestState.new(
380
+ (support_dir + 'compute.jpg').read,
381
+ list: 'thumbnail1,input4,thumbnail5,input6'
382
+ )
383
+ end
384
+
385
+ subject do
386
+ Configuration.read(<<-'EOF')
387
+ put {
388
+ thumbnail "input1" "thumbnail1" if-image-name-on="#{list}"
389
+ thumbnail "input2" "thumbnail2" if-image-name-on="#{list}"
390
+ thumbnail "input3" if-image-name-on="#{list}" {
391
+ "thumbnail3"
392
+ }
393
+ thumbnail "input4" if-image-name-on="#{list}" {
394
+ "thumbnail4"
395
+ }
396
+ thumbnail "input5" if-image-name-on="#{list}" {
397
+ "thumbnail5" if-image-name-on="#{list}"
398
+ }
399
+ thumbnail "input6" if-image-name-on="#{list}" {
400
+ "thumbnail6" if-image-name-on="#{list}"
401
+ }
402
+ }
403
+ EOF
404
+ end
405
+
406
+ it 'should mark source to be included when output image name in oneline and destination image name in multiline statement match if-image-name-on list' do
407
+ subject.handlers[0].image_sources[1].excluded?(state).should be_false
408
+ subject.handlers[0].image_sources[2].excluded?(state).should be_true
409
+ subject.handlers[0].image_sources[3].excluded?(state).should be_true
410
+ subject.handlers[0].image_sources[4].excluded?(state).should be_false
411
+ subject.handlers[0].image_sources[5].excluded?(state).should be_true
412
+ subject.handlers[0].image_sources[6].excluded?(state).should be_false
413
+ end
414
+ end
415
+ end
416
+ end
417
+
@@ -0,0 +1,43 @@
1
+ require_relative 'spec_helper'
2
+ require 'httpimagestore/ruby_string_template'
3
+
4
+ describe RubyStringTemplate do
5
+ subject do
6
+ RubyStringTemplate.new('>#{hello}-#{world}#{test}<')
7
+ end
8
+
9
+ it 'should replace holders with given values' do
10
+ subject.render(hello: 'hello', world: 'world', test: 123).should == '>hello-world123<'
11
+ end
12
+
13
+ it 'should raise NoValueForTemplatePlaceholderError if template value was not provided' do
14
+ expect {
15
+ subject.render(hello: 'hello', test: 123)
16
+ }.to raise_error RubyStringTemplate::NoValueForTemplatePlaceholderError, %q{no value for '#{world}' in template '>#{hello}-#{world}#{test}<'}
17
+ end
18
+
19
+ describe 'with custom resolver' do
20
+ subject do
21
+ RubyStringTemplate.new('>#{hello}-#{world}#{test}<') do |locals, name|
22
+ case name
23
+ when :test
24
+ 321
25
+ else
26
+ locals[name]
27
+ end
28
+ end
29
+ end
30
+
31
+ it 'should ask for values using provided resolver' do
32
+ subject.render(hello: 'hello', world: 'world').should == '>hello-world321<'
33
+ subject.render(hello: 'hello', world: 'world', test: 123).should == '>hello-world321<'
34
+ end
35
+
36
+ it 'should raise NoValueForTemplatePlaceholderError if template value was not provided' do
37
+ expect {
38
+ subject.render(hello: 'hello', test: 123)
39
+ }.to raise_error RubyStringTemplate::NoValueForTemplatePlaceholderError, %q{no value for '#{world}' in template '>#{hello}-#{world}#{test}<'}
40
+ end
41
+ end
42
+ end
43
+