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,39 @@
|
|
|
1
|
+
module Dragonfly
|
|
2
|
+
class JobBuilder
|
|
3
|
+
|
|
4
|
+
def initialize(&block)
|
|
5
|
+
@block = block
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def build(job, *args)
|
|
9
|
+
evaluate_block(job, false, *args)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def build!(job, *args)
|
|
13
|
+
evaluate_block(job, true, *args)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Job.step_names.each do |step|
|
|
17
|
+
|
|
18
|
+
# fetch, process, etc.
|
|
19
|
+
define_method step do |*args|
|
|
20
|
+
if @perform_with_bangs
|
|
21
|
+
@job.send("#{step}!", *args)
|
|
22
|
+
else
|
|
23
|
+
@job = @job.send(step, *args)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def evaluate_block(job, perform_with_bangs, *args)
|
|
32
|
+
@job = job
|
|
33
|
+
@perform_with_bangs = perform_with_bangs
|
|
34
|
+
instance_exec(*args, &@block)
|
|
35
|
+
@job
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Dragonfly
|
|
2
|
+
class JobDefinitions < Module
|
|
3
|
+
|
|
4
|
+
def initialize
|
|
5
|
+
@job_definitions = {}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def add(name, &definition_proc)
|
|
9
|
+
job_definitions[name] = JobBuilder.new(&definition_proc)
|
|
10
|
+
jd = job_definitions # Needed because we're about to change 'self'
|
|
11
|
+
|
|
12
|
+
define_method name do |*args|
|
|
13
|
+
jd[name].build(self, *args)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
define_method "#{name}!" do |*args|
|
|
17
|
+
jd[name].build!(self, *args)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
attr_reader :job_definitions
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
|
|
3
|
+
module Dragonfly
|
|
4
|
+
module Loggable
|
|
5
|
+
|
|
6
|
+
def log
|
|
7
|
+
case @log_object
|
|
8
|
+
when nil
|
|
9
|
+
@log_object = Logger.new($stdout)
|
|
10
|
+
when Proc
|
|
11
|
+
@log_object[]
|
|
12
|
+
when Logger
|
|
13
|
+
@log_object
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def log=(object)
|
|
18
|
+
@log_object = object
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
attr_reader :log_object
|
|
22
|
+
|
|
23
|
+
def use_same_log_as(object)
|
|
24
|
+
self.log = proc{ object.log }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Dragonfly
|
|
2
|
+
class Middleware
|
|
3
|
+
|
|
4
|
+
def initialize(app, dragonfly_app_name, deprecated_arg=nil)
|
|
5
|
+
raise ArgumentError, "mounting Dragonfly::Middleware with a mount point is deprecated - just use Dragonfly::Middleware, #{dragonfly_app_name.inspect}" if deprecated_arg
|
|
6
|
+
@app = app
|
|
7
|
+
@dragonfly_app_name = dragonfly_app_name
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def call(env)
|
|
11
|
+
response = Dragonfly[@dragonfly_app_name].call(env)
|
|
12
|
+
if response[1]['X-Cascade'] == 'pass'
|
|
13
|
+
@app.call(env)
|
|
14
|
+
else
|
|
15
|
+
response
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'dragonfly'
|
|
2
|
+
require 'uri'
|
|
3
|
+
|
|
4
|
+
### The dragonfly app ###
|
|
5
|
+
app = Dragonfly[:images]
|
|
6
|
+
app.configure_with(:rails)
|
|
7
|
+
app.configure_with(:imagemagick)
|
|
8
|
+
|
|
9
|
+
### Extend active record ###
|
|
10
|
+
if defined?(ActiveRecord::Base)
|
|
11
|
+
app.define_macro(ActiveRecord::Base, :image_accessor)
|
|
12
|
+
app.define_macro(ActiveRecord::Base, :file_accessor)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
### Insert the middleware ###
|
|
16
|
+
Rails.application.middleware.insert 0, 'Dragonfly::Middleware', :images
|
|
17
|
+
|
|
18
|
+
begin
|
|
19
|
+
require 'rack/cache'
|
|
20
|
+
Rails.application.middleware.insert_before 'Dragonfly::Middleware', 'Rack::Cache', {
|
|
21
|
+
:verbose => true,
|
|
22
|
+
:metastore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/meta"), # URI encoded because Windows
|
|
23
|
+
:entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body") # has problems with spaces
|
|
24
|
+
}
|
|
25
|
+
rescue LoadError => e
|
|
26
|
+
app.log.warn("Warning: couldn't find rack-cache for caching dragonfly content")
|
|
27
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
|
|
3
|
+
module Dragonfly
|
|
4
|
+
class Response
|
|
5
|
+
|
|
6
|
+
DEFAULT_FILENAME = proc{|job, request|
|
|
7
|
+
if job.basename
|
|
8
|
+
extname = job.encoded_extname || (".#{job.ext}" if job.ext)
|
|
9
|
+
"#{job.basename}#{extname}"
|
|
10
|
+
end
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
def initialize(job, env)
|
|
14
|
+
@job, @env = job, env
|
|
15
|
+
@app = @job.app
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_response
|
|
19
|
+
if !(request.head? || request.get?)
|
|
20
|
+
[405, method_not_allowed_headers, ["#{request.request_method} method not allowed"]]
|
|
21
|
+
elsif etag_matches?
|
|
22
|
+
[304, cache_headers, []]
|
|
23
|
+
elsif request.head?
|
|
24
|
+
[200, success_headers.merge(cache_headers), []]
|
|
25
|
+
elsif request.get?
|
|
26
|
+
[200, success_headers.merge(cache_headers), job.result]
|
|
27
|
+
end
|
|
28
|
+
rescue DataStorage::DataNotFound => e
|
|
29
|
+
app.log.warn(e.message)
|
|
30
|
+
[404, {"Content-Type" => 'text/plain'}, ['Not found']]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def served?
|
|
34
|
+
request.get? && !etag_matches?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
attr_reader :job, :env, :app
|
|
40
|
+
|
|
41
|
+
def request
|
|
42
|
+
@request ||= Rack::Request.new(env)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def cache_headers
|
|
46
|
+
{
|
|
47
|
+
"Cache-Control" => "public, max-age=#{app.cache_duration}",
|
|
48
|
+
"ETag" => %("#{job.unique_signature}")
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def etag_matches?
|
|
53
|
+
return @etag_matches unless @etag_matches.nil?
|
|
54
|
+
if_none_match = env['HTTP_IF_NONE_MATCH']
|
|
55
|
+
@etag_matches = if if_none_match
|
|
56
|
+
if_none_match.tr!('"','')
|
|
57
|
+
if_none_match.split(',').include?(job.unique_signature) || if_none_match == '*'
|
|
58
|
+
else
|
|
59
|
+
false
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def success_headers
|
|
64
|
+
{
|
|
65
|
+
"Content-Type" => job.mime_type,
|
|
66
|
+
"Content-Length" => job.size.to_s
|
|
67
|
+
}.merge(content_disposition_header)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def content_disposition_header
|
|
71
|
+
parts = []
|
|
72
|
+
parts << content_disposition if content_disposition
|
|
73
|
+
parts << %(filename="#{URI.encode(filename)}") if filename
|
|
74
|
+
parts.any? ? {"Content-Disposition" => parts.join('; ')} : {}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def method_not_allowed_headers
|
|
78
|
+
{
|
|
79
|
+
'Content-Type' => 'text/plain',
|
|
80
|
+
'Allow' => 'GET, HEAD'
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def content_disposition
|
|
85
|
+
@content_disposition ||= evaluate(app.content_disposition)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def filename
|
|
89
|
+
@filename ||= evaluate(app.content_filename)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def evaluate(attribute)
|
|
93
|
+
attribute.respond_to?(:call) ? attribute.call(job, request) : attribute
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Dragonfly
|
|
2
|
+
class RoutedEndpoint
|
|
3
|
+
|
|
4
|
+
class NoRoutingParams < RuntimeError; end
|
|
5
|
+
|
|
6
|
+
def initialize(app, &block)
|
|
7
|
+
@app = app
|
|
8
|
+
@block = block
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call(env)
|
|
12
|
+
params = symbolize_keys_of Rack::Request.new(env).params
|
|
13
|
+
job = @block.call(params.merge(routing_params(env)), @app)
|
|
14
|
+
Response.new(job, env).to_response
|
|
15
|
+
rescue Job::NoSHAGiven => e
|
|
16
|
+
[400, {"Content-Type" => 'text/plain'}, ["You need to give a SHA parameter"]]
|
|
17
|
+
rescue Job::IncorrectSHA => e
|
|
18
|
+
[400, {"Content-Type" => 'text/plain'}, ["The SHA parameter you gave (#{e}) is incorrect"]]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def routing_params(env)
|
|
24
|
+
env['rack.routing_args'] ||
|
|
25
|
+
env['action_dispatch.request.path_parameters'] ||
|
|
26
|
+
env['router.params'] ||
|
|
27
|
+
env['usher.params'] ||
|
|
28
|
+
env['dragonfly.params'] ||
|
|
29
|
+
raise(NoRoutingParams, "couldn't find any routing parameters in env #{env.inspect}")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def symbolize_keys_of(hash)
|
|
33
|
+
hash.inject({}) do |h, (key, value)|
|
|
34
|
+
h[(key.to_sym rescue key) || key] = value
|
|
35
|
+
h
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'base64'
|
|
3
|
+
|
|
4
|
+
module Dragonfly
|
|
5
|
+
module Serializer
|
|
6
|
+
|
|
7
|
+
# Exceptions
|
|
8
|
+
class BadString < RuntimeError; end
|
|
9
|
+
|
|
10
|
+
extend self # So we can do Serializer.b64_encode, etc.
|
|
11
|
+
|
|
12
|
+
def b64_encode(string)
|
|
13
|
+
Base64.encode64(string).tr("\n=",'')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def b64_decode(string)
|
|
17
|
+
padding_length = string.length % 4
|
|
18
|
+
Base64.decode64(string + '=' * padding_length)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def marshal_encode(object)
|
|
22
|
+
b64_encode(Marshal.dump(object))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def marshal_decode(string)
|
|
26
|
+
Marshal.load(b64_decode(string))
|
|
27
|
+
rescue TypeError, ArgumentError => e
|
|
28
|
+
raise BadString, "couldn't decode #{string} - got #{e}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
module Dragonfly
|
|
2
|
+
class Server
|
|
3
|
+
|
|
4
|
+
include Loggable
|
|
5
|
+
include Configurable
|
|
6
|
+
|
|
7
|
+
configurable_attr :dragonfly_url, '/dragonfly'
|
|
8
|
+
configurable_attr :protect_from_dos_attacks, false
|
|
9
|
+
configurable_attr :url_format, '/:job/:basename.:format'
|
|
10
|
+
configurable_attr :url_host
|
|
11
|
+
|
|
12
|
+
extend Forwardable
|
|
13
|
+
def_delegator :url_mapper, :params_in_url
|
|
14
|
+
|
|
15
|
+
def initialize(app)
|
|
16
|
+
@app = app
|
|
17
|
+
use_same_log_as(app)
|
|
18
|
+
use_as_fallback_config(app)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def before_serve(&block)
|
|
22
|
+
self.before_serve_callback = block
|
|
23
|
+
end
|
|
24
|
+
configuration_method :before_serve
|
|
25
|
+
|
|
26
|
+
def after_serve(&block)
|
|
27
|
+
self.after_serve_callback = block
|
|
28
|
+
end
|
|
29
|
+
configuration_method :after_serve
|
|
30
|
+
|
|
31
|
+
def call(env)
|
|
32
|
+
if dragonfly_url == env["PATH_INFO"]
|
|
33
|
+
dragonfly_response
|
|
34
|
+
elsif (params = url_mapper.params_for(env["PATH_INFO"], env["QUERY_STRING"])) && params['job']
|
|
35
|
+
job = Job.deserialize(params['job'], app)
|
|
36
|
+
job.validate_sha!(params['sha']) if protect_from_dos_attacks
|
|
37
|
+
response = Response.new(job, env)
|
|
38
|
+
catch(:halt) do
|
|
39
|
+
if before_serve_callback && response.served?
|
|
40
|
+
before_serve_callback.call(job, env)
|
|
41
|
+
end
|
|
42
|
+
resp = response.to_response
|
|
43
|
+
if after_serve_callback && resp[0] == 200 && resp[2].is_a?(Dragonfly::TempObject)
|
|
44
|
+
after_serve_callback.call(job, env)
|
|
45
|
+
end
|
|
46
|
+
resp
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
[404, {'Content-Type' => 'text/plain', 'X-Cascade' => 'pass'}, ['Not found']]
|
|
50
|
+
end
|
|
51
|
+
rescue Serializer::BadString, Job::InvalidArray => e
|
|
52
|
+
log.warn(e.message)
|
|
53
|
+
[404, {'Content-Type' => 'text/plain'}, ['Not found']]
|
|
54
|
+
rescue Job::NoSHAGiven => e
|
|
55
|
+
[400, {"Content-Type" => 'text/plain'}, ["You need to give a SHA parameter"]]
|
|
56
|
+
rescue Job::IncorrectSHA => e
|
|
57
|
+
[400, {"Content-Type" => 'text/plain'}, ["The SHA parameter you gave (#{e}) is incorrect"]]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def url_for(job, opts={})
|
|
61
|
+
opts = opts.dup
|
|
62
|
+
host = opts.delete(:host) || url_host
|
|
63
|
+
params = stringify_keys(opts)
|
|
64
|
+
params['job'] = job.serialize
|
|
65
|
+
params['sha'] = job.sha if protect_from_dos_attacks
|
|
66
|
+
url = url_mapper.url_for(params)
|
|
67
|
+
"#{host}#{url}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
attr_reader :app
|
|
73
|
+
attr_accessor :before_serve_callback
|
|
74
|
+
attr_accessor :after_serve_callback
|
|
75
|
+
|
|
76
|
+
def url_mapper
|
|
77
|
+
@url_mapper ||= UrlMapper.new(url_format,
|
|
78
|
+
:job => '\w',
|
|
79
|
+
:basename => '[^\/]',
|
|
80
|
+
:format => '[^\.]'
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def stringify_keys(params)
|
|
85
|
+
params.inject({}) do |hash, (k, v)|
|
|
86
|
+
hash[k.to_s] = v
|
|
87
|
+
hash
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def dragonfly_response
|
|
92
|
+
body = <<-DRAGONFLY
|
|
93
|
+
_o|o_
|
|
94
|
+
_~~---._( )_.---~~_
|
|
95
|
+
( . \\ / . )
|
|
96
|
+
`-.~--' |=| '--~.-'
|
|
97
|
+
_~-.~'" /|=|\\ "'~.-~_
|
|
98
|
+
( ./ |=| \\. )
|
|
99
|
+
`~~`"` |=| `"'ME"
|
|
100
|
+
|-|
|
|
101
|
+
<->
|
|
102
|
+
V
|
|
103
|
+
DRAGONFLY
|
|
104
|
+
[200, {
|
|
105
|
+
'Content-Type' => 'text/plain',
|
|
106
|
+
'Content-Size' => body.bytesize.to_s
|
|
107
|
+
},
|
|
108
|
+
[body]
|
|
109
|
+
]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
end
|