durran-carrierwave 0.3.2.3

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 (91) hide show
  1. data/Generators +4 -0
  2. data/History.txt +66 -0
  3. data/LICENSE +8 -0
  4. data/Manifest.txt +89 -0
  5. data/README.rdoc +342 -0
  6. data/Rakefile +30 -0
  7. data/carrierwave.gemspec +57 -0
  8. data/cucumber.yml +2 -0
  9. data/features/caching.feature +28 -0
  10. data/features/file_storage.feature +37 -0
  11. data/features/file_storage_overridden_filename.feature +38 -0
  12. data/features/file_storage_overridden_store_dir.feature +38 -0
  13. data/features/file_storage_reversing_processor.feature +43 -0
  14. data/features/fixtures/bork.txt +1 -0
  15. data/features/fixtures/monkey.txt +1 -0
  16. data/features/mount_activerecord.feature +46 -0
  17. data/features/mount_datamapper.feature +46 -0
  18. data/features/step_definitions/activerecord_steps.rb +22 -0
  19. data/features/step_definitions/caching_steps.rb +14 -0
  20. data/features/step_definitions/datamapper_steps.rb +29 -0
  21. data/features/step_definitions/file_steps.rb +42 -0
  22. data/features/step_definitions/general_steps.rb +80 -0
  23. data/features/step_definitions/mount_steps.rb +19 -0
  24. data/features/step_definitions/store_steps.rb +18 -0
  25. data/features/support/activerecord.rb +30 -0
  26. data/features/support/datamapper.rb +7 -0
  27. data/features/support/env.rb +35 -0
  28. data/features/versions_basics.feature +50 -0
  29. data/features/versions_nested_versions.feature +70 -0
  30. data/features/versions_overridden_filename.feature +51 -0
  31. data/features/versions_overriden_store_dir.feature +41 -0
  32. data/lib/carrierwave.rb +145 -0
  33. data/lib/carrierwave/compatibility/paperclip.rb +95 -0
  34. data/lib/carrierwave/core_ext/blank.rb +46 -0
  35. data/lib/carrierwave/core_ext/inheritable_attributes.rb +104 -0
  36. data/lib/carrierwave/core_ext/module_setup.rb +51 -0
  37. data/lib/carrierwave/mount.rb +332 -0
  38. data/lib/carrierwave/orm/activerecord.rb +73 -0
  39. data/lib/carrierwave/orm/datamapper.rb +27 -0
  40. data/lib/carrierwave/orm/mongomapper.rb +27 -0
  41. data/lib/carrierwave/orm/sequel.rb +57 -0
  42. data/lib/carrierwave/processing/image_science.rb +72 -0
  43. data/lib/carrierwave/processing/rmagick.rb +286 -0
  44. data/lib/carrierwave/sanitized_file.rb +272 -0
  45. data/lib/carrierwave/storage/abstract.rb +32 -0
  46. data/lib/carrierwave/storage/file.rb +50 -0
  47. data/lib/carrierwave/storage/s3.rb +215 -0
  48. data/lib/carrierwave/test/matchers.rb +114 -0
  49. data/lib/carrierwave/uploader.rb +43 -0
  50. data/lib/carrierwave/uploader/cache.rb +116 -0
  51. data/lib/carrierwave/uploader/callbacks.rb +42 -0
  52. data/lib/carrierwave/uploader/default_path.rb +23 -0
  53. data/lib/carrierwave/uploader/extension_whitelist.rb +37 -0
  54. data/lib/carrierwave/uploader/mountable.rb +39 -0
  55. data/lib/carrierwave/uploader/paths.rb +27 -0
  56. data/lib/carrierwave/uploader/processing.rb +81 -0
  57. data/lib/carrierwave/uploader/proxy.rb +62 -0
  58. data/lib/carrierwave/uploader/remove.rb +23 -0
  59. data/lib/carrierwave/uploader/store.rb +156 -0
  60. data/lib/carrierwave/uploader/url.rb +24 -0
  61. data/lib/carrierwave/uploader/versions.rb +147 -0
  62. data/lib/generators/uploader_generator.rb +22 -0
  63. data/rails_generators/uploader/USAGE +2 -0
  64. data/rails_generators/uploader/templates/uploader.rb +47 -0
  65. data/rails_generators/uploader/uploader_generator.rb +21 -0
  66. data/script/console +10 -0
  67. data/script/destroy +14 -0
  68. data/script/generate +14 -0
  69. data/spec/compatibility/paperclip_spec.rb +43 -0
  70. data/spec/fixtures/bork.txt +1 -0
  71. data/spec/fixtures/test.jpeg +1 -0
  72. data/spec/fixtures/test.jpg +1 -0
  73. data/spec/mount_spec.rb +517 -0
  74. data/spec/orm/activerecord_spec.rb +271 -0
  75. data/spec/orm/datamapper_spec.rb +161 -0
  76. data/spec/orm/mongomapper_spec.rb +184 -0
  77. data/spec/orm/sequel_spec.rb +192 -0
  78. data/spec/sanitized_file_spec.rb +612 -0
  79. data/spec/spec_helper.rb +99 -0
  80. data/spec/uploader/cache_spec.rb +196 -0
  81. data/spec/uploader/default_path_spec.rb +68 -0
  82. data/spec/uploader/extension_whitelist_spec.rb +44 -0
  83. data/spec/uploader/mountable_spec.rb +33 -0
  84. data/spec/uploader/paths_spec.rb +22 -0
  85. data/spec/uploader/processing_spec.rb +62 -0
  86. data/spec/uploader/proxy_spec.rb +54 -0
  87. data/spec/uploader/remove_spec.rb +70 -0
  88. data/spec/uploader/store_spec.rb +274 -0
  89. data/spec/uploader/url_spec.rb +87 -0
  90. data/spec/uploader/versions_spec.rb +306 -0
  91. metadata +228 -0
@@ -0,0 +1,271 @@
1
+ # encoding: utf-8
2
+
3
+ require File.dirname(__FILE__) + '/../spec_helper'
4
+
5
+ require 'carrierwave/orm/activerecord'
6
+
7
+ # change this if sqlite is unavailable
8
+ dbconfig = {
9
+ :adapter => 'sqlite3',
10
+ :database => ':memory:'
11
+ }
12
+
13
+ ActiveRecord::Base.establish_connection(dbconfig)
14
+ ActiveRecord::Migration.verbose = false
15
+
16
+ class TestMigration < ActiveRecord::Migration
17
+ def self.up
18
+ create_table :events, :force => true do |t|
19
+ t.column :image, :string
20
+ t.column :textfile, :string
21
+ t.column :foo, :string
22
+ end
23
+ end
24
+
25
+ def self.down
26
+ drop_table :events
27
+ end
28
+ end
29
+
30
+ class Event < ActiveRecord::Base; end # setup a basic AR class for testing
31
+ $arclass = 0
32
+
33
+ describe CarrierWave::ActiveRecord do
34
+
35
+ describe '.mount_uploader' do
36
+
37
+ before(:all) { TestMigration.up }
38
+ after(:all) { TestMigration.down }
39
+ after { Event.delete_all }
40
+
41
+ before do
42
+ # My god, what a horrible, horrible solution, but AR validations don't work
43
+ # unless the class has a name. This is the best I could come up with :S
44
+ $arclass += 1
45
+ eval <<-RUBY
46
+ class Event#{$arclass} < Event; end
47
+ @class = Event#{$arclass}
48
+ RUBY
49
+ @class.table_name = "events"
50
+ @uploader = Class.new(CarrierWave::Uploader::Base)
51
+ @class.mount_uploader(:image, @uploader)
52
+ @event = @class.new
53
+ end
54
+
55
+ describe '#image' do
56
+
57
+ it "should return blank uploader when nothing has been assigned" do
58
+ @event.image.should be_blank
59
+ end
60
+
61
+ it "should return blank uploader when an empty string has been assigned" do
62
+ @event[:image] = ''
63
+ @event.save
64
+ @event.reload
65
+ @event.image.should be_blank
66
+ end
67
+
68
+ it "should retrieve a file from the storage if a value is stored in the database" do
69
+ @event[:image] = 'test.jpeg'
70
+ @event.save
71
+ @event.reload
72
+ @event.image.should be_an_instance_of(@uploader)
73
+ end
74
+
75
+ it "should set the path to the store dir" do
76
+ @event[:image] = 'test.jpeg'
77
+ @event.save
78
+ @event.reload
79
+ @event.image.current_path.should == public_path('uploads/test.jpeg')
80
+ end
81
+
82
+ end
83
+
84
+ describe '#image=' do
85
+
86
+ it "should cache a file" do
87
+ @event.image = stub_file('test.jpeg')
88
+ @event.image.should be_an_instance_of(@uploader)
89
+ end
90
+
91
+ it "should write nothing to the database, to prevent overriden filenames to fail because of unassigned attributes" do
92
+ @event[:image].should be_nil
93
+ end
94
+
95
+ it "should copy a file into into the cache directory" do
96
+ @event.image = stub_file('test.jpeg')
97
+ @event.image.current_path.should =~ /^#{public_path('uploads/tmp')}/
98
+ end
99
+
100
+ it "should do nothing when nil is assigned" do
101
+ @event.image = nil
102
+ @event.image.should be_blank
103
+ end
104
+
105
+ it "should do nothing when an empty string is assigned" do
106
+ @event.image = ''
107
+ @event.image.should be_blank
108
+ end
109
+
110
+ it "should make the record invalid when an integrity error occurs" do
111
+ @uploader.class_eval do
112
+ def extension_white_list
113
+ %(txt)
114
+ end
115
+ end
116
+ @event.image = stub_file('test.jpg')
117
+ @event.should_not be_valid
118
+ end
119
+
120
+ it "should make the record invalid when a processing error occurs" do
121
+ @uploader.class_eval do
122
+ process :monkey
123
+ def monkey
124
+ raise CarrierWave::ProcessingError, "Ohh noez!"
125
+ end
126
+ end
127
+ @event.image = stub_file('test.jpg')
128
+ @event.should_not be_valid
129
+ end
130
+
131
+ end
132
+
133
+ describe '#save' do
134
+
135
+ it "should do nothing when no file has been assigned" do
136
+ @event.save.should be_true
137
+ @event.image.should be_blank
138
+ end
139
+
140
+ it "should copy the file to the upload directory when a file has been assigned" do
141
+ @event.image = stub_file('test.jpeg')
142
+ @event.save.should be_true
143
+ @event.image.should be_an_instance_of(@uploader)
144
+ @event.image.current_path.should == public_path('uploads/test.jpeg')
145
+ end
146
+
147
+ it "should do nothing when a validation fails" do
148
+ @class.validate { |r| r.errors.add :textfile, "FAIL!" }
149
+ @event.image = stub_file('test.jpeg')
150
+ @event.save.should be_false
151
+ @event.image.should be_an_instance_of(@uploader)
152
+ @event.image.current_path.should =~ /^#{public_path('uploads/tmp')}/
153
+ end
154
+
155
+ it "should assign the filename to the database" do
156
+ @event.image = stub_file('test.jpeg')
157
+ @event.save.should be_true
158
+ @event.reload
159
+ @event[:image].should == 'test.jpeg'
160
+ end
161
+
162
+ it "should preserve the image when nothing is assigned" do
163
+ @event.image = stub_file('test.jpeg')
164
+ @event.save.should be_true
165
+ @event = @class.find(@event.id)
166
+ @event.foo = "bar"
167
+ @event.save.should be_true
168
+ @event[:image].should == 'test.jpeg'
169
+ end
170
+
171
+ it "should remove the image if remove_image? returns true" do
172
+ @event.image = stub_file('test.jpeg')
173
+ @event.save!
174
+ @event.remove_image = true
175
+ @event.save!
176
+ @event.reload
177
+ @event.image.should be_blank
178
+ @event[:image].should == ''
179
+ end
180
+
181
+ end
182
+
183
+ describe '#destroy' do
184
+
185
+ it "should do nothing when no file has been assigned" do
186
+ @event.save.should be_true
187
+ @event.destroy
188
+ end
189
+
190
+ it "should remove the file from the filesystem" do
191
+ @event.image = stub_file('test.jpeg')
192
+ @event.save.should be_true
193
+ @event.image.should be_an_instance_of(@uploader)
194
+ @event.image.current_path.should == public_path('uploads/test.jpeg')
195
+ @event.destroy
196
+ File.exist?(public_path('uploads/test.jpeg')).should be_false
197
+ end
198
+
199
+ end
200
+
201
+ describe 'with overriddent filename' do
202
+
203
+ describe '#save' do
204
+
205
+ before do
206
+ @uploader.class_eval do
207
+ def filename
208
+ model.name + File.extname(super)
209
+ end
210
+ end
211
+ @event.stub!(:name).and_return('jonas')
212
+ end
213
+
214
+ it "should copy the file to the upload directory when a file has been assigned" do
215
+ @event.image = stub_file('test.jpeg')
216
+ @event.save.should be_true
217
+ @event.image.should be_an_instance_of(@uploader)
218
+ @event.image.current_path.should == public_path('uploads/jonas.jpeg')
219
+ end
220
+
221
+ it "should assign an overridden filename to the database" do
222
+ @event.image = stub_file('test.jpeg')
223
+ @event.save.should be_true
224
+ @event.reload
225
+ @event[:image].should == 'jonas.jpeg'
226
+ end
227
+
228
+ end
229
+
230
+ end
231
+
232
+ describe 'with validates_presence_of' do
233
+
234
+ before do
235
+ @class.validates_presence_of :image
236
+ @event.stub!(:name).and_return('jonas')
237
+ end
238
+
239
+ it "should be valid if a file has been cached" do
240
+ @event.image = stub_file('test.jpeg')
241
+ @event.should be_valid
242
+ end
243
+
244
+ it "should not be valid if a file has not been cached" do
245
+ @event.should_not be_valid
246
+ end
247
+
248
+ end
249
+
250
+ describe 'with validates_size_of' do
251
+
252
+ before do
253
+ @class.validates_size_of :image, :maximum => 40
254
+ @event.stub!(:name).and_return('jonas')
255
+ end
256
+
257
+ it "should be valid if a file has been cached that matches the size criteria" do
258
+ @event.image = stub_file('test.jpeg')
259
+ @event.should be_valid
260
+ end
261
+
262
+ it "should not be valid if a file has been cached that does not match the size criteria" do
263
+ @event.image = stub_file('bork.txt')
264
+ @event.should_not be_valid
265
+ end
266
+
267
+ end
268
+
269
+ end
270
+
271
+ end
@@ -0,0 +1,161 @@
1
+ # encoding: utf-8
2
+
3
+ require File.dirname(__FILE__) + '/../spec_helper'
4
+
5
+ require 'carrierwave/orm/datamapper'
6
+
7
+ DataMapper.setup(:default, 'sqlite3::memory:')
8
+
9
+ describe CarrierWave::DataMapper do
10
+
11
+ before do
12
+ uploader = Class.new(CarrierWave::Uploader::Base)
13
+
14
+ @class = Class.new
15
+ @class.class_eval do
16
+ include DataMapper::Resource
17
+
18
+ storage_names[:default] = 'events'
19
+
20
+ property :id, Integer, :key => true
21
+ property :image, String
22
+
23
+ mount_uploader :image, uploader
24
+ end
25
+
26
+ @class.auto_migrate!
27
+
28
+ @uploader = uploader
29
+
30
+ @event = @class.new
31
+ end
32
+
33
+ describe '#image' do
34
+
35
+ it "should return blank uploader when nothing has been assigned" do
36
+ @event.image.should be_blank
37
+ end
38
+
39
+ it "should return blank uploader when an empty string has been assigned" do
40
+ repository(:default).adapter.query("INSERT INTO events (image) VALUES ('')")
41
+ @event = @class.first
42
+
43
+ @event.image.should be_blank
44
+ end
45
+
46
+ it "should retrieve a file from the storage if a value is stored in the database" do
47
+ repository(:default).adapter.query("INSERT INTO events (image) VALUES ('test.jpg')")
48
+ @event = @class.first
49
+
50
+ @event.save
51
+ @event.reload
52
+ @event.image.should be_an_instance_of(@uploader)
53
+ end
54
+
55
+ it "should set the path to the store dir" do
56
+ @event.attribute_set(:image, 'test.jpeg')
57
+ @event.save
58
+ @event.reload
59
+ @event.image.current_path.should == public_path('uploads/test.jpeg')
60
+ end
61
+
62
+ end
63
+
64
+ describe '#image=' do
65
+
66
+ it "should cache a file" do
67
+ @event.image = stub_file('test.jpeg')
68
+ @event.image.should be_an_instance_of(@uploader)
69
+ end
70
+
71
+ it "should write nothing to the database, to prevent overriden filenames to fail because of unassigned attributes" do
72
+ @event.attribute_get(:image).should be_nil
73
+ end
74
+
75
+ it "should copy a file into into the cache directory" do
76
+ @event.image = stub_file('test.jpeg')
77
+ @event.image.current_path.should =~ /^#{public_path('uploads/tmp')}/
78
+ end
79
+
80
+ it "should do nothing when nil is assigned" do
81
+ @event.image = nil
82
+ @event.image.should be_blank
83
+ end
84
+
85
+ it "should do nothing when an empty string is assigned" do
86
+ @event.image = ''
87
+ @event.image.should be_blank
88
+ end
89
+
90
+ end
91
+
92
+ describe '#save' do
93
+
94
+ it "should do nothing when no file has been assigned" do
95
+ @event.save
96
+ @event.image.should be_blank
97
+ end
98
+
99
+ it "should copy the file to the upload directory when a file has been assigned" do
100
+ @event.image = stub_file('test.jpeg')
101
+ @event.save
102
+ @event.image.should be_an_instance_of(@uploader)
103
+ @event.image.current_path.should == public_path('uploads/test.jpeg')
104
+ end
105
+
106
+ # it "should do nothing when a validation fails" do
107
+ # pending "how do we test with and without dm-validations?"
108
+ # @class.validate { |r| r.errors.add :textfile, "FAIL!" }
109
+ # @event.image = stub_file('test.jpeg')
110
+ # @event.save
111
+ # @event.image.should be_an_instance_of(@uploader)
112
+ # @event.image.current_path.should =~ /^#{public_path('uploads/tmp')}/
113
+ # end
114
+
115
+ it "should assign the filename to the database" do
116
+ @event.image = stub_file('test.jpeg')
117
+ @event.save
118
+ @event.reload
119
+ @event.attribute_get(:image).should == 'test.jpeg'
120
+ end
121
+
122
+ # it "should assign the filename before validation" do
123
+ # pending "how do we test with and without dm-validations?"
124
+ # @class.validate { |r| r.errors.add_to_base "FAIL!" if r[:image].nil? }
125
+ # @event.image = stub_file('test.jpeg')
126
+ # @event.save
127
+ # @event.reload
128
+ # @event.attribute_get(:image).should == 'test.jpeg'
129
+ # end
130
+
131
+ it "should remove the image if remove_image? returns true" do
132
+ @event.image = stub_file('test.jpeg')
133
+ @event.save
134
+ @event.remove_image = true
135
+ @event.save
136
+ @event.reload
137
+ @event.image.should be_blank
138
+ @event.attribute_get(:image).should == ''
139
+ end
140
+
141
+ end
142
+
143
+ describe '#destroy' do
144
+
145
+ it "should do nothing when no file has been assigned" do
146
+ @event.destroy
147
+ end
148
+
149
+ it "should remove the file from the filesystem" do
150
+ @event.image = stub_file('test.jpeg')
151
+ @event.save.should be_true
152
+ File.exist?(public_path('uploads/test.jpeg')).should be_true
153
+ @event.image.should be_an_instance_of(@uploader)
154
+ @event.image.current_path.should == public_path('uploads/test.jpeg')
155
+ @event.destroy
156
+ File.exist?(public_path('uploads/test.jpeg')).should be_false
157
+ end
158
+
159
+ end
160
+
161
+ end
@@ -0,0 +1,184 @@
1
+ # encoding: utf-8
2
+ require File.dirname(__FILE__) + '/../spec_helper'
3
+
4
+ require 'carrierwave/orm/mongomapper'
5
+
6
+ MongoMapper.database = "carrierwave_test"
7
+
8
+ describe CarrierWave::MongoMapper do
9
+
10
+ before do
11
+ uploader = Class.new(CarrierWave::Uploader::Base)
12
+
13
+ @class = Class.new
14
+ @class.class_eval do
15
+ include MongoMapper::Document
16
+ mount_uploader :image, uploader
17
+ end
18
+
19
+ @uploader = uploader
20
+ end
21
+
22
+ describe '#image' do
23
+
24
+ context "when nothing is assigned" do
25
+
26
+ before do
27
+ @document = @class.new
28
+ end
29
+
30
+ it "returns a blank uploader" do
31
+ @document.image.should be_blank
32
+ end
33
+
34
+ end
35
+
36
+ context "when an empty string is assigned" do
37
+
38
+ before do
39
+ @document = @class.new(:image_filename => "")
40
+ @document.save
41
+ end
42
+
43
+ it "returns a blank uploader" do
44
+ @saved_doc = @class.find(:first)
45
+ @saved_doc.image.should be_blank
46
+ end
47
+
48
+ end
49
+
50
+ context "when a filename is saved in the database" do
51
+
52
+ before do
53
+ @document = @class.new(:image_filename => "test.jpg")
54
+ @document.save
55
+ @doc = @class.find(:first)
56
+ end
57
+
58
+ it "returns an uploader" do
59
+ @doc.image.should be_an_instance_of(@uploader)
60
+ end
61
+
62
+ it "sets the path to the store directory" do
63
+ @doc.image.current_path.should == public_path('uploads/test.jpg')
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ describe '#image=' do
71
+
72
+ before do
73
+ @doc = @class.new
74
+ end
75
+
76
+ context "when nil is assigned" do
77
+
78
+ it "does not set the value" do
79
+ @doc.image = nil
80
+ @doc.image.should be_blank
81
+ end
82
+
83
+ end
84
+
85
+ context "when an empty string is assigned" do
86
+
87
+ it "does not set the value" do
88
+ @doc.image = ''
89
+ @doc.image.should be_blank
90
+ end
91
+
92
+ end
93
+
94
+ context "when a file is assigned" do
95
+
96
+ it "should cache a file" do
97
+ @doc.image = stub_file('test.jpeg')
98
+ @doc.image.should be_an_instance_of(@uploader)
99
+ end
100
+
101
+ it "should write nothing to the database, to prevent overriden filenames to fail because of unassigned attributes" do
102
+ @doc[:image_filename].should be_nil
103
+ end
104
+
105
+ it "should copy a file into into the cache directory" do
106
+ @doc.image = stub_file('test.jpeg')
107
+ @doc.image.current_path.should =~ /^#{public_path('uploads/tmp')}/
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+
114
+ describe "#save" do
115
+
116
+ before do
117
+ @doc = @class.new
118
+ end
119
+
120
+ context "when no file is assigned" do
121
+
122
+ it "image is blank" do
123
+ @doc.save
124
+ @doc.image.should be_blank
125
+ end
126
+
127
+ end
128
+
129
+ context "when a file is assigned" do
130
+
131
+ it "copies the file to the upload directory" do
132
+ @doc.image = stub_file('test.jpg')
133
+ @doc.save
134
+ @doc.image.should be_an_instance_of(@uploader)
135
+ @doc.image.current_path.should == public_path('uploads/test.jpg')
136
+ end
137
+
138
+ it "saves the filename in the database" do
139
+ @doc.image = stub_file('test.jpg')
140
+ @doc.save
141
+ @doc[:image_filename].should == 'test.jpg'
142
+ end
143
+
144
+ context "when remove_image? is true" do
145
+
146
+ it "removes the image" do
147
+ @doc.image = stub_file('test.jpeg')
148
+ @doc.save
149
+ @doc.remove_image = true
150
+ @doc.save
151
+ @doc.image.should be_blank
152
+ @doc[:image_filename].should == ''
153
+ end
154
+
155
+ end
156
+
157
+ end
158
+
159
+ end
160
+
161
+ describe '#destroy' do
162
+
163
+ before do
164
+ @doc = @class.new
165
+ end
166
+
167
+ context "when file assigned" do
168
+
169
+ it "removes the file from the filesystem" do
170
+ @doc.image = stub_file('test.jpeg')
171
+ @doc.save.should be_true
172
+ File.exist?(public_path('uploads/test.jpeg')).should be_true
173
+ @doc.image.should be_an_instance_of(@uploader)
174
+ @doc.image.current_path.should == public_path('uploads/test.jpeg')
175
+ @doc.destroy
176
+ File.exist?(public_path('uploads/test.jpeg')).should be_false
177
+ end
178
+
179
+ end
180
+
181
+ end
182
+
183
+
184
+ end