httpthumbnailer 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +4 -3
  3. data/Gemfile.lock +12 -12
  4. data/README.md +242 -68
  5. data/Rakefile +8 -2
  6. data/VERSION +1 -1
  7. data/bin/httpthumbnailer +35 -7
  8. data/lib/httpthumbnailer/error_reporter.rb +4 -2
  9. data/lib/httpthumbnailer/ownership.rb +54 -0
  10. data/lib/httpthumbnailer/plugin.rb +87 -0
  11. data/lib/httpthumbnailer/plugin/thumbnailer.rb +22 -427
  12. data/lib/httpthumbnailer/plugin/thumbnailer/service.rb +163 -0
  13. data/lib/httpthumbnailer/plugin/thumbnailer/service/built_in_plugins.rb +134 -0
  14. data/lib/httpthumbnailer/plugin/thumbnailer/service/images.rb +295 -0
  15. data/lib/httpthumbnailer/plugin/thumbnailer/service/magick.rb +208 -0
  16. data/lib/httpthumbnailer/thumbnail_specs.rb +130 -37
  17. data/lib/httpthumbnailer/thumbnailer.rb +29 -11
  18. metadata +30 -81
  19. data/.rspec +0 -1
  20. data/features/httpthumbnailer.feature +0 -24
  21. data/features/identify.feature +0 -31
  22. data/features/step_definitions/httpthumbnailer_steps.rb +0 -159
  23. data/features/support/env.rb +0 -106
  24. data/features/support/test-large.jpg +0 -0
  25. data/features/support/test-transparent.png +0 -0
  26. data/features/support/test.jpg +0 -0
  27. data/features/support/test.png +0 -0
  28. data/features/support/test.txt +0 -1
  29. data/features/thumbnail.feature +0 -269
  30. data/features/thumbnails.feature +0 -158
  31. data/httpthumbnailer.gemspec +0 -121
  32. data/load_test/extralarge.jpg +0 -0
  33. data/load_test/large.jpg +0 -0
  34. data/load_test/large.png +0 -0
  35. data/load_test/load_test-374846090-1.1.0-rc1-identify-only.csv +0 -3
  36. data/load_test/load_test-374846090-1.1.0-rc1.csv +0 -11
  37. data/load_test/load_test-cd9679c.csv +0 -10
  38. data/load_test/load_test-v0.3.1.csv +0 -10
  39. data/load_test/load_test.jmx +0 -733
  40. data/load_test/medium.jpg +0 -0
  41. data/load_test/small.jpg +0 -0
  42. data/load_test/soak_test-ac0c6bcbe5e-broken-libjpeg-tatoos.csv +0 -11
  43. data/load_test/soak_test-cd9679c.csv +0 -10
  44. data/load_test/soak_test-f98334a-tatoos.csv +0 -11
  45. data/load_test/soak_test.jmx +0 -754
  46. data/load_test/tiny.jpg +0 -0
  47. data/load_test/v0.0.13-loading.csv +0 -7
  48. data/load_test/v0.0.13.csv +0 -7
  49. data/load_test/v0.0.14-no-optimization.csv +0 -10
  50. data/load_test/v0.0.14.csv +0 -10
  51. data/spec/image_processing_spec.rb +0 -148
  52. data/spec/plugin_thumbnailer_spec.rb +0 -318
  53. data/spec/spec_helper.rb +0 -14
  54. data/spec/support/square_even.png +0 -0
  55. data/spec/support/square_odd.png +0 -0
  56. data/spec/support/test_image.rb +0 -16
  57. data/spec/thumbnail_specs_spec.rb +0 -43
