dragonfly 0.9.13 → 0.9.14
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/History.md +12 -0
- data/README.md +1 -1
- data/VERSION +1 -1
- data/dragonfly.gemspec +3 -2
- data/extra_docs/Rails3.md +1 -1
- data/lib/dragonfly/active_model_extensions/attachment.rb +28 -20
- data/lib/dragonfly/job.rb +8 -3
- data/lib/dragonfly/serializer.rb +5 -1
- data/lib/dragonfly/server.rb +1 -1
- data/lib/dragonfly/url_mapper.rb +2 -2
- data/lib/dragonfly/utils.rb +9 -0
- data/spec/dragonfly/active_model_extensions/model_spec.rb +12 -0
- data/spec/dragonfly/job_spec.rb +13 -0
- data/spec/dragonfly/serializer_spec.rb +37 -14
- data/spec/dragonfly/server_spec.rb +10 -0
- data/spec/dragonfly/url_mapper_spec.rb +21 -24
- data/spec/functional/urls_spec.rb +41 -0
- metadata +4 -3
data/History.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
0.9.14 (2013-02-13)
|
2
|
+
===================
|
3
|
+
Features
|
4
|
+
--------
|
5
|
+
- Attachment#b64_data
|
6
|
+
|
7
|
+
Fixes
|
8
|
+
-----
|
9
|
+
- Fix '+' character being converted to ' ' (revert to URI.escape instead of Rack::Utils.escape)
|
10
|
+
- Support old-style deprecated urls (with a check for malicious ones)
|
11
|
+
- Handle case where uid is an empty string
|
12
|
+
|
1
13
|
0.9.13 (2013-01-30)
|
2
14
|
===================
|
3
15
|
Changes
|
data/README.md
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
1
|
+
0.9.14
|
data/dragonfly.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "dragonfly"
|
8
|
-
s.version = "0.9.
|
8
|
+
s.version = "0.9.14"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Mark Evans"]
|
12
|
-
s.date = "2013-
|
12
|
+
s.date = "2013-02-13"
|
13
13
|
s.description = "Dragonfly is a framework that enables on-the-fly processing for any content type.\n It is especially suited to image handling. Its uses range from image thumbnails to standard attachments to on-demand text generation."
|
14
14
|
s.email = "mark@new-bamboo.co.uk"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -178,6 +178,7 @@ Gem::Specification.new do |s|
|
|
178
178
|
"spec/functional/remote_on_the_fly_spec.rb",
|
179
179
|
"spec/functional/shell_commands_spec.rb",
|
180
180
|
"spec/functional/to_response_spec.rb",
|
181
|
+
"spec/functional/urls_spec.rb",
|
181
182
|
"spec/spec_helper.rb",
|
182
183
|
"spec/support/argument_matchers.rb",
|
183
184
|
"spec/support/image_matchers.rb",
|
data/extra_docs/Rails3.md
CHANGED
@@ -11,7 +11,7 @@ module Dragonfly
|
|
11
11
|
|
12
12
|
extend Forwardable
|
13
13
|
def_delegators :job,
|
14
|
-
:data, :to_file, :file, :tempfile, :path,
|
14
|
+
:data, :b64_data, :to_file, :file, :tempfile, :path,
|
15
15
|
:process, :encode, :analyse,
|
16
16
|
:meta, :meta=,
|
17
17
|
:name, :size,
|
@@ -20,11 +20,11 @@ module Dragonfly
|
|
20
20
|
include HasFilename
|
21
21
|
|
22
22
|
alias_method :length, :size
|
23
|
-
|
23
|
+
|
24
24
|
def initialize(model)
|
25
25
|
@model = model
|
26
26
|
self.uid = model_uid
|
27
|
-
set_job_from_uid if uid
|
27
|
+
set_job_from_uid if uid?
|
28
28
|
@should_run_callbacks = true
|
29
29
|
self.class.ensure_uses_cached_magic_attributes
|
30
30
|
end
|
@@ -32,7 +32,7 @@ module Dragonfly
|
|
32
32
|
def app
|
33
33
|
self.class.app
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
def attribute
|
37
37
|
self.class.attribute
|
38
38
|
end
|
@@ -66,7 +66,7 @@ module Dragonfly
|
|
66
66
|
|
67
67
|
def destroy!
|
68
68
|
destroy_previous!
|
69
|
-
destroy_content(uid) if uid
|
69
|
+
destroy_content(uid) if uid?
|
70
70
|
end
|
71
71
|
|
72
72
|
def save!
|
@@ -95,18 +95,18 @@ module Dragonfly
|
|
95
95
|
assign(encode(*args))
|
96
96
|
self
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
def remote_url(opts={})
|
100
|
-
app.remote_url_for(uid, opts) if uid
|
100
|
+
app.remote_url_for(uid, opts) if uid?
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
def apply
|
104
104
|
job.apply
|
105
105
|
self
|
106
106
|
end
|
107
107
|
|
108
108
|
attr_writer :should_run_callbacks
|
109
|
-
|
109
|
+
|
110
110
|
def should_run_callbacks?
|
111
111
|
!!@should_run_callbacks
|
112
112
|
end
|
@@ -121,26 +121,26 @@ module Dragonfly
|
|
121
121
|
end
|
122
122
|
|
123
123
|
attr_writer :should_retain
|
124
|
-
|
124
|
+
|
125
125
|
def should_retain?
|
126
126
|
!!@should_retain
|
127
127
|
end
|
128
|
-
|
128
|
+
|
129
129
|
def retained?
|
130
130
|
!!@retained
|
131
131
|
end
|
132
|
-
|
132
|
+
|
133
133
|
def destroy_retained!
|
134
134
|
destroy_content(retained_attrs[:uid])
|
135
135
|
end
|
136
|
-
|
136
|
+
|
137
137
|
def retained_attrs
|
138
138
|
attribute_keys.inject({}) do |hash, key|
|
139
139
|
hash[key] = send(key)
|
140
140
|
hash
|
141
141
|
end if retained?
|
142
142
|
end
|
143
|
-
|
143
|
+
|
144
144
|
def retained_attrs=(attrs)
|
145
145
|
if changed? # if already set, ignore and destroy this retained content
|
146
146
|
destroy_content(attrs[:uid])
|
@@ -156,7 +156,7 @@ module Dragonfly
|
|
156
156
|
self.retained = true
|
157
157
|
end
|
158
158
|
end
|
159
|
-
|
159
|
+
|
160
160
|
def inspect
|
161
161
|
"<Dragonfly Attachment uid=#{uid.inspect}, app=#{app.name.inspect}>"
|
162
162
|
end
|
@@ -187,7 +187,7 @@ module Dragonfly
|
|
187
187
|
end
|
188
188
|
|
189
189
|
def destroy_previous!
|
190
|
-
if previous_uid
|
190
|
+
if previous_uid?
|
191
191
|
destroy_content(previous_uid)
|
192
192
|
self.previous_uid = nil
|
193
193
|
end
|
@@ -217,7 +217,7 @@ module Dragonfly
|
|
217
217
|
meth = "#{attribute}_uid_will_change!"
|
218
218
|
model.send(meth) if model.respond_to?(meth)
|
219
219
|
end
|
220
|
-
|
220
|
+
|
221
221
|
attr_reader :model, :uid
|
222
222
|
attr_writer :job
|
223
223
|
attr_accessor :previous_uid
|
@@ -227,6 +227,14 @@ module Dragonfly
|
|
227
227
|
@uid = uid
|
228
228
|
end
|
229
229
|
|
230
|
+
def uid?
|
231
|
+
!uid.nil? && !uid.empty?
|
232
|
+
end
|
233
|
+
|
234
|
+
def previous_uid?
|
235
|
+
!previous_uid.nil? && !previous_uid.empty?
|
236
|
+
end
|
237
|
+
|
230
238
|
def magic_attributes
|
231
239
|
self.class.magic_attributes
|
232
240
|
end
|
@@ -264,11 +272,11 @@ module Dragonfly
|
|
264
272
|
:model_attachment => attribute
|
265
273
|
}
|
266
274
|
end
|
267
|
-
|
275
|
+
|
268
276
|
def all_extra_attributes
|
269
277
|
magic_attributes_hash.merge(extra_attributes)
|
270
278
|
end
|
271
|
-
|
279
|
+
|
272
280
|
def set_job_from_uid
|
273
281
|
self.job = app.fetch(uid)
|
274
282
|
job.url_attrs = all_extra_attributes
|
@@ -276,4 +284,4 @@ module Dragonfly
|
|
276
284
|
|
277
285
|
end
|
278
286
|
end
|
279
|
-
end
|
287
|
+
end
|
data/lib/dragonfly/job.rb
CHANGED
@@ -160,19 +160,24 @@ module Dragonfly
|
|
160
160
|
|
161
161
|
def from_a(steps_array, app)
|
162
162
|
unless steps_array.is_a?(Array) &&
|
163
|
-
steps_array.all?{|s| s.is_a?(Array) && step_abbreviations[s.first] }
|
163
|
+
steps_array.all?{|s| s.is_a?(Array) && step_abbreviations[s.first.to_s] }
|
164
164
|
raise InvalidArray, "can't define a job from #{steps_array.inspect}"
|
165
165
|
end
|
166
166
|
job = app.new_job
|
167
167
|
steps_array.each do |step_array|
|
168
|
-
step_class = step_abbreviations[step_array.shift]
|
168
|
+
step_class = step_abbreviations[step_array.shift.to_s]
|
169
169
|
job.steps << step_class.new(job, *step_array)
|
170
170
|
end
|
171
171
|
job
|
172
172
|
end
|
173
173
|
|
174
174
|
def deserialize(string, app)
|
175
|
-
|
175
|
+
array = begin
|
176
|
+
Serializer.json_decode(string)
|
177
|
+
rescue Serializer::BadString
|
178
|
+
Serializer.marshal_decode(string) # legacy strings
|
179
|
+
end
|
180
|
+
from_a(array, app)
|
176
181
|
end
|
177
182
|
|
178
183
|
def step_abbreviations
|
data/lib/dragonfly/serializer.rb
CHANGED
@@ -7,6 +7,7 @@ module Dragonfly
|
|
7
7
|
|
8
8
|
# Exceptions
|
9
9
|
class BadString < RuntimeError; end
|
10
|
+
class MaliciousString < RuntimeError; end
|
10
11
|
|
11
12
|
extend self # So we can do Serializer.b64_encode, etc.
|
12
13
|
|
@@ -16,6 +17,7 @@ module Dragonfly
|
|
16
17
|
|
17
18
|
def b64_decode(string)
|
18
19
|
padding_length = string.length % 4
|
20
|
+
string = string.tr('~', '/')
|
19
21
|
Base64.decode64(string + '=' * padding_length)
|
20
22
|
end
|
21
23
|
|
@@ -24,7 +26,9 @@ module Dragonfly
|
|
24
26
|
end
|
25
27
|
|
26
28
|
def marshal_decode(string)
|
27
|
-
|
29
|
+
marshal_string = b64_decode(string)
|
30
|
+
raise MaliciousString, "potentially malicious marshal string #{marshal_string.inspect}" if marshal_string[/@[a-z_]/i]
|
31
|
+
Marshal.load(marshal_string)
|
28
32
|
rescue TypeError, ArgumentError => e
|
29
33
|
raise BadString, "couldn't decode #{string} - got #{e}"
|
30
34
|
end
|
data/lib/dragonfly/server.rb
CHANGED
@@ -52,7 +52,7 @@ module Dragonfly
|
|
52
52
|
rescue JobNotAllowed => e
|
53
53
|
log.warn(e.message)
|
54
54
|
[403, {"Content-Type" => 'text/plain'}, ["Forbidden"]]
|
55
|
-
rescue Serializer::BadString, Job::InvalidArray => e
|
55
|
+
rescue Serializer::BadString, Serializer::MaliciousString, Job::InvalidArray => e
|
56
56
|
log.warn(e.message)
|
57
57
|
[404, {'Content-Type' => 'text/plain'}, ['Not found']]
|
58
58
|
end
|
data/lib/dragonfly/url_mapper.rb
CHANGED
@@ -24,7 +24,7 @@ module Dragonfly
|
|
24
24
|
params = Rack::Utils.parse_query(query)
|
25
25
|
params_in_url.each_with_index do |var, i|
|
26
26
|
value = md[i+1][1..-1] if md[i+1]
|
27
|
-
params[var] = value &&
|
27
|
+
params[var] = value && Utils.uri_unescape(value)
|
28
28
|
end
|
29
29
|
params
|
30
30
|
end
|
@@ -39,7 +39,7 @@ module Dragonfly
|
|
39
39
|
url = url_format.dup
|
40
40
|
segments.each do |seg|
|
41
41
|
value = params[seg.param]
|
42
|
-
value ? url.sub!(/:[\w_]+/,
|
42
|
+
value ? url.sub!(/:[\w_]+/, Utils.uri_escape_segment(value.to_s)) : url.sub!(/.:[\w_]+/, '')
|
43
43
|
params.delete(seg.param)
|
44
44
|
end
|
45
45
|
url << "?#{Rack::Utils.build_query(params)}" if params.any?
|
data/lib/dragonfly/utils.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'tempfile'
|
2
|
+
require 'uri'
|
2
3
|
|
3
4
|
module Dragonfly
|
4
5
|
module Utils
|
@@ -20,5 +21,13 @@ module Dragonfly
|
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
24
|
+
def uri_escape_segment(string)
|
25
|
+
URI.escape(string).sub('/', '%2F')
|
26
|
+
end
|
27
|
+
|
28
|
+
def uri_unescape(string)
|
29
|
+
URI.unescape(string)
|
30
|
+
end
|
31
|
+
|
23
32
|
end
|
24
33
|
end
|
@@ -124,6 +124,18 @@ describe Item do
|
|
124
124
|
end
|
125
125
|
end
|
126
126
|
|
127
|
+
describe "after a record with an empty uid is saved" do
|
128
|
+
before(:each) do
|
129
|
+
@item.preview_image_uid = ''
|
130
|
+
@item.save!
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should not try to destroy anything on destroy" do
|
134
|
+
@app.datastore.should_not_receive(:destroy)
|
135
|
+
@item.destroy
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
127
139
|
describe "when the uid is set manually" do
|
128
140
|
before(:each) do
|
129
141
|
@item.preview_image_uid = 'some_known_uid'
|
data/spec/dragonfly/job_spec.rb
CHANGED
@@ -499,6 +499,11 @@ describe Dragonfly::Job do
|
|
499
499
|
end
|
500
500
|
end
|
501
501
|
|
502
|
+
it "works with symbols" do
|
503
|
+
job = Dragonfly::Job.from_a([[:f, 'some_uid']], @app)
|
504
|
+
job.steps.should match_steps([Dragonfly::Job::Fetch])
|
505
|
+
end
|
506
|
+
|
502
507
|
[
|
503
508
|
'f',
|
504
509
|
['f'],
|
@@ -539,6 +544,14 @@ describe Dragonfly::Job do
|
|
539
544
|
process_step.name.should == :resize_and_crop
|
540
545
|
process_step.arguments.should == [{'width' => 270, 'height' => 92, 'gravity' => 'n'}]
|
541
546
|
end
|
547
|
+
it "works with json encoded strings" do
|
548
|
+
job = Dragonfly::Job.deserialize("W1siZiIsInNvbWVfdWlkIl1d", @app)
|
549
|
+
job.fetch_step.uid.should == 'some_uid'
|
550
|
+
end
|
551
|
+
it "works with marshal encoded strings (deprecated)" do
|
552
|
+
job = Dragonfly::Job.deserialize("BAhbBlsHSSIGZgY6BkVUSSINc29tZV91aWQGOwBU", @app)
|
553
|
+
job.fetch_step.uid.should == 'some_uid'
|
554
|
+
end
|
542
555
|
end
|
543
556
|
|
544
557
|
describe "to_app" do
|
@@ -5,22 +5,32 @@ describe Dragonfly::Serializer do
|
|
5
5
|
|
6
6
|
include Dragonfly::Serializer
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
8
|
+
describe "base 64 encoding/decoding" do
|
9
|
+
[
|
10
|
+
'a',
|
11
|
+
'sdhflasd',
|
12
|
+
'/2010/03/01/hello.png',
|
13
|
+
'//..',
|
14
|
+
'whats/up.egg.frog',
|
15
|
+
'£ñçùí;',
|
16
|
+
'~'
|
17
|
+
].each do |string|
|
18
|
+
it "should encode #{string.inspect} properly with no padding/line break" do
|
19
|
+
b64_encode(string).should_not =~ /\n|=/
|
20
|
+
end
|
21
|
+
it "should correctly encode and decode #{string.inspect} to the same string" do
|
22
|
+
str = b64_decode(b64_encode(string))
|
23
|
+
str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
|
24
|
+
str.should == string
|
25
|
+
end
|
18
26
|
end
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
27
|
+
|
28
|
+
describe "b64_decode" do
|
29
|
+
it "converts (deprecated) '~' characters to '/' characters" do
|
30
|
+
b64_decode('asdf~asdf').should == b64_decode('asdf/asdf')
|
31
|
+
end
|
23
32
|
end
|
33
|
+
|
24
34
|
end
|
25
35
|
|
26
36
|
[
|
@@ -55,6 +65,19 @@ describe Dragonfly::Serializer do
|
|
55
65
|
marshal_decode('ahasdkjfhasdkfjh')
|
56
66
|
}.should raise_error(Dragonfly::Serializer::BadString)
|
57
67
|
end
|
68
|
+
describe "potentially harmful strings" do
|
69
|
+
['_', 'hello', 'h2', '__send__', 'F'].each do |variable_name|
|
70
|
+
it "should raise an error if the string passed in is potentially harmful (e.g. contains instance variable #{variable_name})" do
|
71
|
+
class C; end
|
72
|
+
c = C.new
|
73
|
+
c.instance_eval{ instance_variable_set("@#{variable_name}", 1) }
|
74
|
+
string = Dragonfly::Serializer.b64_encode(Marshal.dump(c))
|
75
|
+
lambda{
|
76
|
+
marshal_decode(string)
|
77
|
+
}.should raise_error(Dragonfly::Serializer::MaliciousString)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
58
81
|
end
|
59
82
|
|
60
83
|
[
|
@@ -121,6 +121,16 @@ describe Dragonfly::Server do
|
|
121
121
|
response.headers['X-Cascade'].should be_nil
|
122
122
|
end
|
123
123
|
|
124
|
+
it "should return a 404 when the url is malicious" do
|
125
|
+
class C; def initialize; @a = 1; end; end
|
126
|
+
url = "/media/#{Dragonfly::Serializer.marshal_encode(C.new)}"
|
127
|
+
response = request(@server, url)
|
128
|
+
response.status.should == 404
|
129
|
+
response.body.should == 'Not found'
|
130
|
+
response.content_type.should == 'text/plain'
|
131
|
+
response.headers['X-Cascade'].should be_nil
|
132
|
+
end
|
133
|
+
|
124
134
|
it "should return a 403 Forbidden when someone uses fetch_file" do
|
125
135
|
response = request(@server, "/media/#{@app.fetch_file('/some/file.txt').serialize}")
|
126
136
|
response.status.should == 403
|
@@ -20,13 +20,13 @@ describe Dragonfly::UrlMapper do
|
|
20
20
|
url_mapper.params_in_url.should == ['job', 'basename', 'ext']
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
describe "url_regexp" do
|
25
25
|
it "should return a regexp with non-greedy optional groups that include the preceding slash/dot/dash" do
|
26
26
|
url_mapper = Dragonfly::UrlMapper.new('/media/:job/:basename-:size.:format')
|
27
27
|
url_mapper.url_regexp.should == %r{^/media(/[^\/\-\.]+?)?(/[^\/\-\.]+?)?(\-[^\/\-\.]+?)?(\.[^\/\-\.]+?)?$}
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
it "should allow setting custom patterns in the url" do
|
31
31
|
url_mapper = Dragonfly::UrlMapper.new('/media/:job-:size.:format',
|
32
32
|
:job => '\w',
|
@@ -35,7 +35,7 @@ describe Dragonfly::UrlMapper do
|
|
35
35
|
)
|
36
36
|
url_mapper.url_regexp.should == %r{^/media(/\w+?)?(\-\d+?)?(\.[^\.]+?)?$}
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
it "should make optional match patterns (ending in ?) apply to the whole group including the preceding seperator" do
|
40
40
|
url_mapper = Dragonfly::UrlMapper.new('/media/:job', :job => '\w')
|
41
41
|
url_mapper.url_regexp.should == %r{^/media(/\w+?)?$}
|
@@ -44,28 +44,25 @@ describe Dragonfly::UrlMapper do
|
|
44
44
|
|
45
45
|
describe "url_for" do
|
46
46
|
before(:each) do
|
47
|
-
@url_mapper = Dragonfly::UrlMapper.new('/media/:job-:size'
|
48
|
-
:job => '\w',
|
49
|
-
:size => '\w'
|
50
|
-
)
|
47
|
+
@url_mapper = Dragonfly::UrlMapper.new('/media/:job-:size')
|
51
48
|
end
|
52
|
-
|
49
|
+
|
53
50
|
it "should map correctly" do
|
54
51
|
@url_mapper.url_for('job' => 'asdf', 'size' => '30x30').should == '/media/asdf-30x30'
|
55
52
|
end
|
56
|
-
|
53
|
+
|
57
54
|
it "should add extra params as query parameters" do
|
58
55
|
@url_mapper.url_for('job' => 'asdf', 'size' => '30x30', 'when' => 'now').should == '/media/asdf-30x30?when=now'
|
59
56
|
end
|
60
|
-
|
57
|
+
|
61
58
|
it "should not worry if params aren't given" do
|
62
59
|
@url_mapper.url_for('job' => 'asdf', 'when' => 'now', 'then' => 'soon').should match_url '/media/asdf?when=now&then=soon'
|
63
60
|
end
|
64
|
-
|
61
|
+
|
65
62
|
it "should call to_s on non-string values" do
|
66
63
|
@url_mapper.url_for('job' => 'asdf', 'size' => 500).should == '/media/asdf-500'
|
67
64
|
end
|
68
|
-
|
65
|
+
|
69
66
|
it "should url-escape funny characters in the path" do
|
70
67
|
@url_mapper.url_for('job' => 'a#c').should == '/media/a%23c'
|
71
68
|
end
|
@@ -84,20 +81,20 @@ describe Dragonfly::UrlMapper do
|
|
84
81
|
before(:each) do
|
85
82
|
@url_mapper = Dragonfly::UrlMapper.new('/media/:job')
|
86
83
|
end
|
87
|
-
|
84
|
+
|
88
85
|
it "should map correctly" do
|
89
86
|
@url_mapper.params_for('/media/asdf').should == {'job' => 'asdf'}
|
90
87
|
end
|
91
|
-
|
88
|
+
|
92
89
|
it "should include query parameters" do
|
93
90
|
@url_mapper.params_for('/media/asdf', 'when=now').should == {'job' => 'asdf', 'when' => 'now'}
|
94
91
|
end
|
95
|
-
|
92
|
+
|
96
93
|
it "should generally be ok with wierd characters" do
|
97
94
|
@url_mapper = Dragonfly::UrlMapper.new('/media/:doobie')
|
98
95
|
@url_mapper.params_for('/media/sd sdf jl£@$ sdf:_', 'job=goodun').should == {'job' => 'goodun', 'doobie' => 'sd sdf jl£@$ sdf:_'}
|
99
96
|
end
|
100
|
-
|
97
|
+
|
101
98
|
it "should correctly url-unescape funny characters" do
|
102
99
|
@url_mapper.params_for('/media/a%23c').should == {'job' => 'a#c'}
|
103
100
|
end
|
@@ -106,7 +103,6 @@ describe Dragonfly::UrlMapper do
|
|
106
103
|
describe "matching urls with standard format /media/:job/:basename.:format" do
|
107
104
|
before(:each) do
|
108
105
|
@url_mapper = Dragonfly::UrlMapper.new('/media/:job/:basename.:format',
|
109
|
-
:job => '\w',
|
110
106
|
:basename => '[^\/]',
|
111
107
|
:format => '[^\.]'
|
112
108
|
)
|
@@ -126,22 +122,23 @@ describe Dragonfly::UrlMapper do
|
|
126
122
|
'/media/asdf.egg' => {'job' => 'asdf', 'basename' => nil, 'format' => 'egg'},
|
127
123
|
'/media/asdf/stuff/egg' => nil,
|
128
124
|
'/media/asdf/stuff.dog.egg' => {'job' => 'asdf', 'basename' => 'stuff.dog', 'format' => 'egg'},
|
129
|
-
'/media/asdf/s
|
130
|
-
'/media/asdf-40x40/stuff.egg' => nil
|
125
|
+
'/media/asdf/s=2+-.d.e' => {'job' => 'asdf', 'basename' => 's=2+-.d', 'format' => 'e'},
|
126
|
+
'/media/asdf-40x40/stuff.egg' => nil,
|
127
|
+
'/media/a%23c' => {'job' => 'a#c', 'basename' => nil, 'format' => nil}
|
131
128
|
}.each do |path, params|
|
132
|
-
|
129
|
+
|
133
130
|
it "should turn the url #{path} into params #{params.inspect}" do
|
134
131
|
@url_mapper.params_for(path).should == params
|
135
|
-
end
|
136
|
-
|
132
|
+
end
|
133
|
+
|
137
134
|
if params
|
138
135
|
it "should turn the params #{params.inspect} into url #{path}" do
|
139
136
|
@url_mapper.url_for(params).should == path
|
140
137
|
end
|
141
138
|
end
|
142
|
-
|
139
|
+
|
143
140
|
end
|
144
141
|
|
145
142
|
end
|
146
|
-
|
143
|
+
|
147
144
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "urls" do
|
4
|
+
|
5
|
+
def request(app, path)
|
6
|
+
Rack::MockRequest.new(app).get(path)
|
7
|
+
end
|
8
|
+
|
9
|
+
def job_should_match(array)
|
10
|
+
Dragonfly::Response.should_receive(:new).with do |job, env|
|
11
|
+
job.to_a.should == array
|
12
|
+
end.and_return(mock('response', :to_response => [200, {'Content-Type' => 'text/plain'}, ["OK"]]))
|
13
|
+
end
|
14
|
+
|
15
|
+
let (:app) { test_app }
|
16
|
+
|
17
|
+
it "works with old marshalled urls (including with tildes in them)" do
|
18
|
+
url = "/BAhbBlsHOgZmSSIIPD4~BjoGRVQ"
|
19
|
+
job_should_match [["f", "<>?"]]
|
20
|
+
response = request(app, url)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "blows up if it detects bad objects" do
|
24
|
+
url = "/BAhvOgZDBjoLQHRoaW5nSSIId2VlBjoGRVQ"
|
25
|
+
Dragonfly::Response.should_not_receive(:new)
|
26
|
+
response = request(app, url)
|
27
|
+
response.status.should == 404
|
28
|
+
end
|
29
|
+
|
30
|
+
it "works with the '%2B' character" do
|
31
|
+
url = "/W1siZiIsIjIwMTIvMTEvMDMvMTdfMzhfMDhfNTc4X19NR181ODk5Xy5qcGciXSxbInAiLCJ0aHVtYiIsIjQ1MHg0NTA%2BIl1d/_MG_5899+.jpg"
|
32
|
+
job_should_match [["f", "2012/11/03/17_38_08_578__MG_5899_.jpg"], ["p", "thumb", "450x450>"]]
|
33
|
+
response = request(app, url)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "works when '%2B' has been converted to + (e.g. with nginx)" do
|
37
|
+
url = "/W1siZiIsIjIwMTIvMTEvMDMvMTdfMzhfMDhfNTc4X19NR181ODk5Xy5qcGciXSxbInAiLCJ0aHVtYiIsIjQ1MHg0NTA+Il1d/_MG_5899+.jpg"
|
38
|
+
job_should_match [["f", "2012/11/03/17_38_08_578__MG_5899_.jpg"], ["p", "thumb", "450x450>"]]
|
39
|
+
response = request(app, url)
|
40
|
+
end
|
41
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dragonfly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.14
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-02-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -502,6 +502,7 @@ files:
|
|
502
502
|
- spec/functional/remote_on_the_fly_spec.rb
|
503
503
|
- spec/functional/shell_commands_spec.rb
|
504
504
|
- spec/functional/to_response_spec.rb
|
505
|
+
- spec/functional/urls_spec.rb
|
505
506
|
- spec/spec_helper.rb
|
506
507
|
- spec/support/argument_matchers.rb
|
507
508
|
- spec/support/image_matchers.rb
|
@@ -529,7 +530,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
529
530
|
version: '0'
|
530
531
|
segments:
|
531
532
|
- 0
|
532
|
-
hash: -
|
533
|
+
hash: -1610166145286138499
|
533
534
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
534
535
|
none: false
|
535
536
|
requirements:
|