httpimagestore 0.5.0 → 1.0.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 (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
+