Binary file
@@ -1,7 +0,0 @@
1
- sampler_label,aggregate_report_count,average,aggregate_report_median,aggregate_report_90%_line,aggregate_report_min,aggregate_report_max,aggregate_report_error%,aggregate_report_rate,aggregate_report_bandwidth
2
- tiny,16,55,46,50,43,187,0.0,0.9256045354622238,67.94967748466966
3
- small,16,141,140,145,132,150,0.0,0.921605898277749,77.99539917055469
4
- medium,16,118,114,129,110,137,0.0,0.9236808682600162,82.94996969172152
5
- large,16,146,146,154,128,163,0.0625,0.9237875288683602,93.9015025079388
6
- extra large,15,678,675,691,655,747,0.0,0.8956293288750896,89.44310747925125
7
- RAZEM,79,222,140,675,43,747,0.012658227848101266,4.471613743136922,401.30654964057277
@@ -1,7 +0,0 @@
1
- sampler_label,aggregate_report_count,average,aggregate_report_median,aggregate_report_90%_line,aggregate_report_min,aggregate_report_max,aggregate_report_error%,aggregate_report_rate,aggregate_report_bandwidth
2
- tiny,11,54,56,59,47,66,0.0,0.17140898183064793,13.240004713746998
3
- small,11,79,80,85,72,87,0.0,0.17137159593693524,14.533449349956378
4
- medium,11,127,129,133,110,140,0.0,0.1712595360423478,15.379741870037364
5
- large,11,452,477,510,114,516,0.09090909090909091,0.17129153819801302,16.917609378406365
6
- extra large,10,5654,5604,5868,5408,6147,0.0,0.157855688329729,15.774623760832847
7
- RAZEM,54,1192,126,5604,47,6147,0.018518518518518517,0.8372482441043769,75.29186798416981
@@ -1,10 +0,0 @@
1
- sampler_label,aggregate_report_count,average,aggregate_report_median,aggregate_report_90%_line,aggregate_report_min,aggregate_report_max,aggregate_report_error%,aggregate_report_rate,aggregate_report_bandwidth
2
- tiny,5,54,54,54,49,60,0.0,0.08831113780069944,6.821345464339962
3
- small,5,77,78,80,73,81,0.0,0.088281512085739,7.486858469286862
4
- medium,5,122,120,133,111,133,0.0,0.08818808755313333,7.919617522752526
5
- large,5,511,507,521,491,537,0.0,0.0875871492134674,9.501409468827735
6
- extra large,4,6220,5973,6931,5748,6931,0.0,0.08261219769098907,4.524873370990727
7
- extra large w prescale,4,6066,6046,6257,5825,6257,0.0,0.08256615613260124,4.769001826776204
8
- large PNG,4,532,536,568,486,568,0.0,0.0931619154089808,9.325471341065771
9
- large PNG w prescale,4,547,541,571,533,571,0.0,0.0930925339787749,10.098630812465089
10
- RAZEM,36,1591,500,6046,49,6931,0.0,0.6278996755851676,53.82679507360379
@@ -1,10 +0,0 @@
1
- sampler_label,aggregate_report_count,average,aggregate_report_median,aggregate_report_90%_line,aggregate_report_min,aggregate_report_max,aggregate_report_error%,aggregate_report_rate,aggregate_report_bandwidth
2
- tiny,48,51,50,57,42,72,0.0,0.3999566713606026,29.36127232049861
3
- small,48,147,146,154,138,171,0.0,0.39959707295144065,33.81785345609843
4
- medium,48,118,119,125,105,132,0.0,0.3996968965201389,35.89426455771041
5
- large,48,143,142,153,130,183,0.0,0.3995804405374357,43.283068403176664
6
- extra large,48,656,647,676,636,894,0.0,0.3977757704833804,21.74248576708571
7
- extra large w prescale,48,593,590,604,579,654,0.0,0.39787468605200554,23.01534097031689
8
- large PNG,48,509,505,540,483,587,0.0,0.39790766884134265,39.83040221833525
9
- large PNG w prescale,48,326,307,361,299,608,0.0,0.39849898714840765,43.22196289808389
10
- RAZEM,384,318,183,642,42,894,0.0,3.1306559702587684,265.0230977596243
@@ -1,148 +0,0 @@
1
- require_relative 'spec_helper'
2
- require 'unicorn-cuba-base'
3
- require 'httpthumbnailer/plugin/thumbnailer'
4
-
5
- class StubImage
6
- include Plugin::Thumbnailer::ImageProcessing
7
-
8
- @@created = 0
9
- @@destroyed = 0
10
-
11
- def self.alive
12
- @@created - @@destroyed
13
- end
14
-
15
- def initialize
16
- @@created += 1
17
- @destroyed = false
18
- @final = false
19
- end
20
-
21
- def destroy!
22
- fail "already destroyed: #{self}" if @destroyed
23
- @destroyed = true
24
- @@destroyed += 1
25
- self
26
- end
27
-
28
- def copy
29
- StubImage.new
30
- end
31
-
32
- def destroyed?
33
- @destroyed
34
- end
35
-
36
- def final!
37
- @final = true
38
- self
39
- end
40
-
41
- def final?
42
- @final
43
- end
44
- end
45
-
46
- describe 'image processing module' do
47
- it '#replace will return new or original image makng sure that all other images are destroyed' do
48
- image = StubImage.new.replace do |image|
49
- image.copy.replace do |image|
50
- image.final!
51
- end
52
- end
53
- image.should be_final
54
- image.should_not be_destroyed
55
-
56
- StubImage.alive.should == 1
57
- image.destroy!
58
-
59
- image = StubImage.new.replace do |image|
60
- image.copy.replace do |image|
61
- image.copy.replace do |image|
62
- image.copy
63
- end.replace do |image|
64
- image.final!
65
- end
66
- end
67
- end
68
- image.should be_final
69
- image.should_not be_destroyed
70
-
71
- StubImage.alive.should == 1
72
- image.destroy!
73
-
74
- image = StubImage.new.replace do |image|
75
- image.copy.replace do |image|
76
- image = image.copy.replace do |image|
77
- image.copy
78
- end
79
- image.replace do |image|
80
- image.copy.replace do |image|
81
- image.final!
82
- end
83
- end
84
- end
85
- end
86
- image.should be_final
87
- image.should_not be_destroyed
88
-
89
- StubImage.alive.should == 1
90
- image.destroy!
91
- end
92
-
93
- it '#replace will destroy created images on exception' do
94
- lambda {
95
- image = StubImage.new.replace do |image|
96
- image.copy.replace do |image|
97
- image.copy.replace do |image|
98
- image.copy.replace do |image|
99
- fail 'test'
100
- end
101
- end
102
- end
103
- end
104
- }.should raise_error(RuntimeError, 'test')
105
-
106
- StubImage.alive.should == 0
107
-
108
- lambda {
109
- image = StubImage.new.replace do |image|
110
- image.copy.replace do |image|
111
- image.copy.replace do |image|
112
- image.copy
113
- end.replace do |image|
114
- fail 'test'
115
- image.final!
116
- end
117
- end
118
- end
119
- }.should raise_error(RuntimeError, 'test')
120
-
121
- StubImage.alive.should == 0
122
- end
123
-
124
- it '#use should return image to be used for multiple processing and destroy it at the end' do
125
- image = StubImage.new.use do |image|
126
- i1 = image.replace do |image|
127
- image.copy.final!
128
- end
129
- i1.should be_final
130
- i1.destroy!
131
-
132
- i2 = image.replace do |image|
133
- image.copy.final!
134
- end
135
- i2.should be_final
136
- i2.destroy!
137
-
138
- i3 = image.replace do |image|
139
- nil
140
- end
141
- i3.should_not be_destroyed
142
- end
143
- image.should be_destroyed
144
-
145
- StubImage.alive.should == 0
146
- end
147
- end
148
-
@@ -1,318 +0,0 @@
1
- require_relative 'spec_helper'
2
- require 'unicorn-cuba-base'
3
- require 'httpthumbnailer/plugin/thumbnailer'
4
- require 'httpthumbnailer/thumbnail_specs'
5
-
6
- describe Plugin::Thumbnailer::Service do
7
- subject do
8
- service = Plugin::Thumbnailer::Service.new
9
- service.setup_default_methods
10
- service
11
- end
12
-
13
- def square_odd
14
- subject.load(TestImage.io('square_odd.png'))
15
- end
16
-
17
- def square_even
18
- subject.load(TestImage.io('square_even.png'))
19
- end
20
-
21
- before :each do
22
- @before_stats = Plugin::Thumbnailer::Service.stats.to_hash
23
- end
24
-
25
- def diff_stat(name)
26
- Plugin::Thumbnailer::Service.stats.to_hash[name] - @before_stats[name]
27
- end
28
-
29
- it "should load images from provided blob" do
30
- image = subject.load(TestImage.io('square_odd.png'))
31
- image.format.should == 'PNG'
32
- image.width.should == 99
33
- image.height.should == 99
34
- end
35
-
36
- describe 'encoding' do
37
- describe 'interlace' do
38
- it 'should fail if unknown interlace is specified' do
39
- expect {
40
- square_odd.thumbnail(ThumbnailSpec.from_uri('crop,99,99,jpeg,interlace:Blah')) do |thumbnail|
41
- end
42
- }.to raise_error RuntimeError, 'unsupported interlace: Blah'
43
- end
44
-
45
- it 'should allow to construct progressive JPEG with interlace JPEGInterlace or LineInterlace or PlaneInerlace' do
46
- square_odd.thumbnail(ThumbnailSpec.from_uri('crop,99,99,jpeg')) do |thumbnail|
47
- image = Magick::Image.from_blob(thumbnail.data).first
48
- image.columns.should == 99
49
- image.rows.should == 99
50
- image.interlace.to_s.should == 'NoInterlace'
51
- image.destroy!
52
- end
53
-
54
- square_odd.thumbnail(ThumbnailSpec.from_uri('crop,99,99,jpeg,interlace:JPEGInterlace')) do |thumbnail|
55
- image = Magick::Image.from_blob(thumbnail.data).first
56
- image.columns.should == 99
57
- image.rows.should == 99
58
- image.interlace.to_s.should == 'JPEGInterlace'
59
- image.destroy!
60
- end
61
-
62
- square_odd.thumbnail(ThumbnailSpec.from_uri('crop,99,99,jpeg,interlace:LineInterlace')) do |thumbnail|
63
- image = Magick::Image.from_blob(thumbnail.data).first
64
- image.columns.should == 99
65
- image.rows.should == 99
66
- image.interlace.to_s.should == 'JPEGInterlace'
67
- image.destroy!
68
- end
69
-
70
- square_odd.thumbnail(ThumbnailSpec.from_uri('crop,99,99,jpeg,interlace:PlaneInterlace')) do |thumbnail|
71
- image = Magick::Image.from_blob(thumbnail.data).first
72
- image.columns.should == 99
73
- image.rows.should == 99
74
- image.interlace.to_s.should == 'JPEGInterlace'
75
- image.destroy!
76
- end
77
- end
78
- end
79
- end
80
-
81
- describe 'thumbnailing' do
82
- describe 'cropping' do
83
- it 'should be a noop if same width and height are used as original image' do
84
- square_odd.thumbnail(ThumbnailSpec.from_uri('crop,99,99,png')) do |thumbnail|
85
- thumbnail.width.should == 99
86
- thumbnail.height.should == 99
87
- end
88
- diff_stat(:total_images_created_resize).should == 0
89
- diff_stat(:total_images_created_crop).should == 0
90
- end
91
-
92
- it 'should be a resize operation if same proportions are used as original image' do
93
- square_odd.thumbnail(ThumbnailSpec.from_uri('crop,50,50,png')) do |thumbnail|
94
- thumbnail.width.should == 50
95
- thumbnail.height.should == 50
96
- end
97
- diff_stat(:total_images_created_resize).should == 1
98
- diff_stat(:total_images_created_crop).should == 0
99
- end
100
-
101
- it 'should be a resize and crop operation if different proportions are used as original image' do
102
- square_odd.thumbnail(ThumbnailSpec.from_uri('crop,50,60,png')) do |thumbnail|
103
- thumbnail.width.should == 50
104
- thumbnail.height.should == 60
105
- end
106
- diff_stat(:total_images_created_resize).should == 1
107
- diff_stat(:total_images_created_crop).should == 1
108
- end
109
-
110
- it 'should crop to even and odd proportions' do
111
- square_odd.thumbnail(ThumbnailSpec.from_uri('crop,33,33,png')) do |thumbnail|
112
- thumbnail.width.should == 33
113
- thumbnail.height.should == 33
114
- end
115
-
116
- square_odd.thumbnail(ThumbnailSpec.from_uri('crop,91,91,png')) do |thumbnail|
117
- thumbnail.width.should == 91
118
- thumbnail.height.should == 91
119
- end
120
-
121
- square_odd.thumbnail(ThumbnailSpec.from_uri('crop,33,50,png')) do |thumbnail|
122
- thumbnail.width.should == 33
123
- thumbnail.height.should == 50
124
- end
125
- end
126
-
127
- it 'should crop with upwards scaling' do
128
- square_odd.thumbnail(ThumbnailSpec.from_uri('crop,198,99,png')) do |thumbnail|
129
- thumbnail.width.should == 198
130
- thumbnail.height.should == 99
131
- #show_blob thumbnail.data
132
- end
133
- end
134
-
135
- describe 'floating' do
136
- it 'should crop with floating horizontally' do
137
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png')) do |thumbnail|
138
- thumbnail.width.should == 100
139
- thumbnail.height.should == 200
140
- #show_blob thumbnail.data
141
- end
142
-
143
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png,float-x:1.0')) do |thumbnail|
144
- thumbnail.width.should == 100
145
- thumbnail.height.should == 200
146
- #show_blob thumbnail.data
147
- end
148
-
149
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png,float-x:0.0')) do |thumbnail|
150
- thumbnail.width.should == 100
151
- thumbnail.height.should == 200
152
- #show_blob thumbnail.data
153
- end
154
-
155
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png,float-x:0.8')) do |thumbnail|
156
- thumbnail.width.should == 100
157
- thumbnail.height.should == 200
158
- #show_blob thumbnail.data
159
- end
160
-
161
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png,float-x:-9.8')) do |thumbnail|
162
- thumbnail.width.should == 100
163
- thumbnail.height.should == 200
164
- #show_blob thumbnail.data
165
- end
166
-
167
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png,float-x:3')) do |thumbnail|
168
- thumbnail.width.should == 100
169
- thumbnail.height.should == 200
170
- #show_blob thumbnail.data
171
- end
172
- end
173
-
174
- it 'should crop with floating vertically' do
175
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png')) do |thumbnail|
176
- thumbnail.width.should == 200
177
- thumbnail.height.should == 100
178
- #show_blob thumbnail.data
179
- end
180
-
181
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png,float-y:1.0')) do |thumbnail|
182
- thumbnail.width.should == 200
183
- thumbnail.height.should == 100
184
- #show_blob thumbnail.data
185
- end
186
-
187
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png,float-y:0.0')) do |thumbnail|
188
- thumbnail.width.should == 200
189
- thumbnail.height.should == 100
190
- #show_blob thumbnail.data
191
- end
192
-
193
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png,float-y:0.8')) do |thumbnail|
194
- thumbnail.width.should == 200
195
- thumbnail.height.should == 100
196
- #show_blob thumbnail.data
197
- end
198
-
199
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png,float-y:-9.8')) do |thumbnail|
200
- thumbnail.width.should == 200
201
- thumbnail.height.should == 100
202
- #show_blob thumbnail.data
203
- end
204
-
205
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png,float-y:3')) do |thumbnail|
206
- thumbnail.width.should == 200
207
- thumbnail.height.should == 100
208
- #show_blob thumbnail.data
209
- end
210
- end
211
-
212
- it 'should crop with floating horizontally and vertically' do
213
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,100,200,png,float-x:0.25,float-y:0.25')) do |thumbnail|
214
- thumbnail.width.should == 100
215
- thumbnail.height.should == 200
216
- #show_blob thumbnail.data
217
- end
218
-
219
- square_even.thumbnail(ThumbnailSpec.from_uri('crop,200,100,png,float-x:0.25,float-y:0.25')) do |thumbnail|
220
- thumbnail.width.should == 200
221
- thumbnail.height.should == 100
222
- #show_blob thumbnail.data
223
- end
224
- end
225
-
226
- it 'should pad with floating horizontaly' do
227
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png')) do |thumbnail|
228
- thumbnail.width.should == 200
229
- thumbnail.height.should == 100
230
- #show_blob thumbnail.data
231
- end
232
-
233
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png,float-x:1.0')) do |thumbnail|
234
- thumbnail.width.should == 200
235
- thumbnail.height.should == 100
236
- #show_blob thumbnail.data
237
- end
238
-
239
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png,float-x:0.0')) do |thumbnail|
240
- thumbnail.width.should == 200
241
- thumbnail.height.should == 100
242
- #show_blob thumbnail.data
243
- end
244
-
245
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png,float-x:0.8')) do |thumbnail|
246
- thumbnail.width.should == 200
247
- thumbnail.height.should == 100
248
- #show_blob thumbnail.data
249
- end
250
-
251
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png,float-x:-9.8')) do |thumbnail|
252
- thumbnail.width.should == 200
253
- thumbnail.height.should == 100
254
- #show_blob thumbnail.data
255
- end
256
-
257
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png,float-x:3')) do |thumbnail|
258
- thumbnail.width.should == 200
259
- thumbnail.height.should == 100
260
- #show_blob thumbnail.data
261
- end
262
- end
263
-
264
- it 'should pad with floating verticaly' do
265
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png')) do |thumbnail|
266
- thumbnail.width.should == 100
267
- thumbnail.height.should == 200
268
- #show_blob thumbnail.data
269
- end
270
-
271
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png,float-y:1.0')) do |thumbnail|
272
- thumbnail.width.should == 100
273
- thumbnail.height.should == 200
274
- #show_blob thumbnail.data
275
- end
276
-
277
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png,float-y:0.0')) do |thumbnail|
278
- thumbnail.width.should == 100
279
- thumbnail.height.should == 200
280
- #show_blob thumbnail.data
281
- end
282
-
283
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png,float-y:0.8')) do |thumbnail|
284
- thumbnail.width.should == 100
285
- thumbnail.height.should == 200
286
- #show_blob thumbnail.data
287
- end
288
-
289
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png,float-y:-9.8')) do |thumbnail|
290
- thumbnail.width.should == 100
291
- thumbnail.height.should == 200
292
- #show_blob thumbnail.data
293
- end
294
-
295
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png,float-y:3')) do |thumbnail|
296
- thumbnail.width.should == 100
297
- thumbnail.height.should == 200
298
- #show_blob thumbnail.data
299
- end
300
- end
301
-
302
- it 'should pad with floating horizontally and vertically' do
303
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,100,200,png,float-x:0.25,float-y:0.25')) do |thumbnail|
304
- thumbnail.width.should == 100
305
- thumbnail.height.should == 200
306
- #show_blob thumbnail.data
307
- end
308
-
309
- square_even.thumbnail(ThumbnailSpec.from_uri('pad,200,100,png,float-x:0.25,float-y:0.25')) do |thumbnail|
310
- thumbnail.width.should == 200
311
- thumbnail.height.should == 100
312
- #show_blob thumbnail.data
313
- end
314
- end
315
- end
316
- end
317
- end
318
- end