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.
- data/Gemfile +1 -2
- data/History.md +29 -0
- data/README.md +1 -1
- data/VERSION +1 -1
- data/dragonfly.gemspec +15 -14
- data/extra_docs/DataStorage.md +5 -2
- data/extra_docs/Rails3.md +6 -4
- data/features/steps/rails_steps.rb +18 -6
- data/features/support/setup.rb +10 -1
- data/fixtures/rails/files/features/step_definitions/image_steps.rb +1 -1
- data/lib/dragonfly.rb +4 -0
- data/lib/dragonfly/analysis/file_command_analyser.rb +2 -1
- data/lib/dragonfly/data_storage/couch_data_store.rb +1 -1
- data/lib/dragonfly/data_storage/mongo_data_store.rb +4 -2
- data/lib/dragonfly/data_storage/s3data_store.rb +5 -1
- data/lib/dragonfly/image_magick/generator.rb +2 -2
- data/lib/dragonfly/image_magick/processor.rb +2 -2
- data/lib/dragonfly/image_magick/utils.rb +3 -25
- data/lib/dragonfly/rails/images.rb +17 -12
- data/lib/dragonfly/railtie.rb +1 -1
- data/lib/dragonfly/shell.rb +44 -0
- data/lib/dragonfly/temp_object.rb +13 -6
- data/spec/dragonfly/analysis/file_command_analyser_spec.rb +1 -0
- data/spec/dragonfly/cookie_monster_spec.rb +4 -2
- data/spec/dragonfly/data_storage/file_data_store_spec.rb +1 -1
- data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +23 -8
- data/spec/dragonfly/image_magick/generator_spec.rb +0 -5
- data/spec/dragonfly/job_spec.rb +5 -1
- data/spec/dragonfly/shell_spec.rb +34 -0
- data/spec/dragonfly/temp_object_spec.rb +44 -12
- data/spec/functional/image_magick_app_spec.rb +1 -1
- data/spec/functional/remote_on_the_fly_spec.rb +6 -6
- data/spec/functional/shell_commands_spec.rb +23 -0
- data/spec/functional/to_response_spec.rb +1 -1
- data/spec/spec_helper.rb +6 -3
- data/spec/support/image_matchers.rb +1 -0
- data/tmp/.gitignore +2 -0
- metadata +71 -85
- 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.
|
24
|
+
rack_cache_already_inserted = Rails.application.config.action_controller.perform_caching && Rails.application.config.action_dispatch.rack_cache
|
17
25
|
|
18
|
-
|
19
|
-
|
20
|
-
Rails.
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
data/lib/dragonfly/railtie.rb
CHANGED
@@ -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 .
|
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
|
-
|
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
|
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
|
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 = '
|
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)
|
data/spec/dragonfly/job_spec.rb
CHANGED
@@ -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
|
-
|
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="
|
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="
|
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
|
-
|
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.
|
123
|
+
@filename = 'tmp/eggnog.txt'
|
124
|
+
FileUtils.rm_f(@filename) if File.exists?(@filename)
|
121
125
|
end
|
122
126
|
after(:each) do
|
123
|
-
FileUtils.
|
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', '
|
257
|
+
file = new_file('HELLO', 'tmp/bongo')
|
254
258
|
temp_object = Dragonfly::TempObject.new(file)
|
255
|
-
|
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', '
|
290
|
+
pathname = new_pathname('HELLO', 'tmp/bingo')
|
283
291
|
temp_object = Dragonfly::TempObject.new(pathname)
|
284
|
-
|
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
|
@@ -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 = '
|
26
|
-
c.datastore.server_root = '
|
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('
|
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?('
|
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('
|
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 == '/
|
48
|
+
@job.url.should == '/dragonfly_test_urls/yay.txt'
|
49
49
|
end
|
50
50
|
|
51
51
|
end
|