dragonfly 0.9.5 → 0.9.8

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 (39) hide show
  1. data/Gemfile +1 -2
  2. data/History.md +29 -0
  3. data/README.md +1 -1
  4. data/VERSION +1 -1
  5. data/dragonfly.gemspec +15 -14
  6. data/extra_docs/DataStorage.md +5 -2
  7. data/extra_docs/Rails3.md +6 -4
  8. data/features/steps/rails_steps.rb +18 -6
  9. data/features/support/setup.rb +10 -1
  10. data/fixtures/rails/files/features/step_definitions/image_steps.rb +1 -1
  11. data/lib/dragonfly.rb +4 -0
  12. data/lib/dragonfly/analysis/file_command_analyser.rb +2 -1
  13. data/lib/dragonfly/data_storage/couch_data_store.rb +1 -1
  14. data/lib/dragonfly/data_storage/mongo_data_store.rb +4 -2
  15. data/lib/dragonfly/data_storage/s3data_store.rb +5 -1
  16. data/lib/dragonfly/image_magick/generator.rb +2 -2
  17. data/lib/dragonfly/image_magick/processor.rb +2 -2
  18. data/lib/dragonfly/image_magick/utils.rb +3 -25
  19. data/lib/dragonfly/rails/images.rb +17 -12
  20. data/lib/dragonfly/railtie.rb +1 -1
  21. data/lib/dragonfly/shell.rb +44 -0
  22. data/lib/dragonfly/temp_object.rb +13 -6
  23. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +1 -0
  24. data/spec/dragonfly/cookie_monster_spec.rb +4 -2
  25. data/spec/dragonfly/data_storage/file_data_store_spec.rb +1 -1
  26. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +23 -8
  27. data/spec/dragonfly/image_magick/generator_spec.rb +0 -5
  28. data/spec/dragonfly/job_spec.rb +5 -1
  29. data/spec/dragonfly/shell_spec.rb +34 -0
  30. data/spec/dragonfly/temp_object_spec.rb +44 -12
  31. data/spec/functional/image_magick_app_spec.rb +1 -1
  32. data/spec/functional/remote_on_the_fly_spec.rb +6 -6
  33. data/spec/functional/shell_commands_spec.rb +23 -0
  34. data/spec/functional/to_response_spec.rb +1 -1
  35. data/spec/spec_helper.rb +6 -3
  36. data/spec/support/image_matchers.rb +1 -0
  37. data/tmp/.gitignore +2 -0
  38. metadata +71 -85
  39. data/spec/dragonfly/image_magick/utils_spec.rb +0 -18
@@ -1,5 +1,13 @@
1
- require 'dragonfly'
2
1
  require 'uri'
2
+ require 'dragonfly'
3
+ begin
4
+ require 'rack/cache'
5
+ rescue LoadError => e
6
+ puts "Couldn't find rack-cache - make sure you have it in your Gemfile:"
7
+ puts " gem 'rack-cache', :require => 'rack/cache'"
8
+ puts " or configure dragonfly manually instead of using 'dragonfly/rails/images'"
9
+ raise e
10
+ end
3
11
 
4
12
  ### The dragonfly app ###
5
13
  app = Dragonfly[:images]
@@ -13,15 +21,12 @@ if defined?(ActiveRecord::Base)
13
21
  end
14
22
 
15
23
  ### Insert the middleware ###
16
- Rails.application.middleware.insert 0, 'Dragonfly::Middleware', :images
24
+ rack_cache_already_inserted = Rails.application.config.action_controller.perform_caching && Rails.application.config.action_dispatch.rack_cache
17
25
 
18
- begin
19
- require 'rack/cache'
20
- Rails.application.middleware.insert_before 'Dragonfly::Middleware', 'Rack::Cache', {
21
- :verbose => true,
22
- :metastore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/meta"), # URI encoded in case of spaces
23
- :entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body")
24
- }
25
- rescue LoadError => e
26
- app.log.warn("Warning: couldn't find rack-cache for caching dragonfly content")
27
- end
26
+ Rails.application.middleware.insert 0, Rack::Cache, {
27
+ :verbose => true,
28
+ :metastore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/meta"), # URI encoded in case of spaces
29
+ :entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body")
30
+ } unless rack_cache_already_inserted
31
+
32
+ Rails.application.middleware.insert_after Rack::Cache, Dragonfly::Middleware, :images
@@ -4,7 +4,7 @@ require 'rails'
4
4
  module Dragonfly
