dragonfly 0.9.12 → 0.9.13
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 +7 -6
- data/History.md +6 -0
- data/README.md +1 -1
- data/VERSION +1 -1
- data/dragonfly.gemspec +26 -68
- data/extra_docs/Rails3.md +1 -1
- data/features/no_processing.feature +1 -1
- data/fixtures/rails/files/app/models/album.rb +1 -0
- data/fixtures/rails/files/features/step_definitions/web_steps.rb +189 -0
- data/lib/dragonfly/active_model_extensions/class_methods.rb +15 -15
- data/lib/dragonfly/image_magick/generator.rb +2 -0
- data/lib/dragonfly/image_magick/processor.rb +26 -20
- data/lib/dragonfly/job.rb +24 -18
- data/lib/dragonfly/routed_endpoint.rb +1 -8
- data/lib/dragonfly/serializer.rb +20 -9
- data/lib/dragonfly/server.rb +5 -6
- data/lib/dragonfly/url_mapper.rb +11 -17
- data/lib/dragonfly/utils.rb +24 -0
- data/spec/dragonfly/active_model_extensions/model_spec.rb +60 -60
- data/spec/dragonfly/data_storage/s3_data_store_spec.rb +25 -25
- data/spec/dragonfly/image_magick/analyser_spec.rb +3 -8
- data/spec/dragonfly/image_magick/encoder_spec.rb +12 -7
- data/spec/dragonfly/image_magick/generator_spec.rb +18 -7
- data/spec/dragonfly/image_magick/processor_spec.rb +28 -28
- data/spec/dragonfly/job_endpoint_spec.rb +9 -9
- data/spec/dragonfly/job_spec.rb +54 -53
- data/spec/dragonfly/serializer_spec.rb +36 -20
- data/spec/dragonfly/server_spec.rb +23 -41
- data/spec/dragonfly/url_mapper_spec.rb +12 -3
- metadata +166 -98
@@ -6,34 +6,34 @@ module Dragonfly
|
|
6
6
|
|
7
7
|
def register_dragonfly_app(macro_name, app)
|
8
8
|
(class << self; self; end).class_eval do
|
9
|
-
|
9
|
+
|
10
10
|
# Defines e.g. 'image_accessor' for any activemodel class body
|
11
11
|
define_method macro_name do |attribute, &config_block|
|
12
12
|
|
13
13
|
# Add callbacks
|
14
14
|
before_save :save_dragonfly_attachments
|
15
15
|
before_destroy :destroy_dragonfly_attachments
|
16
|
-
|
16
|
+
|
17
17
|
# Register the new attribute
|
18
18
|
dragonfly_attachment_classes << new_dragonfly_attachment_class(attribute, app, config_block)
|
19
|
-
|
19
|
+
|
20
20
|
# Define the setter for the attribute
|
21
21
|
define_method "#{attribute}=" do |value|
|
22
22
|
dragonfly_attachments[attribute].assign(value)
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
# Define the getter for the attribute
|
26
26
|
define_method attribute do
|
27
27
|
dragonfly_attachments[attribute].to_value
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
# Define the URL setter
|
31
31
|
define_method "#{attribute}_url=" do |url|
|
32
32
|
unless url.blank?
|
33
33
|
dragonfly_attachments[attribute].assign(app.fetch_url(url))
|
34
34
|
end
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
# Define the URL getter
|
38
38
|
define_method "#{attribute}_url" do
|
39
39
|
nil
|
@@ -54,28 +54,28 @@ module Dragonfly
|
|
54
54
|
define_method "retained_#{attribute}=" do |string|
|
55
55
|
unless string.blank?
|
56
56
|
begin
|
57
|
-
dragonfly_attachments[attribute].retained_attrs = Serializer.
|
57
|
+
dragonfly_attachments[attribute].retained_attrs = Serializer.json_decode(string, :symbolize_keys => true)
|
58
58
|
rescue Serializer::BadString => e
|
59
|
-
app.log.warn("*** WARNING ***: couldn't update attachment with serialized retained_#{attribute} string #{string.inspect}")
|
59
|
+
app.log.warn("*** WARNING ***: couldn't update attachment with serialized retained_#{attribute} string #{string.inspect}")
|
60
60
|
end
|
61
61
|
end
|
62
62
|
dragonfly_attachments[attribute].should_retain = true
|
63
63
|
dragonfly_attachments[attribute].retain!
|
64
64
|
string
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
# Define the retained getter
|
68
68
|
define_method "retained_#{attribute}" do
|
69
69
|
attrs = dragonfly_attachments[attribute].retained_attrs
|
70
|
-
Serializer.
|
70
|
+
Serializer.json_encode(attrs) if attrs
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
end
|
74
|
-
|
74
|
+
|
75
75
|
end
|
76
76
|
app
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
def dragonfly_attachment_classes
|
80
80
|
@dragonfly_attachment_classes ||= begin
|
81
81
|
parent_class = ancestors.select{|a| a.is_a?(Class) }[1]
|
@@ -88,11 +88,11 @@ module Dragonfly
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
def new_dragonfly_attachment_class(attribute, app, config_block)
|
93
93
|
Class.new(Attachment).init(self, attribute, app, config_block)
|
94
94
|
end
|
95
|
-
|
95
|
+
|
96
96
|
end
|
97
97
|
end
|
98
98
|
end
|
@@ -13,25 +13,27 @@ module Dragonfly
|
|
13
13
|
's' => 'South',
|
14
14
|
'se' => 'SouthEast'
|
15
15
|
}
|
16
|
-
|
16
|
+
|
17
17
|
# Geometry string patterns
|
18
18
|
RESIZE_GEOMETRY = /^\d*x\d*[><%^!]?$|^\d+@$/ # e.g. '300x200!'
|
19
19
|
CROPPED_RESIZE_GEOMETRY = /^(\d+)x(\d+)#(\w{1,2})?$/ # e.g. '20x50#ne'
|
20
20
|
CROP_GEOMETRY = /^(\d+)x(\d+)([+-]\d+)?([+-]\d+)?(\w{1,2})?$/ # e.g. '30x30+10+10'
|
21
21
|
THUMB_GEOMETRY = Regexp.union RESIZE_GEOMETRY, CROPPED_RESIZE_GEOMETRY, CROP_GEOMETRY
|
22
|
-
|
22
|
+
|
23
23
|
include Configurable
|
24
24
|
include Utils
|
25
|
-
|
25
|
+
|
26
26
|
def resize(temp_object, geometry)
|
27
27
|
convert(temp_object, "-resize #{geometry}")
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
def auto_orient(temp_object)
|
31
31
|
convert(temp_object, "-auto-orient")
|
32
32
|
end
|
33
33
|
|
34
34
|
def crop(temp_object, opts={})
|
35
|
+
opts = Dragonfly::Utils.symbolize_keys(opts)
|
36
|
+
|
35
37
|
width = opts[:width]
|
36
38
|
height = opts[:height]
|
37
39
|
gravity = GRAVITIES[opts[:gravity]]
|
@@ -41,24 +43,26 @@ module Dragonfly
|
|
41
43
|
y = '+' + y unless y[/^[+-]/]
|
42
44
|
repage = opts[:repage] == false ? '' : '+repage'
|
43
45
|
resize = opts[:resize]
|
44
|
-
|
46
|
+
|
45
47
|
convert(temp_object, "#{"-resize #{resize} " if resize}#{"-gravity #{gravity} " if gravity}-crop #{width}x#{height}#{x}#{y} #{repage}")
|
46
48
|
end
|
47
|
-
|
49
|
+
|
48
50
|
def flip(temp_object)
|
49
51
|
convert(temp_object, "-flip")
|
50
52
|
end
|
51
|
-
|
53
|
+
|
52
54
|
def flop(temp_object)
|
53
55
|
convert(temp_object, "-flop")
|
54
56
|
end
|
55
|
-
|
57
|
+
|
56
58
|
def greyscale(temp_object)
|
57
59
|
convert(temp_object, "-colorspace Gray")
|
58
60
|
end
|
59
61
|
alias grayscale greyscale
|
60
|
-
|
62
|
+
|
61
63
|
def resize_and_crop(temp_object, opts={})
|
64
|
+
opts = Dragonfly::Utils.symbolize_keys(opts)
|
65
|
+
|
62
66
|
if !opts[:width] && !opts[:height]
|
63
67
|
return temp_object
|
64
68
|
elsif !opts[:width] || !opts[:height]
|
@@ -72,37 +76,39 @@ module Dragonfly
|
|
72
76
|
opts[:resize] = "#{opts[:width]}x#{opts[:height]}^^"
|
73
77
|
crop(temp_object, opts)
|
74
78
|
end
|
75
|
-
|
79
|
+
|
76
80
|
def rotate(temp_object, amount, opts={})
|
81
|
+
opts = Dragonfly::Utils.symbolize_keys(opts)
|
82
|
+
|
77
83
|
convert(temp_object, "-rotate #{amount}#{opts[:qualifier]}")
|
78
84
|
end
|
79
85
|
|
80
86
|
def strip(temp_object)
|
81
87
|
convert(temp_object, "-strip")
|
82
|
-
end
|
83
|
-
|
88
|
+
end
|
89
|
+
|
84
90
|
def thumb(temp_object, geometry)
|
85
91
|
case geometry
|
86
92
|
when RESIZE_GEOMETRY
|
87
93
|
resize(temp_object, geometry)
|
88
94
|
when CROPPED_RESIZE_GEOMETRY
|
89
|
-
resize_and_crop(temp_object,
|
95
|
+
resize_and_crop(temp_object, 'width' => $1, 'height' => $2, 'gravity' => $3)
|
90
96
|
when CROP_GEOMETRY
|
91
97
|
crop(temp_object,
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
98
|
+
'width' => $1,
|
99
|
+
'height' => $2,
|
100
|
+
'x' => $3,
|
101
|
+
'y' => $4,
|
102
|
+
'gravity' => $5
|
97
103
|
)
|
98
104
|
else raise ArgumentError, "Didn't recognise the geometry string #{geometry}"
|
99
105
|
end
|
100
106
|
end
|
101
|
-
|
107
|
+
|
102
108
|
def convert(temp_object, args='', format=nil)
|
103
109
|
format ? [super, {:format => format.to_sym}] : super
|
104
110
|
end
|
105
|
-
|
111
|
+
|
106
112
|
end
|
107
113
|
end
|
108
114
|
end
|
data/lib/dragonfly/job.rb
CHANGED
@@ -35,9 +35,9 @@ module Dragonfly
|
|
35
35
|
def step_name
|
36
36
|
@step_name ||= basename.gsub(/[A-Z]/){ "_#{$&.downcase}" }.sub('_','').to_sym
|
37
37
|
end
|
38
|
-
# Dragonfly::Job::Fetch ->
|
38
|
+
# Dragonfly::Job::Fetch -> 'f'
|
39
39
|
def abbreviation
|
40
|
-
@abbreviation ||= basename.scan(/[A-Z]/).join.downcase
|
40
|
+
@abbreviation ||= basename.scan(/[A-Z]/).join.downcase
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -69,7 +69,7 @@ module Dragonfly
|
|
69
69
|
|
70
70
|
class Process < Step
|
71
71
|
def name
|
72
|
-
args.first
|
72
|
+
args.first.to_sym
|
73
73
|
end
|
74
74
|
def arguments
|
75
75
|
args[1..-1]
|
@@ -86,7 +86,7 @@ module Dragonfly
|
|
86
86
|
job.url_attrs[:format] = format
|
87
87
|
end
|
88
88
|
def format
|
89
|
-
args.first
|
89
|
+
args.first.to_sym
|
90
90
|
end
|
91
91
|
def arguments
|
92
92
|
args[1..-1]
|
@@ -99,8 +99,14 @@ module Dragonfly
|
|
99
99
|
end
|
100
100
|
|
101
101
|
class Generate < Step
|
102
|
+
def name
|
103
|
+
args.first.to_sym
|
104
|
+
end
|
105
|
+
def arguments
|
106
|
+
args[1..-1]
|
107
|
+
end
|
102
108
|
def apply
|
103
|
-
content, meta = job.app.generator.generate(*
|
109
|
+
content, meta = job.app.generator.generate(name, *arguments)
|
104
110
|
job.update(content, meta)
|
105
111
|
end
|
106
112
|
end
|
@@ -166,7 +172,7 @@ module Dragonfly
|
|
166
172
|
end
|
167
173
|
|
168
174
|
def deserialize(string, app)
|
169
|
-
from_a(Serializer.
|
175
|
+
from_a(Serializer.json_decode(string), app)
|
170
176
|
end
|
171
177
|
|
172
178
|
def step_abbreviations
|
@@ -187,20 +193,20 @@ module Dragonfly
|
|
187
193
|
# If we had traits/classboxes in ruby maybe this wouldn't be needed
|
188
194
|
# Think of it as like a normal instance method but with a css-like !important after it
|
189
195
|
module OverrideInstanceMethods
|
190
|
-
|
196
|
+
|
191
197
|
def format
|
192
198
|
apply
|
193
199
|
meta[:format] || (ext.to_sym if ext && app.trust_file_extensions) || analyse(:format)
|
194
200
|
end
|
195
|
-
|
201
|
+
|
196
202
|
def mime_type
|
197
203
|
app.mime_type_for(format) || analyse(:mime_type) || app.fallback_mime_type
|
198
204
|
end
|
199
|
-
|
205
|
+
|
200
206
|
def to_s
|
201
207
|
super.sub(/#<Class:\w+>/, 'Extended Dragonfly::Job')
|
202
208
|
end
|
203
|
-
|
209
|
+
|
204
210
|
end
|
205
211
|
|
206
212
|
def initialize(app, content=nil, meta={}, url_attrs={})
|
@@ -280,7 +286,7 @@ module Dragonfly
|
|
280
286
|
end
|
281
287
|
|
282
288
|
def serialize
|
283
|
-
Serializer.
|
289
|
+
Serializer.json_encode(to_a)
|
284
290
|
end
|
285
291
|
|
286
292
|
def unique_signature
|
@@ -311,7 +317,7 @@ module Dragonfly
|
|
311
317
|
def url_attrs=(hash)
|
312
318
|
@url_attrs = UrlAttributes[hash]
|
313
319
|
end
|
314
|
-
|
320
|
+
|
315
321
|
attr_reader :url_attrs
|
316
322
|
|
317
323
|
def b64_data
|
@@ -380,7 +386,7 @@ module Dragonfly
|
|
380
386
|
def inspect
|
381
387
|
"<Dragonfly::Job app=#{app.name.inspect}, steps=#{steps.inspect}, temp_object=#{temp_object.inspect}, steps applied:#{applied_steps.length}/#{steps.length} >"
|
382
388
|
end
|
383
|
-
|
389
|
+
|
384
390
|
def update(content, new_meta)
|
385
391
|
if new_meta
|
386
392
|
new_meta.merge!(new_meta.delete(:meta)) if new_meta[:meta] # legacy data etc. may have nested meta hash - deprecate gracefully here
|
@@ -388,12 +394,12 @@ module Dragonfly
|
|
388
394
|
old_meta = temp_object ? temp_object.meta : {}
|
389
395
|
self.temp_object = TempObject.new(content, old_meta.merge(new_meta || {}))
|
390
396
|
end
|
391
|
-
|
397
|
+
|
392
398
|
def close
|
393
399
|
previous_temp_objects.each{|temp_object| temp_object.close }
|
394
400
|
temp_object.close if temp_object
|
395
401
|
end
|
396
|
-
|
402
|
+
|
397
403
|
protected
|
398
404
|
|
399
405
|
attr_writer :steps
|
@@ -405,20 +411,20 @@ module Dragonfly
|
|
405
411
|
apply
|
406
412
|
temp_object || raise(NoContent, "Job has not been initialized with content. Need to fetch first?")
|
407
413
|
end
|
408
|
-
|
414
|
+
|
409
415
|
def attributes_for_url
|
410
416
|
attrs = url_attrs.slice(*server.params_in_url)
|
411
417
|
attrs[:format] = (attrs[:format] || (url_attrs.ext if app.trust_file_extensions)).to_s if server.params_in_url.include?('format')
|
412
418
|
attrs.delete_if{|k, v| v.blank? }
|
413
419
|
attrs
|
414
420
|
end
|
415
|
-
|
421
|
+
|
416
422
|
attr_reader :previous_temp_objects
|
417
423
|
|
418
424
|
def last_step_of_type(type)
|
419
425
|
steps.select{|s| s.is_a?(type) }.last
|
420
426
|
end
|
421
|
-
|
427
|
+
|
422
428
|
def opts_for_store
|
423
429
|
{:mime_type => mime_type}
|
424
430
|
end
|
@@ -9,7 +9,7 @@ module Dragonfly
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def call(env)
|
12
|
-
params =
|
12
|
+
params = Utils.symbolize_keys Rack::Request.new(env).params
|
13
13
|
job = @block.call(params.merge(routing_params(env)), @app)
|
14
14
|
Response.new(job, env).to_response
|
15
15
|
rescue Job::NoSHAGiven => e
|
@@ -33,12 +33,5 @@ module Dragonfly
|
|
33
33
|
raise(NoRoutingParams, "couldn't find any routing parameters in env #{env.inspect}")
|
34
34
|
end
|
35
35
|
|
36
|
-
def symbolize_keys_of(hash)
|
37
|
-
hash.inject({}) do |h, (key, value)|
|
38
|
-
h[(key.to_sym rescue key) || key] = value
|
39
|
-
h
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
36
|
end
|
44
37
|
end
|
data/lib/dragonfly/serializer.rb
CHANGED
@@ -1,32 +1,43 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'base64'
|
3
|
+
require 'multi_json'
|
3
4
|
|
4
5
|
module Dragonfly
|
5
6
|
module Serializer
|
6
|
-
|
7
|
+
|
7
8
|
# Exceptions
|
8
9
|
class BadString < RuntimeError; end
|
9
|
-
|
10
|
+
|
10
11
|
extend self # So we can do Serializer.b64_encode, etc.
|
11
|
-
|
12
|
+
|
12
13
|
def b64_encode(string)
|
13
|
-
Base64.encode64(string).tr("\n=",'')
|
14
|
+
Base64.encode64(string).tr("\n=",'')
|
14
15
|
end
|
15
|
-
|
16
|
+
|
16
17
|
def b64_decode(string)
|
17
18
|
padding_length = string.length % 4
|
18
|
-
Base64.decode64(string
|
19
|
+
Base64.decode64(string + '=' * padding_length)
|
19
20
|
end
|
20
|
-
|
21
|
+
|
21
22
|
def marshal_encode(object)
|
22
23
|
b64_encode(Marshal.dump(object))
|
23
24
|
end
|
24
|
-
|
25
|
+
|
25
26
|
def marshal_decode(string)
|
26
27
|
Marshal.load(b64_decode(string))
|
27
28
|
rescue TypeError, ArgumentError => e
|
28
29
|
raise BadString, "couldn't decode #{string} - got #{e}"
|
29
30
|
end
|
30
|
-
|
31
|
+
|
32
|
+
def json_encode(object)
|
33
|
+
b64_encode(MultiJson.encode(object))
|
34
|
+
end
|
35
|
+
|
36
|
+
def json_decode(string, opts={})
|
37
|
+
MultiJson.decode(b64_decode(string), :symbolize_keys => opts[:symbolize_keys])
|
38
|
+
rescue MultiJson::DecodeError => e
|
39
|
+
raise BadString, "couldn't decode #{string} - got #{e}"
|
40
|
+
end
|
41
|
+
|
31
42
|
end
|
32
43
|
end
|
data/lib/dragonfly/server.rb
CHANGED
@@ -6,7 +6,7 @@ module Dragonfly
|
|
6
6
|
|
7
7
|
include Loggable
|
8
8
|
include Configurable
|
9
|
-
|
9
|
+
|
10
10
|
configurable_attr :allow_fetch_file, false
|
11
11
|
configurable_attr :allow_fetch_url, false
|
12
12
|
configurable_attr :dragonfly_url, '/dragonfly'
|
@@ -16,18 +16,18 @@ module Dragonfly
|
|
16
16
|
|
17
17
|
extend Forwardable
|
18
18
|
def_delegator :url_mapper, :params_in_url
|
19
|
-
|
19
|
+
|
20
20
|
def initialize(app)
|
21
21
|
@app = app
|
22
22
|
use_same_log_as(app)
|
23
23
|
use_as_fallback_config(app)
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def before_serve(&block)
|
27
27
|
self.before_serve_callback = block
|
28
28
|
end
|
29
29
|
configuration_method :before_serve
|
30
|
-
|
30
|
+
|
31
31
|
def call(env)
|
32
32
|
if dragonfly_url == env["PATH_INFO"]
|
33
33
|
dragonfly_response
|
@@ -68,13 +68,12 @@ module Dragonfly
|
|
68
68
|
end
|
69
69
|
|
70
70
|
private
|
71
|
-
|
71
|
+
|
72
72
|
attr_reader :app
|
73
73
|
attr_accessor :before_serve_callback
|
74
74
|
|
75
75
|
def url_mapper
|
76
76
|
@url_mapper ||= UrlMapper.new(url_format,
|
77
|
-
:job => '[\w+~]',
|
78
77
|
:basename => '[^\/]',
|
79
78
|
:name => '[^\/]',
|
80
79
|
:format => '[^\.]'
|