oahu-dragonfly 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/.yardopts +24 -0
- data/Gemfile +30 -0
- data/History.md +323 -0
- data/LICENSE +20 -0
- data/README.md +88 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/config.ru +14 -0
- data/docs.watchr +1 -0
- data/dragonfly.gemspec +297 -0
- data/extra_docs/Analysers.md +66 -0
- data/extra_docs/Caching.md +23 -0
- data/extra_docs/Configuration.md +124 -0
- data/extra_docs/Couch.md +49 -0
- data/extra_docs/DataStorage.md +153 -0
- data/extra_docs/Encoding.md +67 -0
- data/extra_docs/GeneralUsage.md +121 -0
- data/extra_docs/Generators.md +60 -0
- data/extra_docs/Heroku.md +50 -0
- data/extra_docs/ImageMagick.md +125 -0
- data/extra_docs/Index.md +33 -0
- data/extra_docs/MimeTypes.md +40 -0
- data/extra_docs/Models.md +272 -0
- data/extra_docs/Mongo.md +45 -0
- data/extra_docs/Processing.md +77 -0
- data/extra_docs/Rack.md +52 -0
- data/extra_docs/Rails2.md +57 -0
- data/extra_docs/Rails3.md +62 -0
- data/extra_docs/Sinatra.md +25 -0
- data/extra_docs/URLs.md +169 -0
- data/features/images.feature +47 -0
- data/features/no_processing.feature +14 -0
- data/features/rails_3.0.5.feature +8 -0
- data/features/steps/common_steps.rb +8 -0
- data/features/steps/dragonfly_steps.rb +66 -0
- data/features/steps/rails_steps.rb +28 -0
- data/features/support/env.rb +13 -0
- data/features/support/setup.rb +32 -0
- data/fixtures/rails_3.0.5/files/app/models/album.rb +7 -0
- data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
- data/fixtures/rails_3.0.5/files/app/views/albums/show.html.erb +6 -0
- data/fixtures/rails_3.0.5/files/config/initializers/dragonfly.rb +4 -0
- data/fixtures/rails_3.0.5/files/features/manage_album_images.feature +38 -0
- data/fixtures/rails_3.0.5/files/features/step_definitions/helper_steps.rb +7 -0
- data/fixtures/rails_3.0.5/files/features/step_definitions/image_steps.rb +25 -0
- data/fixtures/rails_3.0.5/files/features/support/paths.rb +17 -0
- data/fixtures/rails_3.0.5/files/features/text_images.feature +7 -0
- data/fixtures/rails_3.0.5/template.rb +20 -0
- data/irbrc.rb +18 -0
- data/lib/dragonfly.rb +55 -0
- data/lib/dragonfly/active_model_extensions.rb +13 -0
- data/lib/dragonfly/active_model_extensions/attachment.rb +250 -0
- data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +148 -0
- data/lib/dragonfly/active_model_extensions/class_methods.rb +95 -0
- data/lib/dragonfly/active_model_extensions/instance_methods.rb +28 -0
- data/lib/dragonfly/active_model_extensions/validations.rb +41 -0
- data/lib/dragonfly/analyser.rb +58 -0
- data/lib/dragonfly/analysis/file_command_analyser.rb +32 -0
- data/lib/dragonfly/analysis/image_magick_analyser.rb +6 -0
- data/lib/dragonfly/app.rb +172 -0
- data/lib/dragonfly/config/heroku.rb +19 -0
- data/lib/dragonfly/config/image_magick.rb +6 -0
- data/lib/dragonfly/config/rails.rb +20 -0
- data/lib/dragonfly/configurable.rb +207 -0
- data/lib/dragonfly/core_ext/array.rb +7 -0
- data/lib/dragonfly/core_ext/hash.rb +7 -0
- data/lib/dragonfly/core_ext/object.rb +12 -0
- data/lib/dragonfly/core_ext/string.rb +9 -0
- data/lib/dragonfly/core_ext/symbol.rb +9 -0
- data/lib/dragonfly/data_storage.rb +9 -0
- data/lib/dragonfly/data_storage/couch_data_store.rb +64 -0
- data/lib/dragonfly/data_storage/file_data_store.rb +141 -0
- data/lib/dragonfly/data_storage/mongo_data_store.rb +86 -0
- data/lib/dragonfly/data_storage/s3data_store.rb +145 -0
- data/lib/dragonfly/encoder.rb +13 -0
- data/lib/dragonfly/encoding/image_magick_encoder.rb +6 -0
- data/lib/dragonfly/function_manager.rb +71 -0
- data/lib/dragonfly/generation/image_magick_generator.rb +6 -0
- data/lib/dragonfly/generator.rb +9 -0
- data/lib/dragonfly/hash_with_css_style_keys.rb +21 -0
- data/lib/dragonfly/image_magick/analyser.rb +51 -0
- data/lib/dragonfly/image_magick/config.rb +41 -0
- data/lib/dragonfly/image_magick/encoder.rb +57 -0
- data/lib/dragonfly/image_magick/generator.rb +145 -0
- data/lib/dragonfly/image_magick/processor.rb +99 -0
- data/lib/dragonfly/image_magick/utils.rb +72 -0
- data/lib/dragonfly/image_magick_utils.rb +4 -0
- data/lib/dragonfly/job.rb +451 -0
- data/lib/dragonfly/job_builder.rb +39 -0
- data/lib/dragonfly/job_definitions.rb +26 -0
- data/lib/dragonfly/job_endpoint.rb +15 -0
- data/lib/dragonfly/loggable.rb +28 -0
- data/lib/dragonfly/middleware.rb +20 -0
- data/lib/dragonfly/processing/image_magick_processor.rb +6 -0
- data/lib/dragonfly/processor.rb +9 -0
- data/lib/dragonfly/rails/images.rb +27 -0
- data/lib/dragonfly/response.rb +97 -0
- data/lib/dragonfly/routed_endpoint.rb +40 -0
- data/lib/dragonfly/serializer.rb +32 -0
- data/lib/dragonfly/server.rb +113 -0
- data/lib/dragonfly/simple_cache.rb +23 -0
- data/lib/dragonfly/temp_object.rb +175 -0
- data/lib/dragonfly/url_mapper.rb +78 -0
- data/samples/beach.png +0 -0
- data/samples/egg.png +0 -0
- data/samples/round.gif +0 -0
- data/samples/sample.docx +0 -0
- data/samples/taj.jpg +0 -0
- data/spec/dragonfly/active_model_extensions/model_spec.rb +1426 -0
- data/spec/dragonfly/active_model_extensions/spec_helper.rb +91 -0
- data/spec/dragonfly/analyser_spec.rb +123 -0
- data/spec/dragonfly/analysis/file_command_analyser_spec.rb +48 -0
- data/spec/dragonfly/app_spec.rb +135 -0
- data/spec/dragonfly/configurable_spec.rb +461 -0
- data/spec/dragonfly/core_ext/array_spec.rb +19 -0
- data/spec/dragonfly/core_ext/hash_spec.rb +19 -0
- data/spec/dragonfly/core_ext/string_spec.rb +17 -0
- data/spec/dragonfly/core_ext/symbol_spec.rb +17 -0
- data/spec/dragonfly/data_storage/couch_data_store_spec.rb +76 -0
- data/spec/dragonfly/data_storage/file_data_store_spec.rb +296 -0
- data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +57 -0
- data/spec/dragonfly/data_storage/s3_data_store_spec.rb +258 -0
- data/spec/dragonfly/data_storage/shared_data_store_examples.rb +77 -0
- data/spec/dragonfly/function_manager_spec.rb +154 -0
- data/spec/dragonfly/hash_with_css_style_keys_spec.rb +24 -0
- data/spec/dragonfly/image_magick/analyser_spec.rb +64 -0
- data/spec/dragonfly/image_magick/encoder_spec.rb +41 -0
- data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
- data/spec/dragonfly/image_magick/processor_spec.rb +233 -0
- data/spec/dragonfly/image_magick/utils_spec.rb +18 -0
- data/spec/dragonfly/job_builder_spec.rb +37 -0
- data/spec/dragonfly/job_definitions_spec.rb +35 -0
- data/spec/dragonfly/job_endpoint_spec.rb +173 -0
- data/spec/dragonfly/job_spec.rb +1046 -0
- data/spec/dragonfly/loggable_spec.rb +80 -0
- data/spec/dragonfly/middleware_spec.rb +47 -0
- data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
- data/spec/dragonfly/serializer_spec.rb +61 -0
- data/spec/dragonfly/server_spec.rb +278 -0
- data/spec/dragonfly/simple_cache_spec.rb +27 -0
- data/spec/dragonfly/temp_object_spec.rb +306 -0
- data/spec/dragonfly/url_mapper_spec.rb +126 -0
- data/spec/functional/deprecations_spec.rb +51 -0
- data/spec/functional/image_magick_app_spec.rb +27 -0
- data/spec/functional/model_urls_spec.rb +85 -0
- data/spec/functional/remote_on_the_fly_spec.rb +51 -0
- data/spec/functional/to_response_spec.rb +31 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/support/argument_matchers.rb +19 -0
- data/spec/support/image_matchers.rb +47 -0
- data/spec/support/simple_matchers.rb +53 -0
- data/yard/handlers/configurable_attr_handler.rb +38 -0
- data/yard/setup.rb +15 -0
- data/yard/templates/default/fulldoc/html/css/common.css +107 -0
- data/yard/templates/default/layout/html/layout.erb +89 -0
- data/yard/templates/default/module/html/configuration_summary.erb +31 -0
- data/yard/templates/default/module/setup.rb +17 -0
- metadata +544 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module Dragonfly
|
4
|
+
module ImageMagick
|
5
|
+
module Utils
|
6
|
+
|
7
|
+
# Exceptions
|
8
|
+
class ShellCommandFailed < RuntimeError; end
|
9
|
+
|
10
|
+
include Loggable
|
11
|
+
include Configurable
|
12
|
+
configurable_attr :convert_command, "convert"
|
13
|
+
configurable_attr :identify_command, "identify"
|
14
|
+
configurable_attr :log_commands, false
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def convert(temp_object=nil, args='', format=nil)
|
19
|
+
tempfile = new_tempfile(format)
|
20
|
+
run "#{convert_command} #{args} #{temp_object.path if temp_object} #{tempfile.path}"
|
21
|
+
tempfile
|
22
|
+
end
|
23
|
+
|
24
|
+
def identify(temp_object)
|
25
|
+
# example of details string:
|
26
|
+
# myimage.png PNG 200x100 200x100+0+0 8-bit DirectClass 31.2kb
|
27
|
+
details = raw_identify(temp_object)
|
28
|
+
filename, format, geometry, geometry_2, depth, image_class, size = details.split(' ')
|
29
|
+
width, height = geometry.split('x')
|
30
|
+
{
|
31
|
+
:filename => filename,
|
32
|
+
:format => format.downcase.to_sym,
|
33
|
+
:width => width.to_i,
|
34
|
+
:height => height.to_i,
|
35
|
+
:depth => depth.to_i,
|
36
|
+
:image_class => image_class
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def raw_identify(temp_object, args='')
|
41
|
+
run "#{identify_command} #{args} #{temp_object.path}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def new_tempfile(ext=nil)
|
45
|
+
tempfile = ext ? Tempfile.new(['dragonfly', ".#{ext}"]) : Tempfile.new('dragonfly')
|
46
|
+
tempfile.binmode
|
47
|
+
tempfile.close
|
48
|
+
tempfile
|
49
|
+
end
|
50
|
+
|
51
|
+
def run(command)
|
52
|
+
log.debug("Running command: #{command}") if log_commands
|
53
|
+
begin
|
54
|
+
result = `#{command}`
|
55
|
+
rescue Errno::ENOENT
|
56
|
+
raise_shell_command_failed(command)
|
57
|
+
end
|
58
|
+
if $?.exitstatus == 1
|
59
|
+
throw :unable_to_handle
|
60
|
+
elsif !$?.success?
|
61
|
+
raise_shell_command_failed(command)
|
62
|
+
end
|
63
|
+
result
|
64
|
+
end
|
65
|
+
|
66
|
+
def raise_shell_command_failed(command)
|
67
|
+
raise ShellCommandFailed, "Command failed (#{command}) with exit status #{$?.exitstatus}"
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,451 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'digest/sha1'
|
3
|
+
require 'base64'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
module Dragonfly
|
8
|
+
class Job
|
9
|
+
|
10
|
+
# Exceptions
|
11
|
+
class AppDoesNotMatch < StandardError; end
|
12
|
+
class JobAlreadyApplied < StandardError; end
|
13
|
+
class NothingToProcess < StandardError; end
|
14
|
+
class NothingToEncode < StandardError; end
|
15
|
+
class NothingToAnalyse < StandardError; end
|
16
|
+
class InvalidArray < StandardError; end
|
17
|
+
class NoSHAGiven < StandardError; end
|
18
|
+
class IncorrectSHA < StandardError; end
|
19
|
+
|
20
|
+
extend Forwardable
|
21
|
+
def_delegators :result,
|
22
|
+
:data, :file, :tempfile, :path, :to_file, :size
|
23
|
+
def_delegator :app, :server
|
24
|
+
|
25
|
+
class Step
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# Dragonfly::Job::Fetch -> 'Fetch'
|
29
|
+
def basename
|
30
|
+
@basename ||= name.split('::').last
|
31
|
+
end
|
32
|
+
# Dragonfly::Job::Fetch -> :fetch
|
33
|
+
def step_name
|
34
|
+
@step_name ||= basename.gsub(/[A-Z]/){ "_#{$&.downcase}" }.sub('_','').to_sym
|
35
|
+
end
|
36
|
+
# Dragonfly::Job::Fetch -> :f
|
37
|
+
def abbreviation
|
38
|
+
@abbreviation ||= basename.scan(/[A-Z]/).join.downcase.to_sym
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(job, *args)
|
43
|
+
@job, @args = job, args
|
44
|
+
init
|
45
|
+
end
|
46
|
+
|
47
|
+
def init # To be overridden
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_reader :job, :args
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
"#{self.class.step_name}(#{args.map{|a| a.inspect }.join(', ')})"
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
class Fetch < Step
|
59
|
+
def uid
|
60
|
+
args.first
|
61
|
+
end
|
62
|
+
def apply
|
63
|
+
content, meta = job.app.datastore.retrieve(uid)
|
64
|
+
job.update(content, meta)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Process < Step
|
69
|
+
def name
|
70
|
+
args.first
|
71
|
+
end
|
72
|
+
def arguments
|
73
|
+
args[1..-1]
|
74
|
+
end
|
75
|
+
def apply
|
76
|
+
raise NothingToProcess, "Can't process because temp object has not been initialized. Need to fetch first?" unless job.temp_object
|
77
|
+
content, meta = job.app.processor.process(job.temp_object, name, *arguments)
|
78
|
+
job.update(content, meta)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class Encode < Step
|
83
|
+
def init
|
84
|
+
job.meta[:format] = format
|
85
|
+
end
|
86
|
+
def format
|
87
|
+
args.first
|
88
|
+
end
|
89
|
+
def arguments
|
90
|
+
args[1..-1]
|
91
|
+
end
|
92
|
+
def apply
|
93
|
+
raise NothingToEncode, "Can't encode because temp object has not been initialized. Need to fetch first?" unless job.temp_object
|
94
|
+
content, meta = job.app.encoder.encode(job.temp_object, format, *arguments)
|
95
|
+
job.update(content, meta)
|
96
|
+
job.meta[:format] = format
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class Generate < Step
|
101
|
+
def apply
|
102
|
+
content, meta = job.app.generator.generate(*args)
|
103
|
+
job.update(content, meta)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class FetchFile < Step
|
108
|
+
def init
|
109
|
+
job.name = File.basename(path)
|
110
|
+
end
|
111
|
+
def path
|
112
|
+
File.expand_path(args.first)
|
113
|
+
end
|
114
|
+
def apply
|
115
|
+
job.temp_object = TempObject.new(Pathname.new(path))
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class FetchUrl < Step
|
120
|
+
def init
|
121
|
+
job.name = File.basename(path) if path[/[^\/]$/]
|
122
|
+
end
|
123
|
+
def url
|
124
|
+
@url ||= (args.first[%r<^\w+://>] ? args.first : "http://#{args.first}")
|
125
|
+
end
|
126
|
+
def path
|
127
|
+
@path ||= URI.parse(url).path
|
128
|
+
end
|
129
|
+
def apply
|
130
|
+
open(url) do |f|
|
131
|
+
job.temp_object = TempObject.new(f.read)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
STEPS = [
|
137
|
+
Fetch,
|
138
|
+
Process,
|
139
|
+
Encode,
|
140
|
+
Generate,
|
141
|
+
FetchFile,
|
142
|
+
FetchUrl
|
143
|
+
]
|
144
|
+
|
145
|
+
# Class methods
|
146
|
+
class << self
|
147
|
+
|
148
|
+
def from_a(steps_array, app)
|
149
|
+
unless steps_array.is_a?(Array) &&
|
150
|
+
steps_array.all?{|s| s.is_a?(Array) && step_abbreviations[s.first] }
|
151
|
+
raise InvalidArray, "can't define a job from #{steps_array.inspect}"
|
152
|
+
end
|
153
|
+
job = app.new_job
|
154
|
+
steps_array.each do |step_array|
|
155
|
+
step_class = step_abbreviations[step_array.shift]
|
156
|
+
job.steps << step_class.new(job, *step_array)
|
157
|
+
end
|
158
|
+
job
|
159
|
+
end
|
160
|
+
|
161
|
+
def deserialize(string, app)
|
162
|
+
from_a(Serializer.marshal_decode(string), app)
|
163
|
+
end
|
164
|
+
|
165
|
+
def step_abbreviations
|
166
|
+
@step_abbreviations ||= STEPS.inject({}){|hash, step_class| hash[step_class.abbreviation] = step_class; hash }
|
167
|
+
end
|
168
|
+
|
169
|
+
def step_names
|
170
|
+
@step_names ||= STEPS.map{|step_class| step_class.step_name }
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
####### Instance methods #######
|
176
|
+
|
177
|
+
# This is needed because we need a way of overriding
|
178
|
+
# the methods added to Job objects by the analyser and by
|
179
|
+
# the job shortcuts like 'thumb', etc.
|
180
|
+
# If we had traits/classboxes in ruby maybe this wouldn't be needed
|
181
|
+
# Think of it as like a normal instance method but with a css-like !important after it
|
182
|
+
module OverrideInstanceMethods
|
183
|
+
|
184
|
+
def format
|
185
|
+
apply
|
186
|
+
format_from_meta || analyse(:format)
|
187
|
+
end
|
188
|
+
|
189
|
+
def mime_type
|
190
|
+
app.mime_type_for(format) || analyse(:mime_type) || app.fallback_mime_type
|
191
|
+
end
|
192
|
+
|
193
|
+
def to_s
|
194
|
+
super.sub(/#<Class:\w+>/, 'Extended Dragonfly::Job')
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
def initialize(app, content=nil, meta={})
|
200
|
+
@app = app
|
201
|
+
@steps = []
|
202
|
+
@next_step_index = 0
|
203
|
+
@meta = {}
|
204
|
+
update(content, meta)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Used by 'dup' and 'clone'
|
208
|
+
def initialize_copy(other)
|
209
|
+
self.steps = other.steps.map do |step|
|
210
|
+
step.class.new(self, *step.args)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
attr_accessor :temp_object
|
215
|
+
attr_reader :app, :steps
|
216
|
+
|
217
|
+
# define fetch(), fetch!(), process(), etc.
|
218
|
+
STEPS.each do |step_class|
|
219
|
+
class_eval %(
|
220
|
+
def #{step_class.step_name}(*args)
|
221
|
+
new_job = self.dup
|
222
|
+
new_job.steps << #{step_class}.new(new_job, *args)
|
223
|
+
new_job
|
224
|
+
end
|
225
|
+
|
226
|
+
def #{step_class.step_name}!(*args)
|
227
|
+
steps << #{step_class}.new(self, *args)
|
228
|
+
self
|
229
|
+
end
|
230
|
+
)
|
231
|
+
end
|
232
|
+
|
233
|
+
def analyse(method, *args)
|
234
|
+
unless result
|
235
|
+
raise NothingToAnalyse, "Can't analyse because temp object has not been initialized. Need to fetch first?"
|
236
|
+
end
|
237
|
+
analyser.analyse(result, method, *args)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Applying, etc.
|
241
|
+
|
242
|
+
def apply
|
243
|
+
pending_steps.each{|step| step.apply }
|
244
|
+
self.next_step_index = steps.length
|
245
|
+
self
|
246
|
+
end
|
247
|
+
|
248
|
+
def applied?
|
249
|
+
next_step_index == steps.length
|
250
|
+
end
|
251
|
+
|
252
|
+
def result
|
253
|
+
apply
|
254
|
+
temp_object
|
255
|
+
end
|
256
|
+
|
257
|
+
def applied_steps
|
258
|
+
steps[0...next_step_index]
|
259
|
+
end
|
260
|
+
|
261
|
+
def pending_steps
|
262
|
+
steps[next_step_index..-1]
|
263
|
+
end
|
264
|
+
|
265
|
+
def to_a
|
266
|
+
steps.map{|step|
|
267
|
+
[step.class.abbreviation, *step.args]
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
# Serializing, etc.
|
272
|
+
|
273
|
+
def to_unique_s
|
274
|
+
to_a.to_dragonfly_unique_s
|
275
|
+
end
|
276
|
+
|
277
|
+
def serialize
|
278
|
+
Serializer.marshal_encode(to_a)
|
279
|
+
end
|
280
|
+
|
281
|
+
def unique_signature
|
282
|
+
Digest::SHA1.hexdigest(to_unique_s)
|
283
|
+
end
|
284
|
+
|
285
|
+
def sha
|
286
|
+
Digest::SHA1.hexdigest("#{to_unique_s}#{app.secret}")[0...8]
|
287
|
+
end
|
288
|
+
|
289
|
+
def validate_sha!(sha)
|
290
|
+
case sha
|
291
|
+
when nil
|
292
|
+
raise NoSHAGiven
|
293
|
+
when self.sha
|
294
|
+
self
|
295
|
+
else
|
296
|
+
raise IncorrectSHA, sha
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# URLs, etc.
|
301
|
+
|
302
|
+
def url(opts={})
|
303
|
+
app.url_for(self, attributes_for_url.merge(opts)) unless steps.empty?
|
304
|
+
end
|
305
|
+
|
306
|
+
def b64_data
|
307
|
+
"data:#{mime_type};base64,#{Base64.encode64(data)}"
|
308
|
+
end
|
309
|
+
|
310
|
+
# to_stuff...
|
311
|
+
|
312
|
+
def to_app
|
313
|
+
JobEndpoint.new(self)
|
314
|
+
end
|
315
|
+
|
316
|
+
def to_response(env={"REQUEST_METHOD" => "GET"})
|
317
|
+
to_app.call(env)
|
318
|
+
end
|
319
|
+
|
320
|
+
def to_path
|
321
|
+
"/#{serialize}"
|
322
|
+
end
|
323
|
+
|
324
|
+
def to_fetched_job(uid)
|
325
|
+
new_job = self.class.new(app, temp_object, meta)
|
326
|
+
new_job.fetch!(uid)
|
327
|
+
new_job.next_step_index = 1
|
328
|
+
new_job
|
329
|
+
end
|
330
|
+
|
331
|
+
# Step inspection
|
332
|
+
|
333
|
+
def fetch_step
|
334
|
+
last_step_of_type(Fetch)
|
335
|
+
end
|
336
|
+
|
337
|
+
def uid
|
338
|
+
step = fetch_step
|
339
|
+
step.uid if step
|
340
|
+
end
|
341
|
+
|
342
|
+
def uid_basename
|
343
|
+
File.basename(uid, '.*') if uid
|
344
|
+
end
|
345
|
+
|
346
|
+
def uid_extname
|
347
|
+
File.extname(uid) if uid
|
348
|
+
end
|
349
|
+
|
350
|
+
def generate_step
|
351
|
+
last_step_of_type(Generate)
|
352
|
+
end
|
353
|
+
|
354
|
+
def fetch_file_step
|
355
|
+
last_step_of_type(FetchFile)
|
356
|
+
end
|
357
|
+
|
358
|
+
def fetch_url_step
|
359
|
+
last_step_of_type(FetchUrl)
|
360
|
+
end
|
361
|
+
|
362
|
+
def process_steps
|
363
|
+
steps.select{|s| s.is_a?(Process) }
|
364
|
+
end
|
365
|
+
|
366
|
+
def encode_step
|
367
|
+
last_step_of_type(Encode)
|
368
|
+
end
|
369
|
+
|
370
|
+
def encoded_format
|
371
|
+
step = encode_step
|
372
|
+
step.format if step
|
373
|
+
end
|
374
|
+
|
375
|
+
def encoded_extname
|
376
|
+
format = encoded_format
|
377
|
+
".#{format}" if format
|
378
|
+
end
|
379
|
+
|
380
|
+
# Misc
|
381
|
+
|
382
|
+
def store(opts={})
|
383
|
+
app.store(result, opts.merge(:meta => meta))
|
384
|
+
end
|
385
|
+
|
386
|
+
def inspect
|
387
|
+
to_s.sub(/>$/, " app=#{app}, steps=#{steps.inspect}, temp_object=#{temp_object.inspect}, steps applied:#{applied_steps.length}/#{steps.length} >")
|
388
|
+
end
|
389
|
+
|
390
|
+
# Name and stuff
|
391
|
+
|
392
|
+
attr_reader :meta
|
393
|
+
|
394
|
+
def meta=(hash)
|
395
|
+
raise ArgumentError, "meta must be a hash, you tried setting it as #{hash.inspect}" unless hash.is_a?(Hash)
|
396
|
+
@meta = hash
|
397
|
+
end
|
398
|
+
|
399
|
+
def name
|
400
|
+
meta[:name]
|
401
|
+
end
|
402
|
+
|
403
|
+
def name=(name)
|
404
|
+
meta[:name] = name
|
405
|
+
end
|
406
|
+
|
407
|
+
def basename
|
408
|
+
meta[:basename] || (File.basename(name, '.*') if name)
|
409
|
+
end
|
410
|
+
|
411
|
+
def ext
|
412
|
+
meta[:ext] || (File.extname(name)[/\.(.*)/, 1] if name)
|
413
|
+
end
|
414
|
+
|
415
|
+
def attributes_for_url
|
416
|
+
attrs = meta.reject{|k, v| !server.params_in_url.include?(k.to_s) }
|
417
|
+
attrs[:basename] ||= basename if server.params_in_url.include?('basename')
|
418
|
+
attrs[:ext] ||= ext if server.params_in_url.include?('ext')
|
419
|
+
attrs[:format] = (attrs[:format] || format_from_meta).to_s if server.params_in_url.include?('format')
|
420
|
+
attrs.delete_if{|k, v| v.blank? }
|
421
|
+
attrs
|
422
|
+
end
|
423
|
+
|
424
|
+
def update(content, meta)
|
425
|
+
if meta
|
426
|
+
meta.merge!(meta.delete(:meta)) if meta[:meta] # legacy data etc. may have nested meta hash - deprecate gracefully here
|
427
|
+
self.meta.merge!(meta)
|
428
|
+
end
|
429
|
+
if content
|
430
|
+
self.temp_object = TempObject.new(content)
|
431
|
+
self.name = temp_object.original_filename if name.nil? && temp_object.original_filename
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
protected
|
436
|
+
|
437
|
+
attr_writer :steps
|
438
|
+
attr_accessor :next_step_index
|
439
|
+
|
440
|
+
private
|
441
|
+
|
442
|
+
def format_from_meta
|
443
|
+
meta[:format] || (ext.to_sym if ext && app.trust_file_extensions)
|
444
|
+
end
|
445
|
+
|
446
|
+
def last_step_of_type(type)
|
447
|
+
steps.select{|s| s.is_a?(type) }.last
|
448
|
+
end
|
449
|
+
|
450
|
+
end
|
451
|
+
end
|