5
5
  class Railtie < ::Rails::Railtie
6
6
  initializer "dragonfly.railtie.initializer" do |app|
7
- app.middleware.insert 0, Dragonfly::CookieMonster
7
+ app.middleware.insert 1, Dragonfly::CookieMonster
8
8
  end
9
9
  end
10
10
  end
@@ -0,0 +1,44 @@
1
+ require 'shellwords'
2
+
3
+ module Dragonfly
4
+ module Shell
5
+
6
+ include Configurable
7
+ configurable_attr :log_commands, false
8
+
9
+ # Exceptions
10
+ class CommandFailed < RuntimeError; end
11
+
12
+ def run(command, args="")
13
+ full_command = "#{command} #{escape_args(args)}"
14
+ log.debug("Running command: #{full_command}") if log_commands
15
+ begin
16
+ result = `#{full_command}`
17
+ rescue Errno::ENOENT
18
+ raise_shell_command_failed(full_command)
19
+ end
20
+ if $?.exitstatus == 1
21
+ throw :unable_to_handle
22
+ elsif !$?.success?
23
+ raise_shell_command_failed(full_command)
24
+ end
25
+ result
26
+ end
27
+
28
+ def raise_shell_command_failed(command)
29
+ raise CommandFailed, "Command failed (#{command}) with exit status #{$?.exitstatus}"
30
+ end
31
+
32
+ def escape_args(args)
33
+ args.shellsplit.map do |arg|
34
+ quote arg.gsub(/\\?'/, %q('\\\\''))
35
+ end.join(' ')
36
+ end
37
+
38
+ def quote(string)
39
+ q = Dragonfly.running_on_windows? ? '"' : "'"
40
+ q + string + q
41
+ end
42
+
43
+ end
44
+ end
@@ -56,17 +56,24 @@ module Dragonfly
56
56
  @tempfile = obj
57
57
  elsif obj.is_a? File
58
58
  @pathname = Pathname.new(obj.path)
59
- @original_filename = @pathname.basename.to_s
60
59
  elsif obj.is_a? Pathname
61
60
  @pathname = obj
62
- @original_filename = @pathname.basename.to_s
63
61
  elsif obj.respond_to?(:tempfile)
64
62
  @tempfile = obj.tempfile
63
+ elsif obj.respond_to?(:path) # e.g. Rack::Test::UploadedFile
64
+ @pathname = Pathname.new(obj.path)
65
65
  else
66
- raise ArgumentError, "#{self.class.name} must be initialized with a String, a Pathname, a File, a Tempfile, another TempObject, or something that responds to .tempfile"
66
+ raise ArgumentError, "#{self.class.name} must be initialized with a String, a Pathname, a File, a Tempfile, another TempObject, something that responds to .tempfile, or something that responds to .path"
67
67
  end
68
+
68
69
  @tempfile.close if @tempfile
69
- @original_filename = obj.original_filename if obj.respond_to?(:original_filename)
70
+
71
+ # Original filename
72
+ @original_filename = if obj.respond_to?(:original_filename)
73
+ obj.original_filename
74
+ elsif @pathname
75
+ @pathname.basename.to_s
76
+ end
70
77
  end
71
78
 
72
79
  attr_reader :original_filename
@@ -94,7 +101,7 @@ module Dragonfly
94
101
  tempfile.binmode
95
102
  if block_given?
96
103
  ret = yield f
97
- tempfile.close
104
+ tempfile.close unless tempfile.closed?
98
105
  else
99
106
  ret = f
100
107
  end
@@ -188,4 +195,4 @@ module Dragonfly
188
195
  end
189
196
 
190
197
  end
191
- end
198
+ end
@@ -5,6 +5,7 @@ png_path = File.dirname(__FILE__) + '/../../../samples/egg.png'
5
5
  describe Dragonfly::Analysis::FileCommandAnalyser do
6
6
 
7
7
  before(:each) do
8
+ pending "not applicable to windows" if Dragonfly.running_on_windows?
8
9
  @analyser = Dragonfly::Analysis::FileCommandAnalyser.new
9
10
  end
10
11
 
@@ -14,14 +14,16 @@ describe Dragonfly::CookieMonster do
14
14
  response = Rack::MockRequest.new(app).get('')
15
15
  response.status.should == 200
16
16
  response.body.should == "body here"
17
- response.headers.should == {"Set-Cookie" => "blah", "Something" => "else"}
17
+ response.headers["Set-Cookie"].should == "blah"
18
+ response.headers["Something"].should == "else"
18
19
  end
