shutterbug 0.0.11 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby-version +1 -1
- data/.travis.yml +2 -3
- data/Guardfile +5 -0
- data/README.md +59 -10
- data/config.ru +5 -3
- data/demo/{withIframe.html → iframe_example.html} +0 -0
- data/demo/index.html +7 -24
- data/demo/oil-and-water/heatbath.svg +20 -0
- data/demo/oil-and-water/ke-gradient.svg +27 -0
- data/demo/oil-and-water/oil-and-water.svg +487 -0
- data/demo/simple_example.html +35 -0
- data/demo/svg_example.html +571 -0
- data/images/shutterbug.jpg +0 -0
- data/lib/shutterbug.rb +4 -6
- data/lib/shutterbug/cache_manager.rb +6 -0
- data/lib/shutterbug/cache_manager/cache_entry.rb +19 -0
- data/lib/shutterbug/cache_manager/no_cache.rb +19 -0
- data/lib/shutterbug/configuration.rb +20 -24
- data/lib/shutterbug/handlers.rb +7 -0
- data/lib/shutterbug/handlers/convert_handler.rb +38 -0
- data/lib/shutterbug/handlers/file_handlers.rb +9 -0
- data/lib/shutterbug/handlers/file_handlers/base.rb +39 -0
- data/lib/shutterbug/handlers/file_handlers/html_file.rb +22 -0
- data/lib/shutterbug/handlers/file_handlers/png_file.rb +23 -0
- data/lib/shutterbug/handlers/js_file_handler.rb +27 -0
- data/lib/shutterbug/{shutterbug.js → handlers/shutterbug.js} +26 -3
- data/lib/shutterbug/phantom_job.rb +17 -12
- data/lib/shutterbug/rackapp.rb +33 -31
- data/lib/shutterbug/storage.rb +6 -0
- data/lib/shutterbug/storage/file_storage.rb +20 -0
- data/lib/shutterbug/storage/s3_storage.rb +75 -0
- data/shutterbug.gemspec +3 -0
- data/spec/shared_examples_for_file_handlers.rb +16 -0
- data/spec/shared_examples_for_handlers.rb +26 -0
- data/spec/shared_examples_for_storage.rb +17 -0
- data/spec/shutterbug/configuration_spec.rb +83 -0
- data/spec/shutterbug/convert_handler_spec.rb +29 -0
- data/spec/shutterbug/file_storage_spec.rb +5 -0
- data/spec/shutterbug/html_file_handler_spec.rb +10 -0
- data/spec/shutterbug/js_file_handler_spec.rb +4 -0
- data/spec/shutterbug/png_file_handler_spec.rb +10 -0
- data/spec/shutterbug/rackapp_spec.rb +49 -41
- data/spec/shutterbug/s3_storage_spec.rb +12 -0
- metadata +205 -105
- data/lib/shutterbug/bug_file.rb +0 -22
- data/lib/shutterbug/html_file.rb +0 -3
- data/lib/shutterbug/js_file.rb +0 -11
- data/lib/shutterbug/png_file.rb +0 -3
- data/lib/shutterbug/service.rb +0 -39
Binary file
|
data/lib/shutterbug.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
module Shutterbug
|
2
|
-
VERSION = "0.0
|
3
|
-
autoload :Service, "shutterbug/service"
|
2
|
+
VERSION = "0.1.0"
|
4
3
|
autoload :Rackapp, "shutterbug/rackapp"
|
5
4
|
autoload :Configuration, "shutterbug/configuration"
|
6
|
-
autoload :
|
7
|
-
autoload :
|
8
|
-
autoload :PngFile, "shutterbug/png_file"
|
9
|
-
autoload :JsFile, "shutterbug/js_file"
|
5
|
+
autoload :Storage, "shutterbug/storage"
|
6
|
+
autoload :Handlers, "shutterbug/handlers"
|
10
7
|
autoload :PhantomJob, "shutterbug/phantom_job"
|
8
|
+
autoload :CacheManager, "shutterbug/cache_manager"
|
11
9
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Shutterbug
|
2
|
+
module CacheManager
|
3
|
+
class CacheEntry
|
4
|
+
attr_accessor :key
|
5
|
+
attr_accessor :url
|
6
|
+
attr_accessor :preview_url
|
7
|
+
|
8
|
+
def initialize(storage)
|
9
|
+
@key = storage.filename
|
10
|
+
@url = storage.url
|
11
|
+
@preview_url = storage.url
|
12
|
+
end
|
13
|
+
|
14
|
+
def image_tag
|
15
|
+
"<img src='#{self.preview_url}' alt='#{self.preview_url}'>"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Shutterbug
|
2
|
+
module CacheManager
|
3
|
+
class NoCache
|
4
|
+
attr_accessor :entries
|
5
|
+
def initialize
|
6
|
+
@entries = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def find(key)
|
10
|
+
return @entries[key]
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_entry(cache_entry)
|
14
|
+
@entries[cache_entry.key] = cache_entry
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'tmpdir'
|
1
2
|
module Shutterbug
|
2
3
|
class Configuration
|
3
4
|
|
@@ -5,6 +6,10 @@ module Shutterbug
|
|
5
6
|
attr_accessor :path_prefix
|
6
7
|
attr_accessor :resource_dir
|
7
8
|
attr_accessor :phantom_bin_path
|
9
|
+
attr_accessor :s3_bin
|
10
|
+
attr_accessor :s3_key
|
11
|
+
attr_accessor :s3_secret
|
12
|
+
attr_accessor :cache_manager
|
8
13
|
|
9
14
|
def self.instance(opts={})
|
10
15
|
return @instance || @instance = self.new(opts)
|
@@ -15,43 +20,34 @@ module Shutterbug
|
|
15
20
|
self.path_prefix = opts[:path_prefix] || "/shutterbug"
|
16
21
|
self.resource_dir = opts[:resource_dir] || Dir.tmpdir
|
17
22
|
self.phantom_bin_path = opts[:phantom_bin_path] || "phantomjs"
|
23
|
+
self.s3_bin = opts[:s3_bin]
|
24
|
+
self.s3_key = opts[:s3_key]
|
25
|
+
self.s3_secret = opts[:s3_secret]
|
26
|
+
self.cache_manager = opts[:cache_manager] || Shutterbug::CacheManager::NoCache.new
|
18
27
|
end
|
19
28
|
|
20
|
-
def
|
21
|
-
"#{
|
29
|
+
def fs_path_for(filename)
|
30
|
+
File.join(resource_dir,"phantom_#{filename}")
|
22
31
|
end
|
23
32
|
|
24
|
-
def
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
def js_file
|
29
|
-
File.join(File.dirname(__FILE__),"shutterbug.js")
|
33
|
+
def url_prefix
|
34
|
+
"#{uri_prefix}#{path_prefix}"
|
30
35
|
end
|
31
36
|
|
32
37
|
def convert_path
|
33
|
-
"#{
|
34
|
-
end
|
35
|
-
def convert_regex
|
36
|
-
/#{path_prefix}\/make_snapshot/
|
38
|
+
"#{url_prefix}/make_snapshot"
|
37
39
|
end
|
38
40
|
|
39
|
-
def
|
40
|
-
"#{
|
41
|
-
end
|
42
|
-
def png_regex
|
43
|
-
/#{path_prefix}\/get_png\/([^\/]+)/
|
41
|
+
def base_url(req)
|
42
|
+
req.POST()['base_url'] || req.referrer || "#{req.scheme}://#{req.host_with_port}"
|
44
43
|
end
|
45
44
|
|
46
|
-
def
|
47
|
-
|
48
|
-
end
|
49
|
-
def html_regex
|
50
|
-
/#{path_prefix}\/get_html\/([^\/]+)/
|
45
|
+
def storage
|
46
|
+
use_s3? ? Storage::S3Storage : Storage::FileStorage
|
51
47
|
end
|
52
48
|
|
53
|
-
def
|
54
|
-
|
49
|
+
def use_s3?
|
50
|
+
return (self.s3_bin && self.s3_key && self.s3_secret)
|
55
51
|
end
|
56
52
|
end
|
57
53
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
module Shutterbug
|
3
|
+
module Handlers
|
4
|
+
class ConvertHandler
|
5
|
+
|
6
|
+
def initialize(_config = Configuration.instance)
|
7
|
+
@config = _config
|
8
|
+
end
|
9
|
+
|
10
|
+
def regex
|
11
|
+
/#{@config.path_prefix}\/make_snapshot/
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle(helper, req, env)
|
15
|
+
response_text = convert(req).image_tag
|
16
|
+
helper.good_response(response_text,'text/plain')
|
17
|
+
end
|
18
|
+
|
19
|
+
def convert(req)
|
20
|
+
html = req.POST()['content'] || ""
|
21
|
+
width = req.POST()['width'] || 1000
|
22
|
+
height = req.POST()['height'] || 700
|
23
|
+
css = req.POST()['css'] || ""
|
24
|
+
job = PhantomJob.new(@config.base_url(req), html, css, width, height)
|
25
|
+
unless (cache_entry = @config.cache_manager.find(job.cache_key))
|
26
|
+
job.rasterize
|
27
|
+
html_entry = Shutterbug::CacheManager::CacheEntry.new(job.html_file)
|
28
|
+
png_entry = Shutterbug::CacheManager::CacheEntry.new(job.png_file)
|
29
|
+
html_entry.preview_url = png_entry.preview_url
|
30
|
+
@config.cache_manager.add_entry(html_entry)
|
31
|
+
cache_entry = @config.cache_manager.add_entry(png_entry)
|
32
|
+
end
|
33
|
+
# return the image tag
|
34
|
+
return cache_entry
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
module Shutterbug
|
3
|
+
module Handlers
|
4
|
+
module FileHandlers
|
5
|
+
class Base
|
6
|
+
attr_accessor :config
|
7
|
+
|
8
|
+
def self.instance
|
9
|
+
return @instance || self.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(_config = Configuration.instance)
|
13
|
+
self.config = _config
|
14
|
+
end
|
15
|
+
|
16
|
+
def urlify(name)
|
17
|
+
"#{self.path_prefix}/#{name}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def path_prefix
|
21
|
+
"#{self.config.path_prefix}/get_#{file_extension}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def filename_matcher
|
25
|
+
"(([^\/|\.]+)\.?([^\/]+))?"
|
26
|
+
end
|
27
|
+
|
28
|
+
def regex
|
29
|
+
/#{path_prefix}\/#{filename_matcher}/
|
30
|
+
end
|
31
|
+
|
32
|
+
def filename(base)
|
33
|
+
"#{base}.#{file_extension}"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Shutterbug
|
2
|
+
module Handlers
|
3
|
+
module FileHandlers
|
4
|
+
class HtmlFile < Base
|
5
|
+
|
6
|
+
def file_extension
|
7
|
+
"html"
|
8
|
+
end
|
9
|
+
|
10
|
+
def mime_type
|
11
|
+
"text/html"
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle(helper, req, env)
|
15
|
+
sha = regex.match(req.path)[1]
|
16
|
+
file = @config.storage.new(filename(sha),self)
|
17
|
+
helper.good_response(file.get_content, self.mime_type)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Shutterbug
|
2
|
+
module Handlers
|
3
|
+
module FileHandlers
|
4
|
+
class PngFile < Base
|
5
|
+
|
6
|
+
def file_extension
|
7
|
+
"png"
|
8
|
+
end
|
9
|
+
|
10
|
+
def mime_type
|
11
|
+
"image/png"
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle(helper, req, env)
|
15
|
+
local_filename = regex.match(req.path)[1]
|
16
|
+
file = @config.storage.new(local_filename,self)
|
17
|
+
helper.good_response(file.get_content, self.mime_type)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Shutterbug
|
2
|
+
module Handlers
|
3
|
+
class JsFileHandler
|
4
|
+
|
5
|
+
def self.js_path
|
6
|
+
"#{Configuration.instance.url_prefix}/shutterbug.js"
|
7
|
+
end
|
8
|
+
|
9
|
+
def regex
|
10
|
+
/#{@config.path_prefix}\/shutterbug.js/
|
11
|
+
end
|
12
|
+
|
13
|
+
def js_file
|
14
|
+
File.join(File.dirname(__FILE__),"shutterbug.js")
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(_config=Configuration.instance())
|
18
|
+
@config = _config
|
19
|
+
@javascript = File.read(js_file).gsub(/CONVERT_PATH/,@config.convert_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def handle(helper, req, env)
|
23
|
+
helper.good_response(@javascript, 'application/javascript')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
/*global $ */
|
2
2
|
(function(){
|
3
|
+
var $ = window.$;
|
3
4
|
|
4
5
|
var getBaseUrl = function() {
|
5
6
|
var base = window.location.href;
|
@@ -72,17 +73,31 @@
|
|
72
73
|
}
|
73
74
|
}
|
74
75
|
var self = this;
|
76
|
+
var time = 0;
|
77
|
+
var counter = $("<span>");
|
78
|
+
counter.html(time);
|
79
|
+
|
80
|
+
$(self.imgDst).html("creating snapshot: ").append(counter);
|
81
|
+
var timer = setInterval(function(t) {
|
82
|
+
time = time + 1;
|
83
|
+
counter.html(time);
|
84
|
+
}, 1000);
|
85
|
+
|
75
86
|
$.ajax({
|
76
87
|
url: "CONVERT_PATH",
|
77
88
|
type: "POST",
|
78
89
|
data: html
|
79
|
-
}).
|
90
|
+
}).success(function(msg) {
|
80
91
|
if(self.imgDst) {
|
81
92
|
$(self.imgDst).html(msg);
|
82
93
|
}
|
83
94
|
if (self.callback) {
|
84
95
|
self.callback(msg);
|
85
96
|
}
|
97
|
+
clearInterval(timer);
|
98
|
+
}).fail(function(e) {
|
99
|
+
$(self.imgDst).html("snapshot failed");
|
100
|
+
clearInterval(timer);
|
86
101
|
});
|
87
102
|
};
|
88
103
|
|
@@ -95,7 +110,16 @@
|
|
95
110
|
destination.postMessage(JSON.stringify(message),"*");
|
96
111
|
};
|
97
112
|
|
98
|
-
window.Shutterbug = function(selector,imgDst,callback,id) {
|
113
|
+
window.Shutterbug = function(selector,imgDst,callback,id,jQuery) {
|
114
|
+
if (typeof(jQuery) != "undefined" && jQuery != null) {
|
115
|
+
$ = jQuery;
|
116
|
+
}
|
117
|
+
// If we still don't have a valid jQuery, try setting it from the global jQuery default.
|
118
|
+
// This can happen if shutterbug.js is included before jquery.js
|
119
|
+
if ((typeof($) == "undefined" || $ == null) && typeof(window.$) != "undefined" && window.$ != null) {
|
120
|
+
$ = window.$;
|
121
|
+
}
|
122
|
+
|
99
123
|
var shutterbugInstance = {
|
100
124
|
element: selector,
|
101
125
|
imgDst: imgDst,
|
@@ -150,5 +174,4 @@
|
|
150
174
|
});
|
151
175
|
return shutterbugInstance;
|
152
176
|
};
|
153
|
-
|
154
177
|
})();
|
@@ -5,7 +5,7 @@ module Shutterbug
|
|
5
5
|
attr_accessor :html_file
|
6
6
|
|
7
7
|
def program
|
8
|
-
|
8
|
+
@config.phantom_bin_path
|
9
9
|
end
|
10
10
|
|
11
11
|
def rasterize_js
|
@@ -18,10 +18,11 @@ module Shutterbug
|
|
18
18
|
@css = css
|
19
19
|
@width = width
|
20
20
|
@height = height
|
21
|
+
@config = Configuration.instance
|
21
22
|
end
|
22
23
|
|
23
24
|
def cache_key
|
24
|
-
|
25
|
+
@cache_key ||= Digest::SHA1.hexdigest("#{@html}#{@css}#{@base_url}")[0..10]
|
25
26
|
end
|
26
27
|
|
27
28
|
def document
|
@@ -42,29 +43,33 @@ module Shutterbug
|
|
42
43
|
"""
|
43
44
|
end
|
44
45
|
|
45
|
-
def
|
46
|
-
|
46
|
+
def html_file_name
|
47
|
+
"#{cache_key}.html"
|
47
48
|
end
|
48
49
|
|
49
|
-
def
|
50
|
-
|
50
|
+
def png_file_name
|
51
|
+
"#{cache_key}.png"
|
51
52
|
end
|
52
53
|
|
53
|
-
def
|
54
|
-
|
54
|
+
def input_path
|
55
|
+
@config.fs_path_for(html_file_name)
|
56
|
+
end
|
57
|
+
|
58
|
+
def output_path
|
59
|
+
@config.fs_path_for(png_file_name)
|
55
60
|
end
|
56
61
|
|
57
62
|
def rasterize_cl
|
58
|
-
%x[#{self.program} #{self.rasterize_js} #{self.
|
63
|
+
%x[#{self.program} #{self.rasterize_js} #{self.input_path} #{self.output_path} #{@width}*#{@height}]
|
59
64
|
end
|
60
65
|
|
61
66
|
def rasterize
|
62
|
-
File.open(
|
67
|
+
File.open(input_path, 'w') do |f|
|
63
68
|
f.write(document)
|
64
69
|
end
|
65
70
|
rasterize_cl()
|
66
|
-
self.png_file = PngFile.new
|
67
|
-
self.html_file = HtmlFile.new
|
71
|
+
self.png_file = @config.storage.new(png_file_name, Shutterbug::Handlers::FileHandlers::PngFile.new)
|
72
|
+
self.html_file = @config.storage.new(html_file_name, Shutterbug::Handlers::FileHandlers::HtmlFile.new)
|
68
73
|
end
|
69
74
|
end
|
70
75
|
end
|