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.

Files changed (155) hide show
  1. data/{.specopts → .rspec} +0 -1
  2. data/.yardopts +6 -2
  3. data/Gemfile +14 -13
  4. data/History.md +47 -9
  5. data/README.md +25 -5
  6. data/Rakefile +37 -79
  7. data/VERSION +1 -1
  8. data/dragonfly.gemspec +140 -89
  9. data/extra_docs/Analysers.md +8 -48
  10. data/extra_docs/Configuration.md +40 -25
  11. data/extra_docs/Couch.md +49 -0
  12. data/extra_docs/DataStorage.md +94 -24
  13. data/extra_docs/Encoding.md +6 -35
  14. data/extra_docs/ExampleUseCases.md +113 -0
  15. data/extra_docs/GeneralUsage.md +7 -23
  16. data/extra_docs/Generators.md +15 -49
  17. data/extra_docs/Heroku.md +7 -8
  18. data/extra_docs/ImageMagick.md +126 -0
  19. data/extra_docs/MimeTypes.md +3 -3
  20. data/extra_docs/Models.md +163 -0
  21. data/extra_docs/Mongo.md +1 -4
  22. data/extra_docs/Processing.md +7 -60
  23. data/extra_docs/Rails2.md +3 -1
  24. data/extra_docs/Rails3.md +2 -10
  25. data/extra_docs/ServingRemotely.md +83 -0
  26. data/extra_docs/Sinatra.md +3 -3
  27. data/extra_docs/URLs.md +60 -33
  28. data/features/rails_3.0.5.feature +8 -0
  29. data/features/steps/rails_steps.rb +7 -18
  30. data/features/support/env.rb +10 -37
  31. data/features/support/setup.rb +32 -0
  32. data/fixtures/rails_3.0.5/files/app/models/album.rb +5 -0
  33. data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
  34. data/fixtures/{files → rails_3.0.5/files}/app/views/albums/show.html.erb +2 -0
  35. data/fixtures/{files → rails_3.0.5/files}/config/initializers/dragonfly.rb +0 -0
  36. data/fixtures/rails_3.0.5/files/features/manage_album_images.feature +38 -0
  37. data/fixtures/rails_3.0.5/files/features/step_definitions/helper_steps.rb +7 -0
  38. data/fixtures/{files → rails_3.0.5/files}/features/step_definitions/image_steps.rb +11 -1
  39. data/fixtures/{files → rails_3.0.5/files}/features/support/paths.rb +2 -0
  40. data/fixtures/{files → rails_3.0.5/files}/features/text_images.feature +0 -0
  41. data/fixtures/{rails_3.0.3 → rails_3.0.5}/template.rb +2 -2
  42. data/irbrc.rb +2 -1
  43. data/lib/dragonfly.rb +7 -0
  44. data/lib/dragonfly/active_model_extensions/attachment.rb +134 -46
  45. data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +144 -0
  46. data/lib/dragonfly/active_model_extensions/class_methods.rb +62 -9
  47. data/lib/dragonfly/active_model_extensions/instance_methods.rb +2 -2
  48. data/lib/dragonfly/active_model_extensions/validations.rb +10 -6
  49. data/lib/dragonfly/analyser.rb +0 -1
  50. data/lib/dragonfly/analysis/file_command_analyser.rb +1 -1
  51. data/lib/dragonfly/analysis/image_magick_analyser.rb +2 -43
  52. data/lib/dragonfly/app.rb +64 -55
  53. data/lib/dragonfly/config/heroku.rb +1 -1
  54. data/lib/dragonfly/config/image_magick.rb +2 -37
  55. data/lib/dragonfly/config/rails.rb +5 -2
  56. data/lib/dragonfly/configurable.rb +115 -35
  57. data/lib/dragonfly/core_ext/object.rb +1 -1
  58. data/lib/dragonfly/core_ext/string.rb +1 -1
  59. data/lib/dragonfly/data_storage/couch_data_store.rb +84 -0
  60. data/lib/dragonfly/data_storage/file_data_store.rb +43 -18
  61. data/lib/dragonfly/data_storage/mongo_data_store.rb +8 -4
  62. data/lib/dragonfly/data_storage/s3data_store.rb +82 -38
  63. data/lib/dragonfly/encoding/image_magick_encoder.rb +2 -53
  64. data/lib/dragonfly/function_manager.rb +4 -2
  65. data/lib/dragonfly/generation/image_magick_generator.rb +2 -136
  66. data/lib/dragonfly/hash_with_css_style_keys.rb +21 -0
  67. data/lib/dragonfly/image_magick/analyser.rb +51 -0
  68. data/lib/dragonfly/image_magick/config.rb +44 -0
  69. data/lib/dragonfly/{encoding/r_magick_encoder.rb → image_magick/encoder.rb} +10 -14
  70. data/lib/dragonfly/image_magick/generator.rb +145 -0
  71. data/lib/dragonfly/image_magick/processor.rb +104 -0
  72. data/lib/dragonfly/image_magick/utils.rb +72 -0
  73. data/lib/dragonfly/image_magick_utils.rb +2 -79
  74. data/lib/dragonfly/job.rb +152 -90
  75. data/lib/dragonfly/middleware.rb +5 -19
  76. data/lib/dragonfly/processing/image_magick_processor.rb +2 -95
  77. data/lib/dragonfly/rails/images.rb +15 -10
  78. data/lib/dragonfly/response.rb +26 -12
  79. data/lib/dragonfly/serializer.rb +1 -4
  80. data/lib/dragonfly/server.rb +103 -0
  81. data/lib/dragonfly/temp_object.rb +56 -101
  82. data/lib/dragonfly/url_mapper.rb +78 -0
  83. data/spec/dragonfly/active_model_extensions/model_spec.rb +772 -65
  84. data/spec/dragonfly/active_model_extensions/spec_helper.rb +90 -10
  85. data/spec/dragonfly/analyser_spec.rb +1 -1
  86. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +5 -14
  87. data/spec/dragonfly/app_spec.rb +35 -180
  88. data/spec/dragonfly/configurable_spec.rb +259 -18
  89. data/spec/dragonfly/core_ext/string_spec.rb +2 -2
  90. data/spec/dragonfly/core_ext/symbol_spec.rb +1 -1
  91. data/spec/dragonfly/data_storage/couch_data_store_spec.rb +84 -0
  92. data/spec/dragonfly/data_storage/file_data_store_spec.rb +149 -22
  93. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +21 -2
  94. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +207 -43
  95. data/spec/dragonfly/data_storage/{data_store_spec.rb → shared_data_store_examples.rb} +16 -15
  96. data/spec/dragonfly/function_manager_spec.rb +2 -2
  97. data/spec/dragonfly/{generation/hash_with_css_style_keys_spec.rb → hash_with_css_style_keys_spec.rb} +2 -2
  98. data/spec/dragonfly/{analysis/shared_analyser_spec.rb → image_magick/analyser_spec.rb} +19 -6
  99. data/spec/dragonfly/{encoding/image_magick_encoder_spec.rb → image_magick/encoder_spec.rb} +2 -2
  100. data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
  101. data/spec/dragonfly/{processing/shared_processing_spec.rb → image_magick/processor_spec.rb} +55 -6
  102. data/spec/dragonfly/image_magick/utils_spec.rb +18 -0
  103. data/spec/dragonfly/job_builder_spec.rb +1 -1
  104. data/spec/dragonfly/job_definitions_spec.rb +1 -1
  105. data/spec/dragonfly/job_endpoint_spec.rb +26 -3
  106. data/spec/dragonfly/job_spec.rb +426 -208
  107. data/spec/dragonfly/loggable_spec.rb +2 -2
  108. data/spec/dragonfly/middleware_spec.rb +5 -26
  109. data/spec/dragonfly/routed_endpoint_spec.rb +1 -1
  110. data/spec/dragonfly/serializer_spec.rb +1 -14
  111. data/spec/dragonfly/server_spec.rb +261 -0
  112. data/spec/dragonfly/simple_cache_spec.rb +1 -1
  113. data/spec/dragonfly/temp_object_spec.rb +84 -130
  114. data/spec/dragonfly/url_mapper_spec.rb +130 -0
  115. data/spec/functional/deprecations_spec.rb +51 -0
  116. data/spec/functional/image_magick_app_spec.rb +27 -0
  117. data/spec/functional/model_urls_spec.rb +85 -0
  118. data/spec/functional/remote_on_the_fly_spec.rb +51 -0
  119. data/spec/functional/to_response_spec.rb +31 -0
  120. data/spec/spec_helper.rb +12 -22
  121. data/spec/{argument_matchers.rb → support/argument_matchers.rb} +0 -0
  122. data/spec/{image_matchers.rb → support/image_matchers.rb} +4 -4
  123. data/spec/support/simple_matchers.rb +53 -0
  124. data/yard/handlers/configurable_attr_handler.rb +2 -2
  125. data/yard/templates/default/fulldoc/html/css/common.css +12 -10
  126. data/yard/templates/default/layout/html/layout.erb +6 -0
  127. metadata +267 -308
  128. data/Gemfile.rails.2.3.5 +0 -20
  129. data/features/3.0.3.feature +0 -8
  130. data/features/rails_2.3.5.feature +0 -7
  131. data/fixtures/files/app/models/album.rb +0 -3
  132. data/fixtures/files/app/views/albums/new.html.erb +0 -4
  133. data/fixtures/files/features/manage_album_images.feature +0 -12
  134. data/fixtures/rails_2.3.5/template.rb +0 -10
  135. data/lib/dragonfly/analysis/r_magick_analyser.rb +0 -63
  136. data/lib/dragonfly/config/r_magick.rb +0 -46
  137. data/lib/dragonfly/generation/hash_with_css_style_keys.rb +0 -23
  138. data/lib/dragonfly/generation/r_magick_generator.rb +0 -155
  139. data/lib/dragonfly/processing/r_magick_processor.rb +0 -126
  140. data/lib/dragonfly/r_magick_utils.rb +0 -48
  141. data/lib/dragonfly/simple_endpoint.rb +0 -76
  142. data/spec/dragonfly/active_model_extensions/active_model_setup.rb +0 -97
  143. data/spec/dragonfly/active_model_extensions/active_record_setup.rb +0 -85
  144. data/spec/dragonfly/analysis/image_magick_analyser_spec.rb +0 -15
  145. data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +0 -31
  146. data/spec/dragonfly/config/r_magick_spec.rb +0 -29
  147. data/spec/dragonfly/encoding/r_magick_encoder_spec.rb +0 -41
  148. data/spec/dragonfly/generation/image_magick_generator_spec.rb +0 -12
  149. data/spec/dragonfly/generation/r_magick_generator_spec.rb +0 -28
  150. data/spec/dragonfly/generation/shared_generator_spec.rb +0 -91
  151. data/spec/dragonfly/image_magick_utils_spec.rb +0 -16
  152. data/spec/dragonfly/processing/image_magick_processor_spec.rb +0 -29
  153. data/spec/dragonfly/processing/r_magick_processor_spec.rb +0 -30
  154. data/spec/dragonfly/simple_endpoint_spec.rb +0 -97
  155. data/spec/simple_matchers.rb +0 -44
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe String do
4
4
 
@@ -13,5 +13,5 @@ describe String do
13
13
  end
14
14
  end
15
15
  end
16
-
16
+
17
17
  end
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe Symbol do
4
4
 
@@ -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 File.dirname(__FILE__) + '/../../spec_helper'
2
- require File.dirname(__FILE__) + '/data_store_spec'
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 name if it exists" do
46
- @temp_object.should_receive(:name).at_least(:once).and_return('hello.there')
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 get rid of funny characters in the temp_object name" do
52
- @temp_object.should_receive(:name).at_least(:once).and_return('A Picture with many spaces in its name (at 20:00 pm).png')
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(:name).at_least(:once).and_return('hello.png')
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(:name).at_least(:once).and_return('hello.you.png')
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
- file, extra = @data_store.retrieve(uid)
143
- file.should be_closed
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 extra data)" do
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
- file, meta = @data_store.retrieve("jelly_beans/are/good")
150
- File.read(file.path).should == 'hey dog'
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 raise an error if the data doesn't exist" do
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('gooble/gubbub')
189
+ @data_store.destroy(uid)
165
190
  }.should raise_error(Dragonfly::DataStorage::DataNotFound)
166
191
  end
167
192
 
168
- it "should prune empty directories when destroying" do
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 be_an_empty_directory
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
- end
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 File.dirname(__FILE__) + '/../../spec_helper'
3
- require File.dirname(__FILE__) + '/data_store_spec'
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 File.dirname(__FILE__) + '/../../spec_helper'
2
- require File.dirname(__FILE__) + '/data_store_spec'
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
- describe "common data_store behaviour" do
22
+ # Make sure it's a new bucket name
23
+ BUCKET_NAME = "dragonfly-test-#{Time.now.to_i.to_s(36)}"
23
24
 
24
- before(:each) do
25
- @data_store = Dragonfly::DataStorage::S3DataStore.new
26
- @data_store.configure do |d|
27
- d.bucket_name = 'dragonfly_test'
28
- d.access_key_id = KEY
29
- d.secret_access_key = SECRET
30
- end
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
- it_should_behave_like 'data_store'
34
-
35
- describe "store" do
36
- it "should return a unique identifier for each storage" do
37
- temp_object = Dragonfly::TempObject.new('gollum')
38
- temp_object2 = Dragonfly::TempObject.new('gollum')
39
- @data_store.store(temp_object).should_not == @data_store.store(temp_object2)
40
- end
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