19
20
 
20
21
  it "should delete the set-cookie header from the response if the response comes from dragonfly" do
21
22
  response = Rack::MockRequest.new(app('dragonfly.job' => mock)).get('')
22
23
  response.status.should == 200
23
24
  response.body.should == "body here"
24
- response.headers.should == {"Something" => "else"}
25
+ response.headers["Set-Cookie"].should be_nil
26
+ response.headers["Something"].should == "else"
25
27
  end
26
28
 
27
29
  end
@@ -10,7 +10,7 @@ describe Dragonfly::DataStorage::FileDataStore do
10
10
 
11
11
  before(:each) do
12
12
  @data_store = Dragonfly::DataStorage::FileDataStore.new
13
- @data_store.root_path = '/var/tmp/dragonfly_test'
13
+ @data_store.root_path = 'tmp/file_data_store_test'
14
14
  end
15
15
 
16
16
  after(:each) do
@@ -4,7 +4,7 @@ require File.dirname(__FILE__) + '/shared_data_store_examples'
4
4
  require 'mongo'
5
5
 
6
6
  describe Dragonfly::DataStorage::MongoDataStore do
7
-
7
+
8
8
  before(:each) do
9
9
  begin
10
10
  Mongo::Connection.new
@@ -13,19 +13,19 @@ describe Dragonfly::DataStorage::MongoDataStore do
13
13
  end
14
14
  @data_store = Dragonfly::DataStorage::MongoDataStore.new :database => 'dragonfly_test'
15
15
  end
16
-
16
+
17
17
  it_should_behave_like 'data_store'
18
-
18
+
19
19
  describe "authenticating" do
20
20
  before(:each) do
21
21
  @temp_object = Dragonfly::TempObject.new('Feijão verde')
22
22
  end
23
-
23
+
24
24
  it "should not attempt to authenticate if a username is not given" do
25
25
  @data_store.db.should_not_receive(:authenticate)
26
- @data_store.store(@temp_object)
26
+ @data_store.store(@temp_object)
27
27
  end
28
-
28
+
29
29
  it "should attempt to authenticate once if a username is given" do
30
30
  @data_store.username = 'terry'
31
31
  @data_store.password = 'butcher'
@@ -40,13 +40,13 @@ describe Dragonfly::DataStorage::MongoDataStore do
40
40
  @connection = Mongo::Connection.new
41
41
  @temp_object = Dragonfly::TempObject.new('asdf')
42
42
  end
43
-
43
+
44
44
  it "should allow sharing the connection" do
45
45
  @data_store.connection = @connection
46
46
  @connection.should_receive(:db).with('dragonfly_test').and_return(db=mock)
47
47
  @data_store.db.should == db
48
48
  end
49
-
49
+
50
50
  it "should allow sharing the db" do
51
51
  db = @connection.db('dragonfly_test_yo')
52
52
  @data_store.db = db
@@ -54,4 +54,19 @@ describe Dragonfly::DataStorage::MongoDataStore do
54
54
  end
55
55
  end
56
56
 
57
+ describe "extra options" do
58
+
59
+ before(:each) do
60
+ @temp_object = Dragonfly::TempObject.new('testingyo')
61
+ end
62
+
63
+ [:content_type, :mime_type].each do |key|
64
+ it "should allow setting content type on store with #{key.inspect}" do
65
+ uid = @data_store.store(@temp_object, key => 'text/plain')
66
+ @data_store.grid.get(BSON::ObjectId(uid)).content_type.should == 'text/plain'
67
+ @data_store.grid.get(BSON::ObjectId(uid)).read.should == 'testingyo'
68
+ end
69
+ end
70
+ end
71
+
57
72
  end
@@ -27,11 +27,6 @@ describe Dragonfly::ImageMagick::Generator do
27
27
  image.should have_width(1)
28
28
  end
29
29
 
30
- it "should cope with rgb format with whitespace" do
31
- image, meta = @generator.plain(1, 1, ' rgb ( 244 , 255 , 1 ) ')
32
- image.should have_width(1)
33
- end
34
-
35
30
  it "should cope with rgb percent format" do
36
31
  image, meta = @generator.plain(1, 1, 'rgb(100%,50%,20%)')
37
32
  image.should have_width(1)
@@ -749,7 +749,11 @@ describe Dragonfly::Job do
749
749
  it "should return the fetch_file step otherwise" do
750
750
  step = @app.fetch_file('/my/file.png').process(:cheese).fetch_file_step
751
751
  step.should be_a(Dragonfly::Job::FetchFile)
752
- step.path.should == '/my/file.png'
752
+ if Dragonfly.running_on_windows?
753
+ step.path.should =~ %r(:/my/file\.png$)
754
+ else
755
+ step.path.should == '/my/file.png'
756
+ end
753
757
  end
754
758
  end
755
759
 
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dragonfly::Shell do
4
+
5
+ include Dragonfly::Shell
6
+
7
+ it "should raise an error if the identify command isn't found" do
8
+ suppressing_stderr do
9
+ lambda{
10
+ run "non-existent-command"
11
+ }.should raise_error(Dragonfly::Shell::CommandFailed)
12
+ end
13
+ end
14
+
15
+ describe "escaping args" do
16
+ {
17
+ %q(hello) => %q('hello'),
18
+ %q("hello") => %q('hello'),
19
+ %q('hello') => %q('hello'),
20
+ %q(he\'llo) => %q('he'\''llo'),
21
+ %q('he'\''llo') => %q('he'\''llo'),
22
+ %q("he'llo") => %q('he'\''llo'),
23
+ %q(hel$(lo)) => %q('hel$(lo)'),
24
+ %q(hel\$(lo)) => %q('hel$(lo)'),
25
+ %q('hel\$(lo)') => %q('hel\$(lo)')
26
+ }.each do |args, escaped_args|
27
+ it "should escape #{args.inspect} -> #{escaped_args.inspect}" do
28
+ pending "not applicable to windows" if Dragonfly.running_on_windows?
29
+ escape_args(args).should == escaped_args
30
+ end
31
+ end
32
+ end
33
+
34
+ end
@@ -15,14 +15,14 @@ describe Dragonfly::TempObject do
15
15
  tempfile
16
16
  end
17
17
 
18
- def new_file(data='HELLO', path="/tmp/test_file")
18
+ def new_file(data='HELLO', path="tmp/test_file")
19
19
  File.open(path, 'w') do |f|
20
20
  f.write(data)
21
21
  end
22
22
  File.new(path)
23
23
  end
24
24
 
25
- def new_pathname(data='HELLO', path="/tmp/test_file")
25
+ def new_pathname(data='HELLO', path="tmp/test_file")
26
26
  File.open(path, 'w') do |f|
27
27
  f.write(data)
28
28
  end
@@ -104,7 +104,11 @@ describe Dragonfly::TempObject do
104
104
 
105
105
  describe "path" do
106
106
  it "should return an absolute file path" do
107
- @temp_object.path.should =~ %r{^/\w+}
107
+ if Dragonfly.running_on_windows?
108
+ @temp_object.path.should =~ %r{^[a-zA-Z]:/\w+}
109
+ else
110
+ @temp_object.path.should =~ %r{^/\w+}
111
+ end
108
112
  end
109
113
  end
110
114
 
@@ -116,11 +120,11 @@ describe Dragonfly::TempObject do
116
120
 
117
121
  describe "to_file" do
118
122
  before(:each) do
119
- @filename = 'eggnog.txt'
120
- FileUtils.rm(@filename) if File.exists?(@filename)
123
+ @filename = 'tmp/eggnog.txt'
124
+ FileUtils.rm_f(@filename) if File.exists?(@filename)
121
125
  end
122
126
  after(:each) do
123
- FileUtils.rm(@filename) if File.exists?(@filename)
127
+ FileUtils.rm_f(@filename) if File.exists?(@filename)
124
128
  end
125
129
  it "should write to a file" do
126
130
  @temp_object.to_file(@filename)
@@ -246,13 +250,17 @@ describe Dragonfly::TempObject do
246
250
  it "should return the file's path" do
247
251
  file = new_file('HELLO')
248
252
  temp_object = Dragonfly::TempObject.new(file)
249
- temp_object.path.should == file.path
253
+ temp_object.path.should == File.expand_path(file.path)
250
254
  end
251
255
 
252
256
  it "should return an absolute path even if the file wasn't instantiated like that" do
253
- file = new_file('HELLO', 'testfile')
257
+ file = new_file('HELLO', 'tmp/bongo')
254
258
  temp_object = Dragonfly::TempObject.new(file)
255
- temp_object.path.should =~ %r{^/\w.*testfile}
259
+ if Dragonfly.running_on_windows?
260
+ temp_object.path.should =~ %r{^[a-zA-Z]:/\w.*bongo}
261
+ else
262
+ temp_object.path.should =~ %r{^/\w.*bongo}
263
+ end
256
264
  file.close
257
265
  FileUtils.rm(file.path)
258
266
  end
@@ -275,13 +283,17 @@ describe Dragonfly::TempObject do
275
283
  it "should return the file's path" do
276
284
  pathname = new_pathname('HELLO')
277
285
  temp_object = Dragonfly::TempObject.new(pathname)
278
- temp_object.path.should == pathname.to_s
286
+ temp_object.path.should == File.expand_path(pathname.to_s)
279
287
  end
280
288
 
281
289
  it "should return an absolute path even if the pathname is relative" do
282
- pathname = new_pathname('HELLO', 'testfile')
290
+ pathname = new_pathname('HELLO', 'tmp/bingo')
283
291
  temp_object = Dragonfly::TempObject.new(pathname)
284
- temp_object.path.should =~ %r{^/\w.*testfile}
292
+ if Dragonfly.running_on_windows?
293
+ temp_object.path.should =~ %r{^[a-zA-Z]:/\w.*bingo}
294
+ else
295
+ temp_object.path.should =~ %r{^/\w.*bingo}
296
+ end
285
297
  pathname.delete
286
298
  end
287
299
  end
@@ -310,6 +322,26 @@ describe Dragonfly::TempObject do
310
322
  end
311
323
  end
312
324
 
325
+ describe "initialize from a Rack::Test::UploadedFile" do
326
+ def initialization_object(data)
327
+ # The criteria we're using to determine if an object is a
328
+ # Rack::Test::UploadedFile is if it responds to path
329
+ #
330
+ # We can't just check if it is_a?(Rack::Test::UploadedFile) because that
331
+ # class may not always be present.
332
+ uploaded_file = mock("mock_uploadedfile")
333
+ uploaded_file.stub!(:path).and_return File.expand_path('tmp/test_file')
334
+ uploaded_file.stub!(:original_filename).and_return('foo.jpg')
335
+
336
+ # Create a real file with the contents required at the correct path
337
+ new_file(data, 'tmp/test_file')
338
+
339
+ uploaded_file
340
+ end
341
+
342
+ it_should_behave_like "common behaviour"
343
+ end
344
+
313
345
  describe "original_filename" do
314
346
  before(:each) do
315
347
  @obj = new_tempfile
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe "a configured imagemagick app" do
4
4
 
5
5
  before(:each) do
6
- @app = Dragonfly[:images].configure_with(:imagemagick)
6
+ @app = test_app.configure_with(:imagemagick)
7
7
  end
8
8
 
9
9
  describe "convert command path" do
@@ -22,15 +22,15 @@ describe "remote on-the-fly urls" do
22
22
  end
23
23
  end
24
24
  c.datastore = Dragonfly::DataStorage::FileDataStore.new
25
- c.datastore.root_path = '/var/tmp/dragonfly'
26
- c.datastore.server_root = '/var/tmp'
25
+ c.datastore.root_path = 'tmp/dragonfly_test_urls'
26
+ c.datastore.server_root = 'tmp'
27
27
  end
28
28
  @job = @app.generate(:test)
29
29
  end
30
30
 
31
31
  after(:each) do
32
32
  THUMBS.delete_if{true}
33
- FileUtils.rm_f('/var/tmp/dragonfly/yay.txt')
33
+ FileUtils.rm_f('tmp/dragonfly_test_urls/yay.txt')
34
34
  end
35
35
 
36
36
  it "should give the url for the server" do
@@ -38,14 +38,14 @@ describe "remote on-the-fly urls" do
38
38
  end
39
39
 
40
40
  it "should store the content when first called" do
41
- File.exist?('/var/tmp/dragonfly/yay.txt').should be_false
41
+ File.exist?('tmp/dragonfly_test_urls/yay.txt').should be_false
42
42
  @app.server.call('PATH_INFO' => @job.url, 'REQUEST_METHOD' => 'GET')
43
- File.read('/var/tmp/dragonfly/yay.txt').should == 'TEST'
43
+ File.read('tmp/dragonfly_test_urls/yay.txt').should == 'TEST'
44
44
  end
45
45
 
46
46
  it "should point to the external url the second time" do
47
47
  @app.server.call('PATH_INFO' => @job.url, 'REQUEST_METHOD' => 'GET')
48
- @job.url.should == '/dragonfly/yay.txt'
48
+ @job.url.should == '/dragonfly_test_urls/yay.txt'
49
49
  end
50
50
 
51
51
  end