Skip to content

Instantly share code, notes, and snippets.

@rescribet
Last active September 17, 2016 06:06
Show Gist options
  • Save rescribet/2c5d268f1f161c7be5c9 to your computer and use it in GitHub Desktop.
Save rescribet/2c5d268f1f161c7be5c9 to your computer and use it in GitHub Desktop.
A Turbolinks 5 drop-in for when permanent partials can't be lazy loaded (no-script requirement).
# A turbolinks 5 drop-in for when permanent partials can't be lazy loaded (no-script requirement).
#
# Whenever a static partial is really heavy, it is undesirable to render it on every/many request(s), so this render drop-in only renders the partial's content if the `Turbolinks-Referrer` header is *not* present.
#
# Usage:
# ```ruby
# render partial: 'navbar', permanent: true
# # => <div id="turbolinks_navbar" data-turbolinks-permanent="true">[Always rendered]</div> # Behaviour as described in the RailsConf '15 keynote, where HTML might be lazy loaded via JS
#
# render partial: 'navbar', permanent: 'navbar'
# # => <div id="navbar" data-turbolinks-permanent="true">[Always rendered]</div>
#
# render partial: 'navbar', permanent: {id: 'unrelated_id'}
# # => <div id="unrelated_id" data-turbolinks-permanent="true">[Always rendered]</div>
#
# render partial: 'navbar', permanent: {turbolinks_render: false}
# # => <div id="turbolinks_navbar" data-turbolinks-permanent="true">[Never rendered (server side)]</div>
#
# render partial: 'navbar', permanent: {prerender: true, turbolinks_render: false}
# # => <div id="turbolinks_navbar" data-turbolinks-permanent="true">[Rendered on only on non TL requests]</div>
# ```
module ActionView
class Base
# Turbolinks that doesn't rerenders permanent partials on turbolink requests.
# @overload turbolinks_render(options)
# @param [Hash] options
# @option options [String] :permanent The ID of the partial
# @option options [Hash] :permanent An options hash
# @option permanent [Hash] :id The id of the wrapper element
# @option permanent [Hash] :tag (:div) The element type of the wrapper tag
def turbolinks_render(*args, &block)
options = normalize_tl_options(*args)
return normal_render(*args, &block) unless options
if tl_should_render_for(options)
inner_content = normal_render *args, &block
end
if options.fetch(:use_wrapper, true)
content_tag options.fetch(:tag, :div),
inner_content,
options_for_renderer(options)
else
inner_content
end
end
alias_method :normal_render, :render
alias_method :render, :turbolinks_render
private
def normalize_tl_options(*args)
render_options = args.first
options = render_options.is_a?(Hash) && render_options.fetch(:permanent, nil)
return nil unless options.present?
if options.is_a?(Hash)
options.presence
else
{id: partial_tl_id(*args)}
end
end
# Defines the options that are passed to the turbolinks wrapper container
def options_for_renderer(options)
{
id: options.fetch(:id),
data: {turbolinks_permanent: true}
}.merge(options.fetch(:html_options, {}))
end
# Generates the partial's wrapper id based on
# @return [String] id The id passed as an option, or one based on the partial name
def partial_tl_id(*args)
permanent = args.first.fetch(:permanent, nil)
return nil if permanent.blank?
if permanent.is_a?(String)
permanent
elsif permanent.is_a?(Hash) && permanent.fetch(:id).present?
permanent.fetch(:id)
elsif permanent && args.first.fetch(:partial).present?
"turbolinks_#{args.first.fetch(:partial)}"
end
end
# Determines whether Turbolinks should render the partial's body
def tl_should_render_for(options)
if request.headers['Turbolinks-Referrer'].present?
options.fetch(:turbolinks_render, true)
else
options.fetch(:prerender, true)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment