phrender 0.0.1 → 0.0.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.
- checksums.yaml +4 -4
- data/README.md +35 -21
- data/lib/phrender/ember_driver.rb +1 -1
- data/lib/phrender/phantom_js_engine.rb +23 -11
- data/lib/phrender/phantom_js_session.rb +6 -3
- data/lib/phrender/rack_middleware.rb +41 -23
- data/lib/phrender/rack_static.rb +6 -39
- data/lib/phrender/support/ember_driver.js +0 -1
- data/lib/phrender/support/phantom_bridge.js +5 -6
- data/lib/phrender/version.rb +1 -1
- data/lib/phrender.rb +1 -1
- data/spec/phrender/phantom_js_engine_spec.rb +0 -10
- data/spec/phrender/rack_middleware_spec.rb +13 -5
- data/spec/phrender/rack_static_spec.rb +10 -5
- metadata +2 -3
- data/lib/phrender/rack_base.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed3ad1d4b44735e82d185bca486adb853fb15fba
|
4
|
+
data.tar.gz: 1f37c26795b0cacf96926f1a3ebae75f41b15f4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b1dbdae233a0fb2754378696a7757beee1c2ee197cc5aa5a0dac36be76be5b867d9649688c24c23f84b80dbf9a1ea269dc7b113085c1b0fd09459721c65b7ee
|
7
|
+
data.tar.gz: 42bbbaac4dd4aedae6504f5c972b02794dab825a368e00ec8b88accb65a6cc667dbb87ed2ac29ca9fcbde96c341d34a16ad0b96f9f71602272695ba5861da262
|
data/README.md
CHANGED
@@ -22,8 +22,8 @@ Then install PhantomJS:
|
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
|
-
*NOTE*: Ember users, there is a driver for ember. See the [Ember Driver](#
|
26
|
-
|
25
|
+
*NOTE*: Ember users, there is a driver for ember. See the [Ember Driver](#ember-driver)
|
26
|
+
section.
|
27
27
|
|
28
28
|
### For completely static javascript sites:
|
29
29
|
|
@@ -32,23 +32,33 @@ In your rackup file:
|
|
32
32
|
```ruby
|
33
33
|
require 'phrender'
|
34
34
|
|
35
|
-
#
|
36
|
-
#
|
37
|
-
|
38
|
-
phrender = Phrender::RackStatic.new("asset_root", options)
|
35
|
+
# The asset root should reflect where your app is stored, as well as all images,
|
36
|
+
# fonts, css, and other static files.
|
37
|
+
asset_root = "./"
|
39
38
|
|
40
39
|
# The index file should link to no javascript on its own, or include any script
|
41
40
|
# tags.
|
42
|
-
|
41
|
+
index_file = 'phrender.html'
|
43
42
|
|
44
43
|
# This is the main application. It may or may not also start up the app.
|
45
|
-
|
44
|
+
javascript_files = [ '/assets/application.js' ]
|
46
45
|
|
47
46
|
# This is just raw javascript. Typically the app boot code.
|
48
|
-
|
47
|
+
javascript = [ "MyApp.boot()" ]
|
48
|
+
|
49
|
+
# Javascript files and raw javascript get concatenated. Files first, then raw
|
50
|
+
# code in array order. This code is then run against the index file using
|
51
|
+
# PhantomJS
|
49
52
|
|
50
53
|
# Rackup!
|
51
|
-
run
|
54
|
+
run Phrender::RackStatic.new({
|
55
|
+
:asset_root => asset_root,
|
56
|
+
:index_file => index_file,
|
57
|
+
:javascript_files => javascript_files,
|
58
|
+
:javascript => javascript
|
59
|
+
})
|
60
|
+
# Additionally, you map pass the option :timeout, which gets passed on to
|
61
|
+
# PhantomJS.
|
52
62
|
```
|
53
63
|
|
54
64
|
### For partially dynamic javascript sites:
|
@@ -63,23 +73,27 @@ In your rackup file:
|
|
63
73
|
require 'phrender'
|
64
74
|
require 'my_rack_app'
|
65
75
|
|
66
|
-
#
|
67
|
-
|
68
|
-
|
69
|
-
|
76
|
+
# These options are the same as above, but instead of referencing actual files,
|
77
|
+
# the paths are the request paths to send to the upstream application server.
|
78
|
+
index_file = 'phrender.html'
|
79
|
+
javascript_files = [ '/assets/application.js' ]
|
80
|
+
javascript = [ "MyApp.boot()" ]
|
81
|
+
|
82
|
+
use Phrender::RackMiddleware, {
|
83
|
+
:index_file => index_file,
|
84
|
+
:javascript_files => javascript_files,
|
85
|
+
:javascript => javascript
|
86
|
+
}
|
87
|
+
# Additional options are timeout and ssl, if, for some reason, your rack app is
|
70
88
|
# serving up SSL encrypted pages. Run `phantomjs --help` to see available
|
71
89
|
# options, or use something falsey to disable. Both options are passed to
|
72
90
|
# phantom.
|
73
|
-
phrender = Phrender::RackMiddleware.new(app, options)
|
74
91
|
|
75
|
-
#
|
76
|
-
|
77
|
-
phrender.index_file 'phrender.html'
|
78
|
-
phrender.add_javascript_file '/assets/application.js'
|
79
|
-
phrender.add_javascript "MyApp.boot()"
|
92
|
+
# Include other middlewarn
|
93
|
+
use Rack::Static, :urls => "/assets", :root => "./assets"
|
80
94
|
|
81
95
|
# Rackup!
|
82
|
-
run
|
96
|
+
run MyRackApp.new
|
83
97
|
```
|
84
98
|
|
85
99
|
## Signalling Render Completion
|
@@ -3,6 +3,7 @@ require 'phrender/phantom_js_session'
|
|
3
3
|
|
4
4
|
require 'open3'
|
5
5
|
require 'multi_json'
|
6
|
+
require 'tempfile'
|
6
7
|
|
7
8
|
class Phrender::PhantomJSEngine
|
8
9
|
|
@@ -28,8 +29,17 @@ class Phrender::PhantomJSEngine
|
|
28
29
|
end
|
29
30
|
|
30
31
|
def render(html, javascript, url = nil)
|
31
|
-
|
32
|
-
|
32
|
+
javascript_file = make_temp_file(javascript, 'file.js')
|
33
|
+
html_file = make_temp_file(html, 'file.html')
|
34
|
+
program_options = { :html => html_file.path,
|
35
|
+
:javascript => javascript_file.path,
|
36
|
+
:url => url,
|
37
|
+
:timeout => @timeout * 1000.0 }
|
38
|
+
|
39
|
+
session = Phrender::PhantomJSSession.new @boot_cmd.join(' '), @timeout
|
40
|
+
|
41
|
+
session.stdin.puts MultiJson.dump(program_options)
|
42
|
+
session.stdin.close
|
33
43
|
|
34
44
|
begin
|
35
45
|
sleep @poll_interval
|
@@ -39,6 +49,10 @@ class Phrender::PhantomJSEngine
|
|
39
49
|
# Clean up phantom
|
40
50
|
session.shutdown
|
41
51
|
|
52
|
+
# Clean up temp files
|
53
|
+
javascript_file.unlink
|
54
|
+
html_file.unlink
|
55
|
+
|
42
56
|
# Feed something out the chain
|
43
57
|
if session.rendered
|
44
58
|
session.page
|
@@ -51,17 +65,15 @@ class Phrender::PhantomJSEngine
|
|
51
65
|
end
|
52
66
|
end
|
53
67
|
|
54
|
-
def app_cmd(html, javascript, url)
|
55
|
-
program_options = { :html => html,
|
56
|
-
:javascript => javascript,
|
57
|
-
:url => url,
|
58
|
-
:timeout => @timeout }
|
59
|
-
encoded_options = MultiJson.dump(MultiJson.dump(program_options))
|
60
|
-
"%s %s" % [ @boot_cmd.join(' '), encoded_options ]
|
61
|
-
end
|
62
|
-
|
63
68
|
protected
|
64
69
|
|
70
|
+
def make_temp_file(data, name)
|
71
|
+
file = Tempfile.new(name)
|
72
|
+
file.write(data)
|
73
|
+
file.close
|
74
|
+
file
|
75
|
+
end
|
76
|
+
|
65
77
|
def parse_output(session)
|
66
78
|
output = session.stdout.gets
|
67
79
|
begin
|
@@ -18,9 +18,12 @@ class Phrender::PhantomJSSession
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def shutdown
|
21
|
-
@stdin.
|
22
|
-
|
23
|
-
|
21
|
+
[ @stdin, @stdout, @stderr ].each do |pipe|
|
22
|
+
begin
|
23
|
+
pipe.close
|
24
|
+
rescue IOError
|
25
|
+
end
|
26
|
+
end
|
24
27
|
begin
|
25
28
|
Process.kill("TERM", @wait_thr.pid)
|
26
29
|
rescue Errno::ESRCH
|
@@ -1,41 +1,49 @@
|
|
1
1
|
require 'phrender/phantom_js_engine'
|
2
|
-
require 'phrender/rack_base'
|
3
2
|
|
4
3
|
require 'rack'
|
5
4
|
|
6
|
-
class Phrender::RackMiddleware
|
7
|
-
def initialize(
|
5
|
+
class Phrender::RackMiddleware
|
6
|
+
def initialize(app, opts = {})
|
7
|
+
@app = app
|
8
|
+
@index_file = opts[:index_file]
|
9
|
+
@javascript_paths = opts[:javascript_files]
|
10
|
+
@raw_javascript = opts[:javascript].join(';')
|
8
11
|
@phantom = Phrender::PhantomJSEngine.new(opts)
|
9
|
-
@backend = backend
|
10
|
-
super
|
11
12
|
end
|
12
13
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
def call(env)
|
15
|
+
status, headers, body = @app.call(env)
|
16
|
+
if (status == 404 || headers['Content-Type'] == 'text/html')
|
17
|
+
if (env['HTTP_USER_AGENT'].match(/PhantomJS/))
|
18
|
+
[ 500, { 'Content-Type' => 'text/html' }, [
|
19
|
+
'Server Error: HTML file contains recursive lookup' ] ]
|
20
|
+
else
|
21
|
+
body = render(env['REQUEST_URI'])
|
22
|
+
[ 200, { 'Content-Type' => 'text/html' }, [ body ] ]
|
23
|
+
end
|
24
|
+
else
|
25
|
+
[ status, headers, body ]
|
18
26
|
end
|
19
27
|
end
|
20
28
|
|
21
|
-
def render(path, app)
|
22
|
-
program = load_js(app)
|
23
|
-
html = load_html(app)
|
24
|
-
@phantom.render(html, program)
|
25
|
-
end
|
26
|
-
|
27
29
|
protected
|
28
30
|
|
29
|
-
def
|
31
|
+
def render(request_uri)
|
32
|
+
program = load_js
|
33
|
+
html = load_html
|
34
|
+
@phantom.render(html, program, request_uri)
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_html
|
30
38
|
req = Rack::MockRequest.env_for('',
|
31
39
|
'PATH_INFO' => @index_file,
|
32
40
|
'REQUEST_METHOD' => 'GET'
|
33
41
|
)
|
34
|
-
status, headers, body = app.call(req)
|
35
|
-
body
|
42
|
+
status, headers, body = @app.call(req)
|
43
|
+
parse_body body
|
36
44
|
end
|
37
45
|
|
38
|
-
def load_js
|
46
|
+
def load_js
|
39
47
|
js_from_files = @javascript_paths.map do |path|
|
40
48
|
if path == :ember_driver
|
41
49
|
Phrender::EMBER_DRIVER
|
@@ -44,12 +52,22 @@ class Phrender::RackMiddleware < Phrender::RackBase
|
|
44
52
|
'PATH_INFO' => path,
|
45
53
|
'REQUEST_METHOD' => 'GET'
|
46
54
|
)
|
47
|
-
status, headers, body = app.call(req)
|
48
|
-
body
|
55
|
+
status, headers, body = @app.call(req)
|
56
|
+
parse_body body
|
49
57
|
end
|
50
58
|
end.join(';')
|
51
59
|
program = js_from_files + @raw_javascript
|
52
|
-
program
|
60
|
+
program.to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_body(body)
|
64
|
+
if body.respond_to? :each
|
65
|
+
data = ''
|
66
|
+
body.each{ |part| data << part }
|
67
|
+
data
|
68
|
+
else
|
69
|
+
body.to_s
|
70
|
+
end
|
53
71
|
end
|
54
72
|
|
55
73
|
end
|
data/lib/phrender/rack_static.rb
CHANGED
@@ -1,45 +1,12 @@
|
|
1
1
|
require 'phrender/phantom_js_engine'
|
2
|
-
require 'phrender/
|
2
|
+
require 'phrender/rack_middleware'
|
3
3
|
|
4
4
|
require 'rack'
|
5
5
|
|
6
|
-
class Phrender::RackStatic < Phrender::
|
7
|
-
def initialize(
|
8
|
-
|
9
|
-
|
10
|
-
super
|
6
|
+
class Phrender::RackStatic < Phrender::RackMiddleware
|
7
|
+
def initialize(opts = {})
|
8
|
+
asset_root = opts.delete :asset_root
|
9
|
+
app = Rack::File.new(asset_root)
|
10
|
+
super(app, opts)
|
11
11
|
end
|
12
|
-
|
13
|
-
def rack_app
|
14
|
-
static_directory = @root_directory
|
15
|
-
@app ||= Rack::Builder.new do
|
16
|
-
use Proxy
|
17
|
-
run Rack::File.new(static_directory)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def render(path, app)
|
22
|
-
program = load_js(app)
|
23
|
-
html = load_html(app)
|
24
|
-
@phantom.render(html, program)
|
25
|
-
end
|
26
|
-
|
27
|
-
protected
|
28
|
-
|
29
|
-
def load_html(app)
|
30
|
-
File.read File.join(@root_directory, @index_file)
|
31
|
-
end
|
32
|
-
|
33
|
-
def load_js(app)
|
34
|
-
js_from_files = @javascript_paths.map do |path|
|
35
|
-
if path == :ember_driver
|
36
|
-
Phrender::EMBER_DRIVER
|
37
|
-
else
|
38
|
-
File.read File.join(@root_directory, path)
|
39
|
-
end
|
40
|
-
end.join(';')
|
41
|
-
program = js_from_files + @raw_javascript
|
42
|
-
program
|
43
|
-
end
|
44
|
-
|
45
12
|
end
|
@@ -6,13 +6,13 @@ var system = require('system'),
|
|
6
6
|
var stdout = system.stdout;
|
7
7
|
|
8
8
|
// global opts
|
9
|
-
var options = JSON.parse(
|
9
|
+
var options = JSON.parse(fs.read("/dev/stdin")),
|
10
10
|
globals = {
|
11
11
|
"page": null,
|
12
12
|
"timer": null,
|
13
13
|
"expired": false,
|
14
14
|
"rendered": false,
|
15
|
-
"html": options.html
|
15
|
+
"html": fs.read(options.html)
|
16
16
|
};
|
17
17
|
|
18
18
|
// functions
|
@@ -61,8 +61,9 @@ printPage = function(url) {
|
|
61
61
|
// Log javascript console messages
|
62
62
|
globals.page.onConsoleMessage = function(msg) {
|
63
63
|
if (msg.trim() === "-- PHRENDER COMPLETE --"){
|
64
|
+
logMessage('phrender complete');
|
64
65
|
globals.rendered = true;
|
65
|
-
} else {
|
66
|
+
} else if (!msg.match(/^\[✓\].+/)) {
|
66
67
|
logMessage(msg);
|
67
68
|
}
|
68
69
|
};
|
@@ -70,9 +71,7 @@ printPage = function(url) {
|
|
70
71
|
// capture errors
|
71
72
|
globals.page.onError = logError;
|
72
73
|
|
73
|
-
globals.page.
|
74
|
-
eval(code);
|
75
|
-
}, options.javascript);
|
74
|
+
globals.page.injectJs(options.javascript);
|
76
75
|
|
77
76
|
// Catch something
|
78
77
|
globals.timer = setTimeout(writeHtml, options.timeout);
|
data/lib/phrender/version.rb
CHANGED
data/lib/phrender.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require "phrender/version"
|
2
2
|
require "phrender/logger"
|
3
|
+
require "phrender/ember_driver"
|
3
4
|
require "phrender/phantom_js_engine"
|
4
5
|
require "phrender/phantom_js_session"
|
5
|
-
require "phrender/rack_base"
|
6
6
|
require "phrender/rack_middleware"
|
7
7
|
require "phrender/rack_static"
|
8
8
|
|
@@ -14,16 +14,6 @@ describe 'Phrender::PhantomJSEngine' do
|
|
14
14
|
File.read(File.expand_path('../phantom_js_engine/app.js', __FILE__))
|
15
15
|
}
|
16
16
|
|
17
|
-
it 'generates a startup command with escaped json' do
|
18
|
-
command = phantom.app_cmd(index, app, 'http://localhost')
|
19
|
-
expect(command).to match(
|
20
|
-
/phantomjs (.+?)phrender\/lib\/phrender\/support\/phantom_bridge.js/
|
21
|
-
)
|
22
|
-
expect(command).to include("--ignore-ssl-errors=true")
|
23
|
-
expect(command).to include("<html>")
|
24
|
-
expect(command).to include("use strict")
|
25
|
-
end
|
26
|
-
|
27
17
|
it 'renders a simple page' do
|
28
18
|
whitespace_regex = /(\n|^ +)/
|
29
19
|
html = <<-HTML.strip_heredoc.gsub(whitespace_regex, '')
|
@@ -10,11 +10,19 @@ describe 'Phrender::RackMiddleware' do
|
|
10
10
|
b
|
11
11
|
}
|
12
12
|
let(:app) {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
_backend = backend # Needed because builder changes the block's context
|
14
|
+
Rack::Builder.new do
|
15
|
+
use Phrender::RackMiddleware, {
|
16
|
+
:index_file => 'phrender.html',
|
17
|
+
:javascript_files => [
|
18
|
+
'app.js'
|
19
|
+
],
|
20
|
+
:javascript => [
|
21
|
+
"App.run()"
|
22
|
+
]
|
23
|
+
}
|
24
|
+
run _backend
|
25
|
+
end
|
18
26
|
}
|
19
27
|
|
20
28
|
it 'runs the app contained in the referenced assets' do
|
@@ -4,11 +4,16 @@ require 'phrender/rack_static'
|
|
4
4
|
describe 'Phrender::RackStatic' do
|
5
5
|
let(:root) { File.expand_path '../rack_static', __FILE__ }
|
6
6
|
let(:app) {
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
Phrender::RackStatic.new({
|
8
|
+
:asset_root => root,
|
9
|
+
:index_file => 'phrender.html',
|
10
|
+
:javascript_files => [
|
11
|
+
'app.js'
|
12
|
+
],
|
13
|
+
:javascript => [
|
14
|
+
"App.run()"
|
15
|
+
]
|
16
|
+
})
|
12
17
|
}
|
13
18
|
|
14
19
|
it 'runs the app contained in the referenced assets' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phrender
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- M Smart, theScore Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -153,7 +153,6 @@ files:
|
|
153
153
|
- lib/phrender/logger.rb
|
154
154
|
- lib/phrender/phantom_js_engine.rb
|
155
155
|
- lib/phrender/phantom_js_session.rb
|
156
|
-
- lib/phrender/rack_base.rb
|
157
156
|
- lib/phrender/rack_middleware.rb
|
158
157
|
- lib/phrender/rack_static.rb
|
159
158
|
- lib/phrender/support/ember_driver.js
|
data/lib/phrender/rack_base.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
class Phrender::RackBase
|
2
|
-
class Proxy
|
3
|
-
class << self
|
4
|
-
attr_accessor :host
|
5
|
-
end
|
6
|
-
|
7
|
-
def initialize(app)
|
8
|
-
@app = app
|
9
|
-
end
|
10
|
-
|
11
|
-
def call(env)
|
12
|
-
self.class.host.call(env, @app)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
attr_accessor :index_file
|
17
|
-
|
18
|
-
def initialize(*args)
|
19
|
-
@javascript_paths = []
|
20
|
-
@raw_javascript = ''
|
21
|
-
Proxy.host = self
|
22
|
-
end
|
23
|
-
|
24
|
-
def rack_app
|
25
|
-
raise NotImplementedError
|
26
|
-
end
|
27
|
-
|
28
|
-
def render(path, app)
|
29
|
-
raise NotImplementedError
|
30
|
-
end
|
31
|
-
|
32
|
-
def call(env, app)
|
33
|
-
status, headers, body = app.call(env)
|
34
|
-
if status == 404
|
35
|
-
body = render(env['PATH_INFO'], app)
|
36
|
-
[ 200, { 'Content-Type' => 'text/html' }, body ]
|
37
|
-
else
|
38
|
-
[ status, headers, body ]
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def add_javascript_file(path)
|
43
|
-
@javascript_paths.push path
|
44
|
-
end
|
45
|
-
|
46
|
-
def add_javascript(code)
|
47
|
-
@raw_javascript << ';' + code
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|