dragonfly 0.8.6 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of dragonfly might be problematic. Click here for more details.
- data/{.specopts → .rspec} +0 -1
- data/.yardopts +6 -2
- data/Gemfile +14 -13
- data/History.md +47 -9
- data/README.md +25 -5
- data/Rakefile +37 -79
- data/VERSION +1 -1
- data/dragonfly.gemspec +140 -89
- data/extra_docs/Analysers.md +8 -48
- data/extra_docs/Configuration.md +40 -25
- data/extra_docs/Couch.md +49 -0
- data/extra_docs/DataStorage.md +94 -24
- data/extra_docs/Encoding.md +6 -35
- data/extra_docs/ExampleUseCases.md +113 -0
- data/extra_docs/GeneralUsage.md +7 -23
- data/extra_docs/Generators.md +15 -49
- data/extra_docs/Heroku.md +7 -8
- data/extra_docs/ImageMagick.md +126 -0
- data/extra_docs/MimeTypes.md +3 -3
- data/extra_docs/Models.md +163 -0
- data/extra_docs/Mongo.md +1 -4
- data/extra_docs/Processing.md +7 -60
- data/extra_docs/Rails2.md +3 -1
- data/extra_docs/Rails3.md +2 -10
- data/extra_docs/ServingRemotely.md +83 -0
- data/extra_docs/Sinatra.md +3 -3
- data/extra_docs/URLs.md +60 -33
- data/features/rails_3.0.5.feature +8 -0
- data/features/steps/rails_steps.rb +7 -18
- data/features/support/env.rb +10 -37
- data/features/support/setup.rb +32 -0
- data/fixtures/rails_3.0.5/files/app/models/album.rb +5 -0
- data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
- data/fixtures/{files → rails_3.0.5/files}/app/views/albums/show.html.erb +2 -0
- data/fixtures/{files → rails_3.0.5/files}/config/initializers/dragonfly.rb +0 -0
- data/fixtures/rails_3.0.5/files/features/manage_album_images.feature +38 -0
- data/fixtures/rails_3.0.5/files/features/step_definitions/helper_steps.rb +7 -0
- data/fixtures/{files → rails_3.0.5/files}/features/step_definitions/image_steps.rb +11 -1
- data/fixtures/{files → rails_3.0.5/files}/features/support/paths.rb +2 -0
- data/fixtures/{files → rails_3.0.5/files}/features/text_images.feature +0 -0
- data/fixtures/{rails_3.0.3 → rails_3.0.5}/template.rb +2 -2
- data/irbrc.rb +2 -1
- data/lib/dragonfly.rb +7 -0
- data/lib/dragonfly/active_model_extensions/attachment.rb +134 -46
- data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +144 -0
- data/lib/dragonfly/active_model_extensions/class_methods.rb +62 -9
- data/lib/dragonfly/active_model_extensions/instance_methods.rb +2 -2
- data/lib/dragonfly/active_model_extensions/validations.rb +10 -6
- data/lib/dragonfly/analyser.rb +0 -1
- data/lib/dragonfly/analysis/file_command_analyser.rb +1 -1
- data/lib/dragonfly/analysis/image_magick_analyser.rb +2 -43
- data/lib/dragonfly/app.rb +64 -55
- data/lib/dragonfly/config/heroku.rb +1 -1
- data/lib/dragonfly/config/image_magick.rb +2 -37
- data/lib/dragonfly/config/rails.rb +5 -2
- data/lib/dragonfly/configurable.rb +115 -35
- data/lib/dragonfly/core_ext/object.rb +1 -1
- data/lib/dragonfly/core_ext/string.rb +1 -1
- data/lib/dragonfly/data_storage/couch_data_store.rb +84 -0
- data/lib/dragonfly/data_storage/file_data_store.rb +43 -18
- data/lib/dragonfly/data_storage/mongo_data_store.rb +8 -4
- data/lib/dragonfly/data_storage/s3data_store.rb +82 -38
- data/lib/dragonfly/encoding/image_magick_encoder.rb +2 -53
- data/lib/dragonfly/function_manager.rb +4 -2
- data/lib/dragonfly/generation/image_magick_generator.rb +2 -136
- data/lib/dragonfly/hash_with_css_style_keys.rb +21 -0
- data/lib/dragonfly/image_magick/analyser.rb +51 -0
- data/lib/dragonfly/image_magick/config.rb +44 -0
- data/lib/dragonfly/{encoding/r_magick_encoder.rb → image_magick/encoder.rb} +10 -14
- data/lib/dragonfly/image_magick/generator.rb +145 -0
- data/lib/dragonfly/image_magick/processor.rb +104 -0
- data/lib/dragonfly/image_magick/utils.rb +72 -0
- data/lib/dragonfly/image_magick_utils.rb +2 -79
- data/lib/dragonfly/job.rb +152 -90
- data/lib/dragonfly/middleware.rb +5 -19
- data/lib/dragonfly/processing/image_magick_processor.rb +2 -95
- data/lib/dragonfly/rails/images.rb +15 -10
- data/lib/dragonfly/response.rb +26 -12
- data/lib/dragonfly/serializer.rb +1 -4
- data/lib/dragonfly/server.rb +103 -0
- data/lib/dragonfly/temp_object.rb +56 -101
- data/lib/dragonfly/url_mapper.rb +78 -0
- data/spec/dragonfly/active_model_extensions/model_spec.rb +772 -65
- data/spec/dragonfly/active_model_extensions/spec_helper.rb +90 -10
- data/spec/dragonfly/analyser_spec.rb +1 -1
- data/spec/dragonfly/analysis/file_command_analyser_spec.rb +5 -14
- data/spec/dragonfly/app_spec.rb +35 -180
- data/spec/dragonfly/configurable_spec.rb +259 -18
- data/spec/dragonfly/core_ext/string_spec.rb +2 -2
- data/spec/dragonfly/core_ext/symbol_spec.rb +1 -1
- data/spec/dragonfly/data_storage/couch_data_store_spec.rb +84 -0
- data/spec/dragonfly/data_storage/file_data_store_spec.rb +149 -22
- data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +21 -2
- data/spec/dragonfly/data_storage/s3_data_store_spec.rb +207 -43
- data/spec/dragonfly/data_storage/{data_store_spec.rb → shared_data_store_examples.rb} +16 -15
- data/spec/dragonfly/function_manager_spec.rb +2 -2
- data/spec/dragonfly/{generation/hash_with_css_style_keys_spec.rb → hash_with_css_style_keys_spec.rb} +2 -2
- data/spec/dragonfly/{analysis/shared_analyser_spec.rb → image_magick/analyser_spec.rb} +19 -6
- data/spec/dragonfly/{encoding/image_magick_encoder_spec.rb → image_magick/encoder_spec.rb} +2 -2
- data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
- data/spec/dragonfly/{processing/shared_processing_spec.rb → image_magick/processor_spec.rb} +55 -6
- data/spec/dragonfly/image_magick/utils_spec.rb +18 -0
- data/spec/dragonfly/job_builder_spec.rb +1 -1
- data/spec/dragonfly/job_definitions_spec.rb +1 -1
- data/spec/dragonfly/job_endpoint_spec.rb +26 -3
- data/spec/dragonfly/job_spec.rb +426 -208
- data/spec/dragonfly/loggable_spec.rb +2 -2
- data/spec/dragonfly/middleware_spec.rb +5 -26
- data/spec/dragonfly/routed_endpoint_spec.rb +1 -1
- data/spec/dragonfly/serializer_spec.rb +1 -14
- data/spec/dragonfly/server_spec.rb +261 -0
- data/spec/dragonfly/simple_cache_spec.rb +1 -1
- data/spec/dragonfly/temp_object_spec.rb +84 -130
- data/spec/dragonfly/url_mapper_spec.rb +130 -0
- data/spec/functional/deprecations_spec.rb +51 -0
- data/spec/functional/image_magick_app_spec.rb +27 -0
- data/spec/functional/model_urls_spec.rb +85 -0
- data/spec/functional/remote_on_the_fly_spec.rb +51 -0
- data/spec/functional/to_response_spec.rb +31 -0
- data/spec/spec_helper.rb +12 -22
- data/spec/{argument_matchers.rb → support/argument_matchers.rb} +0 -0
- data/spec/{image_matchers.rb → support/image_matchers.rb} +4 -4
- data/spec/support/simple_matchers.rb +53 -0
- data/yard/handlers/configurable_attr_handler.rb +2 -2
- data/yard/templates/default/fulldoc/html/css/common.css +12 -10
- data/yard/templates/default/layout/html/layout.erb +6 -0
- metadata +267 -308
- data/Gemfile.rails.2.3.5 +0 -20
- data/features/3.0.3.feature +0 -8
- data/features/rails_2.3.5.feature +0 -7
- data/fixtures/files/app/models/album.rb +0 -3
- data/fixtures/files/app/views/albums/new.html.erb +0 -4
- data/fixtures/files/features/manage_album_images.feature +0 -12
- data/fixtures/rails_2.3.5/template.rb +0 -10
- data/lib/dragonfly/analysis/r_magick_analyser.rb +0 -63
- data/lib/dragonfly/config/r_magick.rb +0 -46
- data/lib/dragonfly/generation/hash_with_css_style_keys.rb +0 -23
- data/lib/dragonfly/generation/r_magick_generator.rb +0 -155
- data/lib/dragonfly/processing/r_magick_processor.rb +0 -126
- data/lib/dragonfly/r_magick_utils.rb +0 -48
- data/lib/dragonfly/simple_endpoint.rb +0 -76
- data/spec/dragonfly/active_model_extensions/active_model_setup.rb +0 -97
- data/spec/dragonfly/active_model_extensions/active_record_setup.rb +0 -85
- data/spec/dragonfly/analysis/image_magick_analyser_spec.rb +0 -15
- data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +0 -31
- data/spec/dragonfly/config/r_magick_spec.rb +0 -29
- data/spec/dragonfly/encoding/r_magick_encoder_spec.rb +0 -41
- data/spec/dragonfly/generation/image_magick_generator_spec.rb +0 -12
- data/spec/dragonfly/generation/r_magick_generator_spec.rb +0 -28
- data/spec/dragonfly/generation/shared_generator_spec.rb +0 -91
- data/spec/dragonfly/image_magick_utils_spec.rb +0 -16
- data/spec/dragonfly/processing/image_magick_processor_spec.rb +0 -29
- data/spec/dragonfly/processing/r_magick_processor_spec.rb +0 -30
- data/spec/dragonfly/simple_endpoint_spec.rb +0 -97
- data/spec/simple_matchers.rb +0 -44
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require File.dirname(__FILE__) + '/shared_data_store_examples'
|
4
|
+
require 'net/http'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
describe Dragonfly::DataStorage::CouchDataStore do
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
WebMock.allow_net_connect!
|
11
|
+
@data_store = Dragonfly::DataStorage::CouchDataStore.new(
|
12
|
+
:database => "dragonfly_test"
|
13
|
+
)
|
14
|
+
begin
|
15
|
+
@data_store.db.get('ping')
|
16
|
+
rescue Errno::ECONNREFUSED => e
|
17
|
+
pending "You need to start CouchDB on localhost:5984 to test the CouchDataStore"
|
18
|
+
rescue RestClient::ResourceNotFound
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
it_should_behave_like 'data_store'
|
24
|
+
|
25
|
+
describe "destroy" do
|
26
|
+
before(:each) do
|
27
|
+
@temp_object = Dragonfly::TempObject.new('gollum')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should raise an error if the data doesn't exist on destroy" do
|
31
|
+
uid = @data_store.store(@temp_object)
|
32
|
+
@data_store.destroy(uid)
|
33
|
+
lambda{
|
34
|
+
@data_store.destroy(uid)
|
35
|
+
}.should raise_error(Dragonfly::DataStorage::DataNotFound)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "url_for" do
|
40
|
+
it "should give the correct url" do
|
41
|
+
@data_store.url_for('asd7fas9df/thing.txt').should == 'http://localhost:5984/dragonfly_test/asd7fas9df/thing.txt'
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should assume the attachment is called 'file' if not given" do
|
45
|
+
@data_store.url_for('asd7fas9df').should == 'http://localhost:5984/dragonfly_test/asd7fas9df/file'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "serving from couchdb" do
|
50
|
+
|
51
|
+
def get_content(url)
|
52
|
+
uri = URI.parse(url)
|
53
|
+
Net::HTTP.start(uri.host, uri.port) {|http|
|
54
|
+
http.get(uri.path)
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
before(:each) do
|
59
|
+
@temp_object = Dragonfly::TempObject.new('testingyo')
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should use the fallback by default" do
|
63
|
+
uid = @data_store.store(@temp_object)
|
64
|
+
response = get_content(@data_store.url_for(uid))
|
65
|
+
response.body.should == 'testingyo'
|
66
|
+
response['Content-Type'].should == 'application/octet-stream'
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should allow setting on store with 'content_type'" do
|
70
|
+
uid = @data_store.store(@temp_object, :content_type => 'text/plain')
|
71
|
+
response = get_content(@data_store.url_for(uid))
|
72
|
+
response.body.should == 'testingyo'
|
73
|
+
response['Content-Type'].should == 'text/plain'
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should allow setting on store with 'mime_type'" do
|
77
|
+
uid = @data_store.store(@temp_object, :mime_type => 'text/plain-yo')
|
78
|
+
response = get_content(@data_store.url_for(uid))
|
79
|
+
response.body.should == 'testingyo'
|
80
|
+
response['Content-Type'].should == 'text/plain-yo'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require File.dirname(__FILE__) + '/
|
1
|
+
require 'spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/shared_data_store_examples'
|
3
3
|
|
4
4
|
describe Dragonfly::DataStorage::FileDataStore do
|
5
5
|
|
@@ -42,14 +42,25 @@ describe Dragonfly::DataStorage::FileDataStore do
|
|
42
42
|
@data_store.store(@temp_object)
|
43
43
|
end
|
44
44
|
|
45
|
-
it "should use the temp_object
|
46
|
-
@temp_object.should_receive(:
|
45
|
+
it "should use the temp_object original filename if it exists" do
|
46
|
+
@temp_object.should_receive(:original_filename).at_least(:once).and_return('hello.there')
|
47
47
|
it_should_write_to_file("#{@file_pattern_prefix}hello.there", @temp_object)
|
48
48
|
@data_store.store(@temp_object)
|
49
49
|
end
|
50
50
|
|
51
|
-
it "should
|
52
|
-
@
|
51
|
+
it "should use the meta name if it exists" do
|
52
|
+
it_should_write_to_file("#{@file_pattern_prefix}damp.squib", @temp_object)
|
53
|
+
@data_store.store(@temp_object, :meta => {:name => 'damp.squib'})
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should prefer the meta name to the original filename" do
|
57
|
+
@temp_object.stub!(:original_filename).and_return('hello.there')
|
58
|
+
it_should_write_to_file("#{@file_pattern_prefix}damp.squib", @temp_object)
|
59
|
+
@data_store.store(@temp_object, :meta => {:name => 'damp.squib'})
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should get rid of funny characters in the temp_object original filename" do
|
63
|
+
@temp_object.should_receive(:original_filename).at_least(:once).and_return('A Picture with many spaces in its name (at 20:00 pm).png')
|
53
64
|
it_should_write_to_file("#{@file_pattern_prefix}A_Picture_with_many_spaces_in_its_name_at_20_00_pm_.png", @temp_object)
|
54
65
|
@data_store.store(@temp_object)
|
55
66
|
end
|
@@ -64,7 +75,7 @@ describe Dragonfly::DataStorage::FileDataStore do
|
|
64
75
|
end
|
65
76
|
|
66
77
|
it "should use a different filename taking into account the name and ext" do
|
67
|
-
@temp_object.should_receive(:
|
78
|
+
@temp_object.should_receive(:original_filename).at_least(:once).and_return('hello.png')
|
68
79
|
touch_file("#{@file_pattern_prefix}hello.png")
|
69
80
|
@data_store.should_receive(:disambiguate).with("#{@file_pattern_prefix}hello.png").and_return("#{@file_pattern_prefix}blah.png")
|
70
81
|
@data_store.store(@temp_object)
|
@@ -101,7 +112,7 @@ describe Dragonfly::DataStorage::FileDataStore do
|
|
101
112
|
end
|
102
113
|
|
103
114
|
it "should return the filepath without the root of the stored file when a file name is provided" do
|
104
|
-
@temp_object.should_receive(:
|
115
|
+
@temp_object.should_receive(:original_filename).at_least(:once).and_return('hello.you.png')
|
105
116
|
@data_store.store(@temp_object).should == "#{@file_pattern_prefix_without_root}hello.you.png"
|
106
117
|
end
|
107
118
|
|
@@ -136,21 +147,27 @@ describe Dragonfly::DataStorage::FileDataStore do
|
|
136
147
|
end
|
137
148
|
|
138
149
|
describe "retrieve" do
|
139
|
-
|
140
|
-
it "should return a closed file" do
|
150
|
+
it "should return a pathname" do
|
141
151
|
uid = @data_store.store(@temp_object)
|
142
|
-
|
143
|
-
|
152
|
+
pathname, meta = @data_store.retrieve(uid)
|
153
|
+
pathname.should be_a(Pathname)
|
144
154
|
end
|
145
|
-
|
146
|
-
it "should be able to retrieve any file, stored or not (and without
|
155
|
+
|
156
|
+
it "should be able to retrieve any file, stored or not (and without meta data)" do
|
147
157
|
FileUtils.mkdir_p("#{@data_store.root_path}/jelly_beans/are")
|
148
158
|
File.open("#{@data_store.root_path}/jelly_beans/are/good", 'w'){|f| f.write('hey dog') }
|
149
|
-
|
150
|
-
|
159
|
+
pathname, meta = @data_store.retrieve("jelly_beans/are/good")
|
160
|
+
pathname.read.should == 'hey dog'
|
151
161
|
meta.should == {}
|
152
162
|
end
|
153
|
-
|
163
|
+
|
164
|
+
it "should work even if meta is stored in old .extra file" do
|
165
|
+
uid = @data_store.store(@temp_object, :meta => {:dog => 'food'})
|
166
|
+
FileUtils.mv("#{@data_store.root_path}/#{uid}.meta", "#{@data_store.root_path}/#{uid}.extra")
|
167
|
+
pathname, meta = @data_store.retrieve(uid)
|
168
|
+
meta.should == {:dog => 'food'}
|
169
|
+
end
|
170
|
+
|
154
171
|
it "should raise an error if the file path has .. in it" do
|
155
172
|
expect{
|
156
173
|
@data_store.retrieve('jelly_beans/../are/good')
|
@@ -159,16 +176,27 @@ describe Dragonfly::DataStorage::FileDataStore do
|
|
159
176
|
end
|
160
177
|
|
161
178
|
describe "destroying" do
|
162
|
-
it "should
|
179
|
+
it "should prune empty directories when destroying" do
|
180
|
+
uid = @data_store.store(@temp_object)
|
181
|
+
@data_store.destroy(uid)
|
182
|
+
@data_store.root_path.should be_an_empty_directory
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should raise an error if the data doesn't exist on destroy" do
|
186
|
+
uid = @data_store.store(@temp_object)
|
187
|
+
@data_store.destroy(uid)
|
163
188
|
lambda{
|
164
|
-
@data_store.destroy(
|
189
|
+
@data_store.destroy(uid)
|
165
190
|
}.should raise_error(Dragonfly::DataStorage::DataNotFound)
|
166
191
|
end
|
167
192
|
|
168
|
-
it "should
|
193
|
+
it "should also destroy old .extra files" do
|
169
194
|
uid = @data_store.store(@temp_object)
|
195
|
+
FileUtils.cp("#{@data_store.root_path}/#{uid}.meta", "#{@data_store.root_path}/#{uid}.extra")
|
170
196
|
@data_store.destroy(uid)
|
171
|
-
@data_store.root_path.should
|
197
|
+
File.exist?("#{@data_store.root_path}/#{uid}").should be_false
|
198
|
+
File.exist?("#{@data_store.root_path}/#{uid}.meta").should be_false
|
199
|
+
File.exist?("#{@data_store.root_path}/#{uid}.extra").should be_false
|
172
200
|
end
|
173
201
|
|
174
202
|
it "should raise an error if the file path has .. in it" do
|
@@ -178,4 +206,103 @@ describe Dragonfly::DataStorage::FileDataStore do
|
|
178
206
|
end
|
179
207
|
end
|
180
208
|
|
181
|
-
|
209
|
+
describe "relative paths" do
|
210
|
+
let(:store) { Dragonfly::DataStorage::FileDataStore.new }
|
211
|
+
let(:relative_path) { "2011/02/11/picture.jpg" }
|
212
|
+
let(:absolute_path) { "#{root_path}#{relative_path}" }
|
213
|
+
let(:root_path) { "/path/to/file/" }
|
214
|
+
|
215
|
+
before do
|
216
|
+
store.root_path = root_path
|
217
|
+
end
|
218
|
+
|
219
|
+
subject { store.send :relative, absolute_path }
|
220
|
+
|
221
|
+
it { should == relative_path }
|
222
|
+
|
223
|
+
context "where root path contains spaces" do
|
224
|
+
let(:root_path) { "/path/to/file name/" }
|
225
|
+
it { should == relative_path }
|
226
|
+
end
|
227
|
+
context "where root path contains special chars" do
|
228
|
+
let(:root_path) { "/path/to/file name (Special backup directory)/" }
|
229
|
+
it { should == relative_path }
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe "turning meta off" do
|
234
|
+
before(:each) do
|
235
|
+
@data_store.store_meta = false
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should not write a meta file" do
|
239
|
+
uid = @data_store.store(@temp_object, :meta => {:bitrate => '35', :name => 'danny.boy'})
|
240
|
+
path = File.join(@data_store.root_path, uid) + '.meta'
|
241
|
+
File.exist?(path).should be_false
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should return an empty hash on retrieve" do
|
245
|
+
uid = @data_store.store(@temp_object, :meta => {:bitrate => '35', :name => 'danny.boy'})
|
246
|
+
obj, meta = @data_store.retrieve(uid)
|
247
|
+
meta.should == {}
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should still destroy the meta file if it exists" do
|
251
|
+
@data_store.store_meta = true
|
252
|
+
uid = @data_store.store(@temp_object)
|
253
|
+
@data_store.store_meta = false
|
254
|
+
@data_store.destroy(uid)
|
255
|
+
@data_store.root_path.should be_an_empty_directory
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should still destroy properly if meta is on but the meta file doesn't exist" do
|
259
|
+
uid = @data_store.store(@temp_object)
|
260
|
+
@data_store.store_meta = true
|
261
|
+
@data_store.destroy(uid)
|
262
|
+
@data_store.root_path.should be_an_empty_directory
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe "urls for serving directly" do
|
267
|
+
before(:each) do
|
268
|
+
@uid = 'some/path/to/file.png'
|
269
|
+
@data_store.root_path = '/var/tmp/eggs'
|
270
|
+
end
|
271
|
+
|
272
|
+
it "should raise an error if called without configuring" do
|
273
|
+
expect{
|
274
|
+
@data_store.url_for(@uid)
|
275
|
+
}.to raise_error(Dragonfly::Configurable::NotConfigured)
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should work as expected when the the server root is above the root path" do
|
279
|
+
@data_store.server_root = '/var/tmp'
|
280
|
+
@data_store.url_for(@uid).should == '/eggs/some/path/to/file.png'
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should work as expected when the the server root is the root path" do
|
284
|
+
@data_store.server_root = '/var/tmp/eggs'
|
285
|
+
@data_store.url_for(@uid).should == '/some/path/to/file.png'
|
286
|
+
end
|
287
|
+
|
288
|
+
it "should work as expected when the the server root is below the root path" do
|
289
|
+
@data_store.server_root = '/var/tmp/eggs/some/path'
|
290
|
+
@data_store.url_for(@uid).should == '/to/file.png'
|
291
|
+
end
|
292
|
+
|
293
|
+
it "should raise an error when the server root doesn't coincide with the root path" do
|
294
|
+
@data_store.server_root = '/var/blimey/eggs'
|
295
|
+
expect{
|
296
|
+
@data_store.url_for(@uid)
|
297
|
+
}.to raise_error(Dragonfly::DataStorage::FileDataStore::UnableToFormUrl)
|
298
|
+
end
|
299
|
+
|
300
|
+
it "should raise an error when the server root doesn't coincide with the uid" do
|
301
|
+
@data_store.server_root = '/var/tmp/eggs/some/gooney'
|
302
|
+
expect{
|
303
|
+
@data_store.url_for(@uid)
|
304
|
+
}.to raise_error(Dragonfly::DataStorage::FileDataStore::UnableToFormUrl)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require
|
3
|
-
require File.dirname(__FILE__) + '/
|
2
|
+
require 'spec_helper'
|
3
|
+
require File.dirname(__FILE__) + '/shared_data_store_examples'
|
4
4
|
require 'mongo'
|
5
5
|
|
6
6
|
describe Dragonfly::DataStorage::MongoDataStore do
|
@@ -35,4 +35,23 @@ describe Dragonfly::DataStorage::MongoDataStore do
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
describe "sharing already configured stuff" do
|
39
|
+
before(:each) do
|
40
|
+
@connection = Mongo::Connection.new
|
41
|
+
@temp_object = Dragonfly::TempObject.new('asdf')
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should allow sharing the connection" do
|
45
|
+
@data_store.connection = @connection
|
46
|
+
@connection.should_receive(:db).with('dragonfly_test').and_return(db=mock)
|
47
|
+
@data_store.db.should == db
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should allow sharing the db" do
|
51
|
+
db = @connection.db('dragonfly_test_yo')
|
52
|
+
@data_store.db = db
|
53
|
+
@data_store.grid.instance_eval{@db}.should == db # so wrong
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
38
57
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require File.dirname(__FILE__) + '/
|
1
|
+
require 'spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/shared_data_store_examples'
|
3
3
|
require 'yaml'
|
4
4
|
|
5
5
|
describe Dragonfly::DataStorage::S3DataStore do
|
@@ -19,54 +19,218 @@ describe Dragonfly::DataStorage::S3DataStore do
|
|
19
19
|
|
20
20
|
if enabled
|
21
21
|
|
22
|
-
|
22
|
+
# Make sure it's a new bucket name
|
23
|
+
BUCKET_NAME = "dragonfly-test-#{Time.now.to_i.to_s(36)}"
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
before(:each) do
|
26
|
+
WebMock.allow_net_connect!
|
27
|
+
@data_store = Dragonfly::DataStorage::S3DataStore.new
|
28
|
+
@data_store.configure do |d|
|
29
|
+
d.bucket_name = BUCKET_NAME
|
30
|
+
d.access_key_id = KEY
|
31
|
+
d.secret_access_key = SECRET
|
32
|
+
d.region = 'eu-west-1'
|
31
33
|
end
|
34
|
+
end
|
35
|
+
|
36
|
+
else
|
37
|
+
|
38
|
+
BUCKET_NAME = 'test-bucket'
|
32
39
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
it "should work ok with files with funny names" do
|
43
|
-
temp_object = Dragonfly::TempObject.new('eggheads',
|
44
|
-
:name => 'A Picture with many spaces in its name (at 20:00 pm).png'
|
45
|
-
)
|
46
|
-
uid = @data_store.store(temp_object)
|
47
|
-
uid.should =~ /A_Picture_with_many_spaces_in_its_name_at_20_00_pm_\.png$/
|
48
|
-
data, extra = @data_store.retrieve(uid)
|
49
|
-
data.should == 'eggheads'
|
50
|
-
end
|
51
|
-
|
52
|
-
it "should allow for setting the path manually" do
|
53
|
-
temp_object = Dragonfly::TempObject.new('eggheads')
|
54
|
-
uid = @data_store.store(temp_object, :path => 'hello/there')
|
55
|
-
uid.should == 'hello/there'
|
56
|
-
data, extra = @data_store.retrieve(uid)
|
57
|
-
data.should == 'eggheads'
|
58
|
-
end
|
59
|
-
|
60
|
-
it "should work fine when not using the filesystem" do
|
61
|
-
@data_store.use_filesystem = false
|
62
|
-
temp_object = Dragonfly::TempObject.new('gollum')
|
63
|
-
uid = @data_store.store(temp_object)
|
64
|
-
@data_store.retrieve(uid).should == ["gollum", {:meta=>{}, :format=>nil, :name=>nil}]
|
65
|
-
end
|
40
|
+
before(:each) do
|
41
|
+
Fog.mock!
|
42
|
+
@data_store = Dragonfly::DataStorage::S3DataStore.new
|
43
|
+
@data_store.configure do |d|
|
44
|
+
d.bucket_name = BUCKET_NAME
|
45
|
+
d.access_key_id = 'XXXXXXXXX'
|
46
|
+
d.secret_access_key = 'XXXXXXXXX'
|
47
|
+
d.region = 'eu-west-1'
|
66
48
|
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
it_should_behave_like 'data_store'
|
54
|
+
|
55
|
+
describe "store" do
|
56
|
+
it "should return a unique identifier for each storage" do
|
57
|
+
temp_object = Dragonfly::TempObject.new('gollum')
|
58
|
+
temp_object2 = Dragonfly::TempObject.new('gollum')
|
59
|
+
@data_store.store(temp_object).should_not == @data_store.store(temp_object2)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should use the name in the meta if set" do
|
63
|
+
temp_object = Dragonfly::TempObject.new('eggheads')
|
64
|
+
uid = @data_store.store(temp_object, :meta => {:name => 'doobie'})
|
65
|
+
uid.should =~ /doobie$/
|
66
|
+
data, meta = @data_store.retrieve(uid)
|
67
|
+
data.should == 'eggheads'
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should work ok with files with funny names" do
|
71
|
+
temp_object = Dragonfly::TempObject.new('eggheads')
|
72
|
+
uid = @data_store.store(temp_object, :meta => {:name => 'A Picture with many spaces in its name (at 20:00 pm).png'})
|
73
|
+
uid.should =~ /A_Picture_with_many_spaces_in_its_name_at_20_00_pm_\.png$/
|
74
|
+
data, meta = @data_store.retrieve(uid)
|
75
|
+
data.should == 'eggheads'
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should allow for setting the path manually" do
|
79
|
+
temp_object = Dragonfly::TempObject.new('eggheads')
|
80
|
+
uid = @data_store.store(temp_object, :path => 'hello/there')
|
81
|
+
uid.should == 'hello/there'
|
82
|
+
data, meta = @data_store.retrieve(uid)
|
83
|
+
data.should == 'eggheads'
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should work fine when not using the filesystem" do
|
87
|
+
@data_store.use_filesystem = false
|
88
|
+
temp_object = Dragonfly::TempObject.new('gollum')
|
89
|
+
uid = @data_store.store(temp_object)
|
90
|
+
@data_store.retrieve(uid).first.should == "gollum"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
if enabled # Fog.mock! doesn't work consistently with real one here
|
95
|
+
|
96
|
+
describe "destroy" do
|
97
|
+
before(:each) do
|
98
|
+
@temp_object = Dragonfly::TempObject.new('gollum')
|
99
|
+
end
|
100
|
+
it "should raise an error if the data doesn't exist on destroy" do
|
101
|
+
uid = @data_store.store(@temp_object)
|
102
|
+
@data_store.destroy(uid)
|
103
|
+
lambda{
|
104
|
+
@data_store.destroy(uid)
|
105
|
+
}.should raise_error(Dragonfly::DataStorage::DataNotFound)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "domain" do
|
112
|
+
it "should default to the US" do
|
113
|
+
@data_store.region = nil
|
114
|
+
@data_store.domain.should == 's3.amazonaws.com'
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should return the correct domain" do
|
118
|
+
@data_store.region = 'eu-west-1'
|
119
|
+
@data_store.domain.should == 's3-eu-west-1.amazonaws.com'
|
120
|
+
end
|
121
|
+
|
122
|
+
it "does raise an error if an unknown region is given" do
|
123
|
+
@data_store.region = 'latvia-central'
|
124
|
+
lambda{
|
125
|
+
@data_store.domain
|
126
|
+
}.should raise_error
|
127
|
+
end
|
128
|
+
end
|
67
129
|
|
130
|
+
describe "not configuring stuff properly" do
|
131
|
+
before(:each) do
|
132
|
+
@temp_object = Dragonfly::TempObject.new("Hi guys")
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should require a bucket name on store" do
|
136
|
+
@data_store.bucket_name = nil
|
137
|
+
proc{ @data_store.store(@temp_object) }.should raise_error(Dragonfly::Configurable::NotConfigured)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should require an access_key_id on store" do
|
141
|
+
@data_store.access_key_id = nil
|
142
|
+
proc{ @data_store.store(@temp_object) }.should raise_error(Dragonfly::Configurable::NotConfigured)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should require a secret access key on store" do
|
146
|
+
@data_store.secret_access_key = nil
|
147
|
+
proc{ @data_store.store(@temp_object) }.should raise_error(Dragonfly::Configurable::NotConfigured)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should require a bucket name on retrieve" do
|
151
|
+
@data_store.bucket_name = nil
|
152
|
+
proc{ @data_store.retrieve('asdf') }.should raise_error(Dragonfly::Configurable::NotConfigured)
|
68
153
|
end
|
154
|
+
|
155
|
+
it "should require an access_key_id on retrieve" do
|
156
|
+
@data_store.access_key_id = nil
|
157
|
+
proc{ @data_store.retrieve('asdf') }.should raise_error(Dragonfly::Configurable::NotConfigured)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should require a secret access key on retrieve" do
|
161
|
+
@data_store.secret_access_key = nil
|
162
|
+
proc{ @data_store.retrieve('asdf') }.should raise_error(Dragonfly::Configurable::NotConfigured)
|
163
|
+
end
|
164
|
+
end
|
69
165
|
|
166
|
+
describe "autocreating the bucket" do
|
167
|
+
it "should create the bucket on store if it doesn't exist" do
|
168
|
+
@data_store.bucket_name = "dragonfly-test-blah-blah-#{rand(100000000)}"
|
169
|
+
@data_store.store(Dragonfly::TempObject.new("asdfj"))
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should not try to create the bucket on retrieve if it doesn't exist" do
|
173
|
+
@data_store.bucket_name = "dragonfly-test-blah-blah-#{rand(100000000)}"
|
174
|
+
@data_store.send(:storage).should_not_receive(:put_bucket)
|
175
|
+
proc{ @data_store.retrieve("gungle") }.should raise_error(Dragonfly::DataStorage::DataNotFound)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "headers" do
|
180
|
+
before(:each) do
|
181
|
+
@temp_object = Dragonfly::TempObject.new('fjkdlsa')
|
182
|
+
@data_store.storage_headers = {'x-amz-foo' => 'biscuithead'}
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should allow configuring globally" do
|
186
|
+
@data_store.storage.should_receive(:put_object).with('test-bucket', anything, anything,
|
187
|
+
hash_including('x-amz-foo' => 'biscuithead')
|
188
|
+
)
|
189
|
+
@data_store.store(@temp_object)
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should allow adding per-store" do
|
193
|
+
@data_store.storage.should_receive(:put_object).with('test-bucket', anything, anything,
|
194
|
+
hash_including('x-amz-foo' => 'biscuithead', 'hello' => 'there')
|
195
|
+
)
|
196
|
+
@data_store.store(@temp_object, :headers => {'hello' => 'there'})
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should let the per-store one take precedence" do
|
200
|
+
@data_store.storage.should_receive(:put_object).with('test-bucket', anything, anything,
|
201
|
+
hash_including('x-amz-foo' => 'override!')
|
202
|
+
)
|
203
|
+
@data_store.store(@temp_object, :headers => {'x-amz-foo' => 'override!'})
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should not mess with the meta" do
|
207
|
+
@data_store.storage.should_receive(:put_object) do |_, __, ___, headers|
|
208
|
+
headers['x-amz-meta-extra'].should =~ /^\w+$/
|
209
|
+
end
|
210
|
+
@data_store.store(@temp_object, :headers => {'hello' => 'there'})
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe "urls for serving directly" do
|
215
|
+
|
216
|
+
before(:each) do
|
217
|
+
@uid = 'some/path/on/s3'
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should use the bucket subdomain" do
|
221
|
+
@data_store.url_for(@uid).should == "http://#{BUCKET_NAME}.s3.amazonaws.com/some/path/on/s3"
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should use the bucket subdomain for other regions too" do
|
225
|
+
@data_store.region = 'eu-west-1'
|
226
|
+
@data_store.url_for(@uid).should == "http://#{BUCKET_NAME}.s3.amazonaws.com/some/path/on/s3"
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should give an expiring url" do
|
230
|
+
@data_store.url_for(@uid, :expires => 1301476942).should =~
|
231
|
+
%r{^https://#{@data_store.domain}/#{BUCKET_NAME}/some/path/on/s3\?AWSAccessKeyId=#{@data_store.access_key_id}&Signature=[\w%]+&Expires=1301476942$}
|
232
|
+
end
|
233
|
+
|
70
234
|
end
|
71
235
|
|
72
236
|
end
|