oahu-dragonfly 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|