-
Notifications
You must be signed in to change notification settings - Fork 157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Server-side template rendering with Handlebars & HandlebarsAssets. #103
Open
wideopenspaces
wants to merge
13
commits into
leshill:master
Choose a base branch
from
wideopenspaces:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 11 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
3f3a515
render templates server side for rails
deepak 045d43f
Bundled
variousauthors 6257ec6
Hard coded the solution
variousauthors aeb1552
Add controller extensions and Responder
wideopenspaces c320945
use Responder's reader methods...
wideopenspaces c2c7720
Change supported_formats to :js , :jst
wideopenspaces a8f9788
Merge remote-tracking branch 'upstream/master'
wideopenspaces 0d35efa
Restore conflicted bits
wideopenspaces f409063
Merge pull request #1 from wideopenspaces/adds_responder
wideopenspaces a26df8c
Merge latest 0.17 changes and rearrange a bit
wideopenspaces dd8c17c
Merge pull request #2 from wideopenspaces/tmp
wideopenspaces ecde7f5
Fix method docs
wideopenspaces 7716145
remove Gemfile.lock. Not sure where that came from
wideopenspaces File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
PATH | ||
remote: . | ||
specs: | ||
handlebars_assets (0.15) | ||
execjs (>= 1.2.9) | ||
multi_json | ||
sprockets (>= 2.0.3) | ||
tilt | ||
|
||
GEM | ||
remote: http://rubygems.org/ | ||
specs: | ||
columnize (0.3.6) | ||
debugger (1.6.5) | ||
columnize (>= 0.3.1) | ||
debugger-linecache (~> 1.2.0) | ||
debugger-ruby_core_source (~> 1.3.1) | ||
debugger-linecache (1.2.0) | ||
debugger-ruby_core_source (1.3.1) | ||
execjs (2.0.2) | ||
haml (4.0.5) | ||
tilt | ||
hike (1.2.3) | ||
json (1.7.7) | ||
multi_json (1.8.4) | ||
rack (1.5.2) | ||
rake (10.1.1) | ||
slim (2.0.2) | ||
temple (~> 0.6.6) | ||
tilt (>= 1.3.3, < 2.1) | ||
sprockets (2.11.0) | ||
hike (~> 1.2) | ||
multi_json (~> 1.0) | ||
rack (~> 1.0) | ||
tilt (~> 1.1, != 1.3.0) | ||
temple (0.6.7) | ||
tilt (1.4.1) | ||
|
||
PLATFORMS | ||
ruby | ||
|
||
DEPENDENCIES | ||
debugger | ||
haml | ||
handlebars_assets! | ||
json (~> 1.7.7) | ||
rake | ||
slim |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
require 'handlebars_assets' | ||
|
||
%w(responder controller handlers/handlebars extensions).each do |lib| | ||
require File.join('handlebars_assets', 'server', lib) | ||
end | ||
|
||
module HandlebarsAssets | ||
module Server | ||
extend self | ||
HANDLER = HandlebarsAssets::Server::Handlers::Handlebars | ||
|
||
def self.register_template_handlers | ||
Config.handlebars_extensions.each do |ext| | ||
ActionView::Template.register_template_handler(ext, HANDLER) | ||
end | ||
end | ||
|
||
def self.register_mime_types | ||
Config.handlebars_extensions.each do |ext| | ||
Mime::Type.register_alias 'text/html', ext.to_sym | ||
end | ||
end | ||
end | ||
end | ||
|
||
if defined?(Rails) | ||
::HandlebarsAssets::Server.register_template_handlers | ||
::HandlebarsAssets::Server.register_mime_types | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
module HandlebarsAssets | ||
module Server | ||
# Prepare a controller to use <code>respond_with</code> | ||
# to render a Handlebars template (for GET requests, currently) | ||
# | ||
# You must require 'handlebar_assets/server' | ||
# in your controller, as it is not loaded by default. | ||
# | ||
# require 'handlebars_assets/server' | ||
# | ||
# class MyApp < ActionController::Base | ||
# include HandlebarsAssets::Server::Controller | ||
# | ||
# respond_to :hbs # or :handlebars | ||
# | ||
# def show | ||
# @bike = Bike.find(params[:id]) | ||
# render_with(@bike) # Look ma, no 'to_hbs'! | ||
# end | ||
# end | ||
# | ||
# Your class must define #to_hbs (or #to_handlebars) | ||
# that returns a Hash of JSON-compatible objects. | ||
# | ||
# You can specify the location of your templates: | ||
# | ||
# handlebars_templates path: 'app/assets/...' | ||
# | ||
# -or- | ||
# | ||
# handlebars_templates paths: ['array', 'of', 'paths'] | ||
# | ||
# 'app/assets/javascripts/templates' is prepended on | ||
# include, so you do not need to add this line | ||
# if you keep your templates in that directory | ||
# | ||
# If you support other mime types (like JSON), in the same | ||
# controller, you'll need to give your template a 'js' or | ||
# 'jst' format in order to keep Rails from trying to use | ||
# the view for everything: | ||
# | ||
# show.js.hbs # *not* show.hbs or show.html.hbs | ||
# | ||
module Controller | ||
extend ActiveSupport::Concern | ||
|
||
DEFAULT_HBS_TEMPLATE_PATH = 'app/assets/javascripts/templates' | ||
|
||
included do | ||
self.responder = HandlebarsAssets::Server::Responder | ||
|
||
def self.handlebars_templates(options = {}) | ||
paths = options[:paths] || options[:path] || [] | ||
Array(paths).reverse_each { |p| prepend_view_path(p) } | ||
end | ||
|
||
handlebars_templates path: DEFAULT_HBS_TEMPLATE_PATH | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
module HandlebarsAssets | ||
module Server | ||
module Extensions | ||
def context_for(template, extra = '') | ||
tmpl = "var template = Handlebars.template(#{precompile(template)})" | ||
context = [runtime, extra, tmpl].join('; ') | ||
|
||
ExecJS.compile(context) | ||
end | ||
|
||
def render(template, *args) | ||
locals = args.last.is_a?(Hash) ? args.pop : {} | ||
extra = args.first.to_s | ||
context_for(template, extra).call('template', locals) | ||
end | ||
|
||
protected | ||
|
||
def runtime | ||
@runtime ||= runtime_path.read | ||
end | ||
|
||
def runtime_path | ||
@runtime_path ||= assets_path.join(HandlebarsAssets::Config.compiler_runtime) | ||
end | ||
end | ||
end | ||
end | ||
|
||
::HandlebarsAssets::Handlebars.send(:extend, HandlebarsAssets::Server::Extensions) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
module HandlebarsAssets | ||
module Server | ||
module Handlers | ||
class Handlebars | ||
def self.call(template) | ||
new.call(template) | ||
end | ||
|
||
def call(template) | ||
if template.locals.blank? | ||
"#{template.source.inspect}.html_safe" | ||
else | ||
<<-HBS | ||
variable_names = controller.instance_variable_names | ||
variable_names -= %w[@template] | ||
if controller.respond_to?(:protected_instance_variables) | ||
variable_names -= controller.protected_instance_variables | ||
end | ||
variable_names.reject! { |name| name.starts_with? '@_' } | ||
|
||
variables = variable_names.inject({}) { |acc,name| acc[name.sub(/^@/, "")] = controller.instance_variable_get(name); acc } | ||
variables.merge!(local_assigns) | ||
|
||
HandlebarsAssets::Handlebars.render(#{template.source.inspect}, variables).html_safe | ||
HBS | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
module HandlebarsAssets | ||
module Server | ||
# Provides the requisite #to_{format} methods a Responder needs | ||
# to handle <code>respond_with</code> | ||
class Responder < ActionController::Responder | ||
# Ask the resource to give a HandleBars-compatible | ||
# representation and then passes it back to | ||
# <code>render</code> with the appropriate new options. | ||
# | ||
# Add the HBS-compatible version of the resource | ||
# to the locals hash, and ensure ':html' is in | ||
# the list of requested formats. | ||
def to_handlebars | ||
display resource, resource_options if resourceful? | ||
end | ||
alias_method :to_hbs, :to_handlebars | ||
|
||
private | ||
|
||
# Aggregate the options for displaying this resource | ||
# and return the new Hash. | ||
def resource_options | ||
{ locals: resource_locals, formats: resource_formats } | ||
end | ||
|
||
# Merge any user-supplied locals with the formatted resource | ||
# and return the Hash | ||
def resource_locals | ||
(options.delete(:locals) || {}).merge!(resource.send(:"to_#{format}")) | ||
end | ||
|
||
# Merge any user-supplied formats with our | ||
# supported format(s) and return the new Array. | ||
def resource_formats | ||
supported_formats | (options.delete(:formats) || []) | ||
end | ||
|
||
# :nodoc: | ||
def supported_formats | ||
[:js, :jst] | ||
end | ||
end | ||
end | ||
end |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing I didn't really like about this is that the JS context has already compiled this template (especially if precompile is on); I tried hooking this into the sprockets side but it really is a hack as well... I am not sure of the runtime speed of a view like this (which is the thing I was trying to make better).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree about your speed concerns. For my purposes, in any case, the HTML is a fallback for generating pages to be consumed by clients that don't support Javascript (GoogleBot, I'm looking at you.)
The controller I've implemented it within also serves JSON that the web app uses with Handlebars to render on the client side, which is much better overall. I'll be caching the hell out of 'static' HTML generated, and expect the usage of the HTML-only versions to be rather low.
Given the numbers I'm seeing from generating the HTML server side on my development box (200-500ms in views), there's definitely a lot of room to improve before I'd recommend using it for any large scale system without an effective caching strategy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only other concern which I thought of last night (but had already been on the way to bed) was that the context might not be thread safe.