Skip to content

Instantly share code, notes, and snippets.

@jondkinney
Created March 14, 2013 20:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jondkinney/5164703 to your computer and use it in GitHub Desktop.
Save jondkinney/5164703 to your computer and use it in GitHub Desktop.
require 'rack/utils'
require 'nokogiri'
class HtmlDemo
include Rack::Utils
def initialize(app, opts = {})
@app = app
@opts = {
:element_id => 'page',
:local_save_dir => 'public/shots',
:public_save_dir => 'public/demos'
}
@opts.merge! opts
end
def call(env)
status, headers, response = @app.call(env)
headers = HeaderHash.new(headers)
if !STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
!headers['transfer-encoding'] &&
headers['content-type'] &&
headers['content-type'].include?('text/html')
#&& headers['record-demo'] == 'true'
content = ''
response.each { |part| content += part }
doc = Nokogiri::HTML(content, nil, 'UTF-8')
body = doc.to_html
user_id = env['rack.session']['user_id']
# removing this for now so that we can have demos span across users
#if user_id
# current_user = User.find(user_id)
# save_dir = "#{@opts[:local_save_dir]}/user_#{current_user.id}"
#else
save_dir = "#{@opts[:local_save_dir]}"
#end
response_params = env['action_dispatch.request.parameters']
if response_params
if response_params['record'] && response_params['record'] == 'true'
# ensure directory exists
FileUtils.mkdir_p("#{save_dir}")
files = Dir.entries("#{save_dir}") - ['.','..']
file_number = files.length + 1
if file_number < 10
file_number = "0#{file_number}"
end
filename = "#{file_number}_#{response_params['controller'].gsub(/\//,'_')}_#{response_params['action']}.html"
# write html template file
File.open("#{save_dir}/#{filename}", 'w') { |f| f.write(body) }
elsif response_params['new_demo'] && response_params['new_demo'] == 'true' && user_id
FileUtils.rm_r(Dir.glob("#{save_dir}/*")) unless Dir["#{save_dir}"].empty?
end
if demo_name = response_params['new_html_demo_name']
demo_dir = "#{@opts[:public_save_dir]}/#{demo_name}"
FileUtils.mkdir_p(demo_dir)
FileUtils.cp_r("#{save_dir}/.", demo_dir)
files = Dir.entries(demo_dir) - ['.','..']
sorted = files.sort { |a,b| a.split('.')[0].split('_')[0] <=> b.split('.')[0].split('_')[0] }
sorted.each_with_index do |file,index|
if index+1 < sorted.length
next_filename = sorted[index+1]
else
next_filename = '#'
end
full_file = "#{demo_dir}/#{file}"
doc = Nokogiri::HTML(open(full_file), nil, 'utf-8')
#Remove elements we don't want in the HTML demos
doc.css('div#html_demo_controls').remove
doc.css('div#google_analytics').remove
doc.css('div#get_clicky_user_voice').remove
doc.css('div#footer_version').remove
doc.css('#html_demo_controls_handle').remove
# Add a starting html page with a link to the first file so we can
# just refer to the demo by name
if index == 0
starting_html_page = doc.dup
node = starting_html_page.css('div#content > .wrapper').first
node.children.remove
link_node = Nokogiri::XML::Node.new('a',starting_html_page)
link_node['href'] = "#{file}"
link_node['class'] = 'button orange'
link_node.content = 'Start Demo'
node.add_child(link_node)
node['style'] = 'margin:50px auto;width:116px;'
File.open("#{demo_dir}/index.html", 'w') { |f| f.write(starting_html_page.to_html) }
end
elements = doc.css('form') + doc.css('a')
leave_elements = doc.css('.firm_modal_click') + doc.css('.cancel_modal')
elements = elements - leave_elements
elements.each do |element|
if element.attributes['action']
element.attributes['action'].value = next_filename
elsif element.attributes['href']
if next_filename == '#'
element['data-prevent-default'] = 'true'
end
element.attributes['href'].value = next_filename
end
# ensure a get request
if element.attributes['method']
element.attributes['method'].value = 'get'
end
if element.attributes['data-method']
element.attributes['data-method'].remove
end
end
# remove iframes since we will want to replace them with our own custom content (a real screenshot most likely)
if doc.css('iframe').first
doc.css('iframe').first['src'] = '/docusign_placeholder.html'
end
# Give us back the Bolstr logo as a link back to the real application
if logo = doc.css('h1#logo_backlink > a').first
logo['href'] = '/'
logo['data-prevent-default'] = 'false'
logo['data-no-turbolink'] = 'true'
end
# adjust the stylesheet and javascript tags to point to the non
# minified/gzipped/asset pipelined version so that between
# deploys on staging the demos saved to the shared folder will
# continue to work
doc.css('.app_stylesheet').each_with_index do |element, i|
if i > 0
element.remove
else
if element.attributes['href']
element.attributes['href'].value = '/assets/application.css'
end
end
end
doc.css('.app_javascript').each_with_index do |element, i|
if i > 0
element.remove
else
if element.attributes['src']
element.attributes['src'].value = '/assets/application.js'
end
end
end
File.open(full_file, 'w') { |f| f.write(doc) }
end
[301, {'Location' => '/demos'}, self]
else #no demo name given, but a screenshot might have been taken
[status, headers, [body]]
end
else #no screenshot params passed
[status, headers, [body]]
end
else
[status, headers, response]
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment