dragonfly 1.0 → 1.0.1
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -4
- data/History.md +17 -0
- data/README.md +6 -2
- data/Rakefile +1 -0
- data/dragonfly.gemspec +17 -1
- data/lib/dragonfly/app.rb +10 -1
- data/lib/dragonfly/file_data_store.rb +1 -1
- data/lib/dragonfly/image_magick/plugin.rb +3 -2
- data/lib/dragonfly/job.rb +5 -178
- data/lib/dragonfly/job/fetch.rb +19 -0
- data/lib/dragonfly/job/fetch_file.rb +27 -0
- data/lib/dragonfly/job/fetch_url.rb +75 -0
- data/lib/dragonfly/job/generate.rb +27 -0
- data/lib/dragonfly/job/process.rb +27 -0
- data/lib/dragonfly/job/step.rb +44 -0
- data/lib/dragonfly/model/attachment.rb +0 -2
- data/lib/dragonfly/model/class_methods.rb +5 -0
- data/lib/dragonfly/rails/images.rb +1 -0
- data/lib/dragonfly/response.rb +11 -2
- data/lib/dragonfly/routed_endpoint.rb +15 -5
- data/lib/dragonfly/serializer.rb +2 -2
- data/lib/dragonfly/shell.rb +28 -5
- data/lib/dragonfly/version.rb +1 -1
- data/spec/dragonfly/file_data_store_spec.rb +29 -32
- data/spec/dragonfly/job/fetch_file_spec.rb +26 -0
- data/spec/dragonfly/job/fetch_spec.rb +26 -0
- data/spec/dragonfly/job/fetch_url_spec.rb +123 -0
- data/spec/dragonfly/job/generate_spec.rb +28 -0
- data/spec/dragonfly/job/process_spec.rb +28 -0
- data/spec/dragonfly/job_endpoint_spec.rb +17 -0
- data/spec/dragonfly/job_spec.rb +0 -187
- data/spec/dragonfly/model/model_spec.rb +27 -12
- data/spec/dragonfly/routed_endpoint_spec.rb +69 -28
- data/spec/dragonfly/shell_spec.rb +2 -2
- data/spec/support/simple_matchers.rb +1 -1
- metadata +43 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f679f5471b494a363b6ad8b63abe134ab56169e0
|
4
|
+
data.tar.gz: 16e5bddb9fabf32fe9f494025aa1d9ec052f9421
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce1fb089fa71ad9f0ac5ba3a2841de04510c1a9241a99a404be6886a62a1574a594c3fb07efe629e66528d8fdb92b973e67f1f6db4328506cebd6fbec108ba8b
|
7
|
+
data.tar.gz: e53fb3a2d1da7c4cd64ab58ca10f4fa39f3167da1a10c7273f36e8ba65e3b142c2df8e29095d06cef1d54d4de2e95be83e9f6f86d52ad1dd5ebbc91e274b85ba
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/History.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
1.0.1 (2013-11-28)
|
2
|
+
===================
|
3
|
+
Changes
|
4
|
+
-------
|
5
|
+
- FileDataStore doesn't use hours_minutes_seconds in its path - it uses a random string instead (12_15_59_saf4fs_file.png -> sdf4c2G_file.png)
|
6
|
+
|
7
|
+
Features
|
8
|
+
--------
|
9
|
+
- model attribute `xxx_changed?` method (useful e.g. in validations)
|
10
|
+
|
11
|
+
Fixes
|
12
|
+
-----
|
13
|
+
- proper support for Ruby 1.8.7 and JRuby (version 1.7.8)
|
14
|
+
- routed endpoints can deal with returned `Attachment` objects (rather than returned `Job` objects) and return 404 if the endpoint proc returns nil
|
15
|
+
- default Content-Disposition header doesn't url-encode filename unless the request is from IE
|
16
|
+
- `fetch_url` deals with urls that redirect to https (previously was blowing up)
|
17
|
+
|
1
18
|
1.0.0 (2013-11-24)
|
2
19
|
===================
|
3
20
|
Changes
|
data/README.md
CHANGED
@@ -55,8 +55,12 @@ Require with
|
|
55
55
|
require 'dragonfly'
|
56
56
|
```
|
57
57
|
|
58
|
-
|
59
|
-
|
58
|
+
Ruby Versions
|
59
|
+
=============
|
60
|
+
Dragonfly has been tested with ruby versions 1.8.7, 1.9.2, 1.9.3, 2.0.0 and jruby 1.7.8.
|
61
|
+
|
62
|
+
Plugins / add-ons
|
63
|
+
=================
|
60
64
|
See [the Add-ons wiki](http://github.com/markevans/dragonfly/wiki/Dragonfly-add-ons).
|
61
65
|
|
62
66
|
Please feel free to contribute!!
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/dragonfly.gemspec
CHANGED
@@ -24,10 +24,26 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_runtime_dependency("rack", [">= 0"])
|
25
25
|
spec.add_runtime_dependency("multi_json", ["~> 1.0"])
|
26
26
|
|
27
|
-
spec.add_development_dependency("activemodel")
|
28
27
|
spec.add_development_dependency("rspec", ["~> 2.5"])
|
29
28
|
spec.add_development_dependency("webmock")
|
29
|
+
if RUBY_VERSION < '1.9.3'
|
30
|
+
spec.add_development_dependency("activemodel", '~> 3.2')
|
31
|
+
else
|
32
|
+
spec.add_development_dependency("activemodel")
|
33
|
+
end
|
30
34
|
if RUBY_PLATFORM == "java"
|
31
35
|
spec.add_development_dependency("jruby-openssl")
|
32
36
|
end
|
37
|
+
spec.post_install_message =<<-POST_INSTALL_MESSAGE
|
38
|
+
================================================================================
|
39
|
+
|
40
|
+
Please check the documentation at <http://markevans.github.io/dragonfly/>.
|
41
|
+
|
42
|
+
--------------------------------------------------------------------------------
|
43
|
+
|
44
|
+
DEPRECATED: dragonfly/rails/images is now deprecated.
|
45
|
+
Please refer to <http://markevans.github.io/dragonfly/rails/> for instructions to setup
|
46
|
+
with Rails
|
47
|
+
--------------------------------------------------------------------------------
|
48
|
+
POST_INSTALL_MESSAGE
|
33
49
|
end
|
data/lib/dragonfly/app.rb
CHANGED
@@ -207,7 +207,7 @@ module Dragonfly
|
|
207
207
|
|
208
208
|
def ext_for(mime_type)
|
209
209
|
return 'txt' if mime_type == 'text/plain'
|
210
|
-
ext = mime_types
|
210
|
+
ext = key_for(mime_types, mime_type)
|
211
211
|
ext.tr('.', '') if ext
|
212
212
|
end
|
213
213
|
|
@@ -279,5 +279,14 @@ module Dragonfly
|
|
279
279
|
'.' + format.to_s.downcase.sub(/^.*\./,'')
|
280
280
|
end
|
281
281
|
|
282
|
+
def key_for(hash, value)
|
283
|
+
if hash.respond_to?(:key)
|
284
|
+
hash.key(value)
|
285
|
+
else
|
286
|
+
hash.each {|k, v| return k if v == value }
|
287
|
+
nil
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
282
291
|
end
|
283
292
|
end
|
@@ -177,7 +177,7 @@ module Dragonfly
|
|
177
177
|
|
178
178
|
def relative_path_for(filename)
|
179
179
|
time = Time.now
|
180
|
-
"#{time.strftime '%Y/%m/%d
|
180
|
+
"#{time.strftime '%Y/%m/%d/'}#{rand(1e15).to_s(36)}_#{filename.gsub(/[^\w.]+/,'_')}"
|
181
181
|
end
|
182
182
|
|
183
183
|
def purge_empty_directories(path)
|
@@ -69,9 +69,10 @@ module Dragonfly
|
|
69
69
|
end
|
70
70
|
|
71
71
|
# Extra methods
|
72
|
-
app.define :identify do
|
72
|
+
app.define :identify do |*args|
|
73
|
+
cli_args = args.first # because ruby 1.8.7 can't deal with default args in blocks
|
73
74
|
shell_eval do |path|
|
74
|
-
"#{app.env[:identify_command]} #{
|
75
|
+
"#{app.env[:identify_command]} #{cli_args} #{path}"
|
75
76
|
end
|
76
77
|
end
|
77
78
|
|
data/lib/dragonfly/job.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
require 'digest/sha1'
|
3
|
-
require 'uri'
|
4
|
-
require 'open-uri'
|
5
|
-
require 'pathname'
|
6
|
-
require 'base64'
|
7
3
|
require 'dragonfly/serializer'
|
8
4
|
require 'dragonfly/content'
|
9
5
|
require 'dragonfly/url_attributes'
|
@@ -27,180 +23,11 @@ module Dragonfly
|
|
27
23
|
:analyse, :shell_eval, :store,
|
28
24
|
:b64_data
|
29
25
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
@basename ||= name.split('::').last
|
36
|
-
end
|
37
|
-
# Dragonfly::Job::Fetch -> :fetch
|
38
|
-
def step_name
|
39
|
-
@step_name ||= basename.gsub(/[A-Z]/){ "_#{$&.downcase}" }.sub('_','').to_sym
|
40
|
-
end
|
41
|
-
# Dragonfly::Job::Fetch -> 'f'
|
42
|
-
def abbreviation
|
43
|
-
@abbreviation ||= basename.scan(/[A-Z]/).join.downcase
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def initialize(job, *args)
|
48
|
-
@job, @args = job, args
|
49
|
-
init
|
50
|
-
end
|
51
|
-
|
52
|
-
def init # To be overridden
|
53
|
-
end
|
54
|
-
|
55
|
-
attr_reader :job, :args
|
56
|
-
|
57
|
-
def app
|
58
|
-
job.app
|
59
|
-
end
|
60
|
-
|
61
|
-
def to_a
|
62
|
-
[self.class.abbreviation, *args]
|
63
|
-
end
|
64
|
-
|
65
|
-
def inspect
|
66
|
-
"#{self.class.step_name}(#{args.map{|a| a.inspect }.join(', ')})"
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|
70
|
-
|
71
|
-
class Fetch < Step
|
72
|
-
class NotFound < RuntimeError; end
|
73
|
-
|
74
|
-
def uid
|
75
|
-
args.first
|
76
|
-
end
|
77
|
-
|
78
|
-
def apply
|
79
|
-
content, meta = app.datastore.read(uid)
|
80
|
-
raise NotFound, "uid #{uid} not found" if content.nil?
|
81
|
-
job.content.update(content, meta)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
class Process < Step
|
86
|
-
def init
|
87
|
-
processor.update_url(job.url_attributes, *arguments) if processor.respond_to?(:update_url)
|
88
|
-
end
|
89
|
-
|
90
|
-
def name
|
91
|
-
args.first.to_sym
|
92
|
-
end
|
93
|
-
|
94
|
-
def arguments
|
95
|
-
args[1..-1]
|
96
|
-
end
|
97
|
-
|
98
|
-
def processor
|
99
|
-
@processor ||= app.get_processor(name)
|
100
|
-
end
|
101
|
-
|
102
|
-
def apply
|
103
|
-
processor.call(job.content, *arguments)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
class Generate < Step
|
108
|
-
def init
|
109
|
-
generator.update_url(job.url_attributes, *arguments) if generator.respond_to?(:update_url)
|
110
|
-
end
|
111
|
-
|
112
|
-
def name
|
113
|
-
args.first
|
114
|
-
end
|
115
|
-
|
116
|
-
def generator
|
117
|
-
@generator ||= app.get_generator(name)
|
118
|
-
end
|
119
|
-
|
120
|
-
def arguments
|
121
|
-
args[1..-1]
|
122
|
-
end
|
123
|
-
|
124
|
-
def apply
|
125
|
-
generator.call(job.content, *arguments)
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
class FetchFile < Step
|
130
|
-
def initialize(job, path)
|
131
|
-
super(job, path.to_s)
|
132
|
-
end
|
133
|
-
def init
|
134
|
-
job.url_attributes.name = filename
|
135
|
-
end
|
136
|
-
|
137
|
-
def path
|
138
|
-
@path ||= File.expand_path(args.first)
|
139
|
-
end
|
140
|
-
|
141
|
-
def filename
|
142
|
-
@filename ||= File.basename(path)
|
143
|
-
end
|
144
|
-
|
145
|
-
def apply
|
146
|
-
job.content.update(Pathname.new(path), 'name' => filename)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
class FetchUrl < Step
|
151
|
-
class ErrorResponse < RuntimeError
|
152
|
-
def initialize(status, body)
|
153
|
-
@status, @body = status, body
|
154
|
-
end
|
155
|
-
attr_reader :status, :body
|
156
|
-
end
|
157
|
-
class CannotHandle < RuntimeError; end
|
158
|
-
|
159
|
-
def init
|
160
|
-
job.url_attributes.name = filename
|
161
|
-
end
|
162
|
-
|
163
|
-
def uri
|
164
|
-
args.first
|
165
|
-
end
|
166
|
-
|
167
|
-
def url
|
168
|
-
@url ||= uri =~ /^\w+:/ ? uri : "http://#{uri}"
|
169
|
-
end
|
170
|
-
|
171
|
-
def filename
|
172
|
-
return if data_uri?
|
173
|
-
@filename ||= URI.parse(url).path[/[^\/]+$/]
|
174
|
-
end
|
175
|
-
|
176
|
-
def data_uri?
|
177
|
-
uri =~ /^data:/
|
178
|
-
end
|
179
|
-
|
180
|
-
def apply
|
181
|
-
if data_uri?
|
182
|
-
update_from_data_uri
|
183
|
-
else
|
184
|
-
open(URI.escape(url)) do |f|
|
185
|
-
job.content.update(f.read, 'name' => filename)
|
186
|
-
end
|
187
|
-
end
|
188
|
-
rescue OpenURI::HTTPError => e
|
189
|
-
status, message = e.io.status
|
190
|
-
raise ErrorResponse.new(status.to_i, e.io.read)
|
191
|
-
end
|
192
|
-
|
193
|
-
def update_from_data_uri
|
194
|
-
mime_type, b64_data = uri.scan(/^data:([^;]+);base64,(.*)$/)[0]
|
195
|
-
if mime_type && b64_data
|
196
|
-
data = Base64.decode64(b64_data)
|
197
|
-
ext = app.ext_for(mime_type)
|
198
|
-
job.content.update(data, 'name' => "file.#{ext}")
|
199
|
-
else
|
200
|
-
raise CannotHandle, "fetch_url can only deal with base64-encoded data uris with specified content type"
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
26
|
+
require 'dragonfly/job/fetch'
|
27
|
+
require 'dragonfly/job/fetch_file'
|
28
|
+
require 'dragonfly/job/fetch_url'
|
29
|
+
require 'dragonfly/job/generate'
|
30
|
+
require 'dragonfly/job/process'
|
204
31
|
|
205
32
|
STEPS = [
|
206
33
|
Fetch,
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'dragonfly/job/step'
|
2
|
+
|
3
|
+
module Dragonfly
|
4
|
+
class Job
|
5
|
+
class Fetch < Step
|
6
|
+
class NotFound < RuntimeError; end
|
7
|
+
|
8
|
+
def uid
|
9
|
+
args.first
|
10
|
+
end
|
11
|
+
|
12
|
+
def apply
|
13
|
+
content, meta = app.datastore.read(uid)
|
14
|
+
raise NotFound, "uid #{uid} not found" if content.nil?
|
15
|
+
job.content.update(content, meta)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'dragonfly/job/step'
|
3
|
+
|
4
|
+
module Dragonfly
|
5
|
+
class Job
|
6
|
+
class FetchFile < Step
|
7
|
+
def initialize(job, path)
|
8
|
+
super(job, path.to_s)
|
9
|
+
end
|
10
|
+
def init
|
11
|
+
job.url_attributes.name = filename
|
12
|
+
end
|
13
|
+
|
14
|
+
def path
|
15
|
+
@path ||= File.expand_path(args.first)
|
16
|
+
end
|
17
|
+
|
18
|
+
def filename
|
19
|
+
@filename ||= File.basename(path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def apply
|
23
|
+
job.content.update(Pathname.new(path), 'name' => filename)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'base64'
|
4
|
+
require 'dragonfly/job/step'
|
5
|
+
|
6
|
+
module Dragonfly
|
7
|
+
class Job
|
8
|
+
class FetchUrl < Step
|
9
|
+
|
10
|
+
class ErrorResponse < RuntimeError
|
11
|
+
def initialize(status, body)
|
12
|
+
@status, @body = status, body
|
13
|
+
end
|
14
|
+
attr_reader :status, :body
|
15
|
+
end
|
16
|
+
class CannotHandle < RuntimeError; end
|
17
|
+
class TooManyRedirects < RuntimeError; end
|
18
|
+
|
19
|
+
def init
|
20
|
+
job.url_attributes.name = filename
|
21
|
+
end
|
22
|
+
|
23
|
+
def uri
|
24
|
+
args.first
|
25
|
+
end
|
26
|
+
|
27
|
+
def url
|
28
|
+
@url ||= uri =~ /^\w+:/ ? uri : "http://#{uri}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def filename
|
32
|
+
return if data_uri?
|
33
|
+
@filename ||= URI.parse(url).path[/[^\/]+$/]
|
34
|
+
end
|
35
|
+
|
36
|
+
def data_uri?
|
37
|
+
uri =~ /^data:/
|
38
|
+
end
|
39
|
+
|
40
|
+
def apply
|
41
|
+
if data_uri?
|
42
|
+
update_from_data_uri
|
43
|
+
else
|
44
|
+
data = get(URI.escape(url))
|
45
|
+
job.content.update(data, 'name' => filename)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def get(url, redirect_limit=10)
|
50
|
+
raise TooManyRedirects, "url #{url} redirected too many times" if redirect_limit == 0
|
51
|
+
response = Net::HTTP.get_response(URI.parse(url))
|
52
|
+
case response
|
53
|
+
when Net::HTTPSuccess then response.body || ""
|
54
|
+
when Net::HTTPRedirection then get(response['location'], redirect_limit-1)
|
55
|
+
else
|
56
|
+
response.error!
|
57
|
+
end
|
58
|
+
rescue Net::HTTPExceptions => e
|
59
|
+
raise ErrorResponse.new(e.response.code.to_i, e.response.body)
|
60
|
+
end
|
61
|
+
|
62
|
+
def update_from_data_uri
|
63
|
+
mime_type, b64_data = uri.scan(/^data:([^;]+);base64,(.*)$/)[0]
|
64
|
+
if mime_type && b64_data
|
65
|
+
data = Base64.decode64(b64_data)
|
66
|
+
ext = app.ext_for(mime_type)
|
67
|
+
job.content.update(data, 'name' => "file.#{ext}")
|
68
|
+
else
|
69
|
+
raise CannotHandle, "fetch_url can only deal with base64-encoded data uris with specified content type"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|