Share
Embed
Render It!: A Deep Dive into ActionView and Template Engines
Slides for Rails Pacific 2014 talk "Render It!: A Deep Dive into ActionView and Template Engines" http://railspacific.com/
Akira Matsuda
September 27, 2014
Transcript
152 slides
-
Action View Rails <= 4.0: Part of Action Pack Rails
4.1: Extracted to a separate gem -
Names Documented name: Action View Gem name: actionview Directory name:
action_view Module name: ActionView -
actionview.gemspec Gem::Specification.new do |s| ... s.add_dependency 'activesupport', version s.add_dependency 'builder',
'~> 3.1' s.add_dependency 'erubis', '~> 2.7.0' s.add_dependency 'rails-html-sanitizer', '~> 1.0' s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.2' s.add_development_dependency 'actionpack', version s.add_development_dependency 'activemodel', version end -
actionview.gemspec Gem::Specification.new do |s| ... s.add_dependency 'activesupport', version s.add_dependency 'builder',
'~> 3.1' s.add_dependency 'erubis', '~> 2.7.0' s.add_dependency 'rails-html-sanitizer', '~> 1.0' s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.2' s.add_development_dependency 'actionpack', version s.add_development_dependency 'activemodel', version end -
Why was ActionView extracted? Rails apps without ActionView Something more
lightweight, maybe? -
Why was ActionView extracted? Rails apps without ActionView Something more
lightweight, maybe? Apps without rails / actionpack e.g. Sinatra You can use ActionView's rich helper methods View files could be rails compatible -
Why was ActionView extracted? Rails apps without ActionView Apps without
rails / actionpack -
rails --help Options: -r, [--ruby=PATH] # Path to the Ruby
binary of your choice # Default: /Users/a_matsuda/.rbenv/versions/2.1.3/bin/ruby -m, [--template=TEMPLATE] # Path to some application template (can be a filesystem path or URL) [--skip-gemfile], [--no-skip-gemfile] # Don't create a Gemfile -B, [--skip-bundle], [--no-skip-bundle] # Don't run bundle install -G, [--skip-git], [--no-skip-git] # Skip .gitignore file [--skip-keeps], [--no-skip-keeps] # Skip source control .keep files -O, [--skip-active-record], [--no-skip-active-record] # Skip Active Record files -V, [--skip-action-view], [--no-skip-action-view] # Skip Action View files -S, [--skip-sprockets], [--no-skip-sprockets] # Skip Sprockets files [--skip-spring], [--no-skip-spring] # Don't install Spring application preloader -d, [--database=DATABASE] # Preconfigure for selected database (options: mysql/oracle/postgresql/ sqlite3/frontbase/ibm_db/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc) # Default: sqlite3 -j, [--javascript=JAVASCRIPT] # Preconfigure for selected JavaScript library # Default: jquery -J, [--skip-javascript], [--no-skip-javascript] # Skip JavaScript files [--dev], [--no-dev] # Setup the application with Gemfile pointing to your Rails checkout [--edge], [--no-edge] # Setup the application with Gemfile pointing to Rails repository -T, [--skip-test-unit], [--no-skip-test-unit] # Skip Test::Unit files [--rc=RC] # Path to file containing extra configuration options for rails command [--no-rc], [--no-no-rc] # Skip loading of extra configuration options from .railsrc file -
OMG there it is! Options: -r, [--ruby=PATH] # Path to
the Ruby binary of your choice # Default: /Users/a_matsuda/.rbenv/versions/2.1.3/bin/ruby -m, [--template=TEMPLATE] # Path to some application template (can be a filesystem path or URL) [--skip-gemfile], [--no-skip-gemfile] # Don't create a Gemfile -B, [--skip-bundle], [--no-skip-bundle] # Don't run bundle install -G, [--skip-git], [--no-skip-git] # Skip .gitignore file [--skip-keeps], [--no-skip-keeps] # Skip source control .keep files -O, [--skip-active-record], [--no-skip-active-record] # Skip Active Record files -V, [--skip-action-view], [--no-skip-action-view] # Skip Action View files -S, [--skip-sprockets], [--no-skip-sprockets] # Skip Sprockets files [--skip-spring], [--no-skip-spring] # Don't install Spring application preloader -d, [--database=DATABASE] # Preconfigure for selected database (options: mysql/oracle/postgresql/ sqlite3/frontbase/ibm_db/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc) # Default: sqlite3 -j, [--javascript=JAVASCRIPT] # Preconfigure for selected JavaScript library # Default: jquery -J, [--skip-javascript], [--no-skip-javascript] # Skip JavaScript files [--dev], [--no-dev] # Setup the application with Gemfile pointing to your Rails checkout [--edge], [--no-edge] # Setup the application with Gemfile pointing to Rails repository -T, [--skip-test-unit], [--no-skip-test-unit] # Skip Test::Unit files [--rc=RC] # Path to file containing extra configuration options for rails command [--no-rc], [--no-no-rc] # Skip loading of extra configuration options from .railsrc file -
config/application.rb # Pick the frameworks you want: require "active_model/railtie" require
"active_record/railtie" require "action_controller/railtie" require "action_mailer/railtie" # require "action_view/railtie" require "sprockets/railtie" require "rails/test_unit/railtie" -
Let's generate something % rails g scaffold user name %
rake db:migrate % rails s % open http://localhost:3000/users -
config/application.rb # Pick the frameworks you want: require "active_model/railtie" require
"active_record/railtie" require "action_controller/railtie" require "action_mailer/railtie" # require "action_view/railtie" require "sprockets/railtie" require "rails/test_unit/railtie" -
actionpack/lib/action_controller/ railtie.rb require "rails" require "action_controller" require "action_dispatch/railtie" require "abstract_controller/railties/
routes_helpers" require "action_controller/railties/helpers" require "action_view/railtie" -
This should be a bug. Let's just remove this line.
require "action_dispatch/railtie" require "abstract_controller/railties/routes_helpers" require "action_controller/railties/helpers" -require "action_view/railtie" module ActionController class Railtie < Rails::Railtie #:nodoc: -
Because these gems depend on actionview in their gemspecs actionpack
actionmailer sprockets-rails (rails depends on it) -
sprockets-rails/sprockets/rails/ helper.rb require 'action_view' require 'sprockets' require 'active_support/core_ext/class/attribute' module Sprockets
module Rails module Helper ... include ActionView::Helpers::AssetUrlHelper include ActionView::Helpers::AssetTagHelper ... -
So, the option was removed --skip-action-view option was only available
in Rails 4.1 Will be removed since Rails 4.2 -
Why was ActionView extracted? Rails apps without ActionView Apps without
rails / actionpack -
Let's try creating a minimum ActionView app A command line
app that renders a template to STDOUT -
How can I render? Maybe there's a method named "render"
inside actionview? -
% git grep "def render" % git grep "def render"
actionview/lib dependency_tracker.rb: def render_dependencies helpers/rendering_helper.rb: def render(options = {}, locals = {}, &block) helpers/tags/base.rb: def render helpers/tags/check_box.rb: def render helpers/tags/collection_check_boxes.rb: def render(&block) helpers/tags/collection_check_boxes.rb: def render_component(builder) helpers/tags/collection_helpers.rb: def render_collection #:nodoc: helpers/tags/collection_radio_buttons.rb: def render(&block) helpers/tags/collection_radio_buttons.rb: def render_component(builder) helpers/tags/collection_select.rb: def render helpers/tags/color_field.rb: def render helpers/tags/date_select.rb: def render helpers/tags/datetime_field.rb: def render helpers/tags/grouped_collection_select.rb: def render helpers/tags/label.rb: def render(&block) helpers/tags/label.rb: def render_component(builder) helpers/tags/number_field.rb: def render helpers/tags/password_field.rb: def render helpers/tags/radio_button.rb: def render helpers/tags/search_field.rb: def render helpers/tags/select.rb: def render helpers/tags/text_area.rb: def render helpers/tags/text_field.rb: def render helpers/tags/time_zone_select.rb: def render log_subscriber.rb: def render_template(event) renderer/abstract_renderer.rb: def render renderer/partial_renderer.rb: def render(context, options, block) renderer/partial_renderer.rb: def render_collection renderer/partial_renderer.rb: def render_partial renderer/renderer.rb: def render(context, options) renderer/renderer.rb: def render_body(context, options) renderer/renderer.rb: def render_template(context, options) #:nodoc: renderer/renderer.rb: def render_partial(context, options, &block) #:nodoc: renderer/streaming_template_renderer.rb: def render_template(template, layout_name = nil, locals = {}) #:nodoc: renderer/template_renderer.rb: def render(context, options) renderer/template_renderer.rb: def render_template(template, layout_name = nil, locals = nil) #:nodoc: renderer/template_renderer.rb: def render_with_layout(path, locals) #:nodoc: rendering.rb: def render_to_body(options = {}) rendering.rb: def rendered_format template.rb: def render(view, locals, buffer=nil, &block) template/html.rb: def render(*args) template/text.rb: def render(*args) test_case.rb: def render(options = {}, local_assigns = {}, &block) test_case.rb: def rendered_views test_case.rb: def rendered_views test_case.rb: def render(options = {}, local_assigns = {}) -
Helpers::Tags and TestCase % git grep "def render" actionview/lib dependency_tracker.rb:
def render_dependencies helpers/rendering_helper.rb: def render(options = {}, locals = {}, &block) helpers/tags/base.rb: def render helpers/tags/check_box.rb: def render helpers/tags/collection_check_boxes.rb: def render(&block) helpers/tags/collection_check_boxes.rb: def render_component(builder) helpers/tags/collection_helpers.rb: def render_collection #:nodoc: helpers/tags/collection_radio_buttons.rb: def render(&block) helpers/tags/collection_radio_buttons.rb: def render_component(builder) helpers/tags/collection_select.rb: def render helpers/tags/color_field.rb: def render helpers/tags/date_select.rb: def render helpers/tags/datetime_field.rb: def render helpers/tags/grouped_collection_select.rb: def render helpers/tags/label.rb: def render(&block) helpers/tags/label.rb: def render_component(builder) helpers/tags/number_field.rb: def render helpers/tags/password_field.rb: def render helpers/tags/radio_button.rb: def render helpers/tags/search_field.rb: def render helpers/tags/select.rb: def render helpers/tags/text_area.rb: def render helpers/tags/text_field.rb: def render helpers/tags/time_zone_select.rb: def render log_subscriber.rb: def render_template(event) renderer/abstract_renderer.rb: def render renderer/partial_renderer.rb: def render(context, options, block) renderer/partial_renderer.rb: def render_collection renderer/partial_renderer.rb: def render_partial renderer/renderer.rb: def render(context, options) renderer/renderer.rb: def render_body(context, options) renderer/renderer.rb: def render_template(context, options) #:nodoc: renderer/renderer.rb: def render_partial(context, options, &block) #:nodoc: renderer/streaming_template_renderer.rb: def render_template(template, layout_name = nil, locals = {}) #:nodoc: renderer/template_renderer.rb: def render(context, options) renderer/template_renderer.rb: def render_template(template, layout_name = nil, locals = nil) #:nodoc: renderer/template_renderer.rb: def render_with_layout(path, locals) #:nodoc: rendering.rb: def render_to_body(options = {}) rendering.rb: def rendered_format template.rb: def render(view, locals, buffer=nil, &block) template/html.rb: def render(*args) template/text.rb: def render(*args) test_case.rb: def render(options = {}, local_assigns = {}, &block) test_case.rb: def rendered_views test_case.rb: def rendered_views test_case.rb: def render(options = {}, local_assigns = {}) -
Excluding Helpers::Tags and TestCase % git grep "def render" actionview/lib
| grep -v "^helpers\/tags\/" | grep -v "^test_case\.rb" dependency_tracker.rb: def render_dependencies helpers/rendering_helper.rb: def render(options = {}, locals = {}, &block) log_subscriber.rb: def render_template(event) renderer/abstract_renderer.rb: def render renderer/partial_renderer.rb: def render(context, options, block) renderer/partial_renderer.rb: def render_collection renderer/partial_renderer.rb: def render_partial renderer/renderer.rb: def render(context, options) renderer/renderer.rb: def render_body(context, options) renderer/renderer.rb: def render_template(context, options) #:nodoc: renderer/renderer.rb: def render_partial(context, options, &block) #:nodoc: renderer/streaming_template_renderer.rb: def render_template(template, layout_name = nil, locals = {}) #:nodoc: renderer/template_renderer.rb: def render(context, options) renderer/template_renderer.rb: def render_template(template, layout_name = nil, locals = nil) #:nodoc: renderer/template_renderer.rb: def render_with_layout(path, locals) #:nodoc: rendering.rb: def render_to_body(options = {}) rendering.rb: def rendered_format template.rb: def render(view, locals, buffer=nil, &block) template/html.rb: def render(*args) template/text.rb: def render(*args) -
Excluding Helpers::Tags and TestCase Still 20 render methods... No matter
which one, view should respond_to `render` -
Error! % bundle ex ruby app.rb action_view/lookup_context.rb:69:in `get': uninitialized constant
ActionView::LookupContext::DetailsKey::Mime (NameError) from action_view/lookup_context.rb:90:in `details_key' from action_view/lookup_context.rb:158:in `detail_args_for' from action_view/lookup_context.rb:152:in `args_for_lookup' from action_view/lookup_context.rb:121:in `find' from action_view/renderer/abstract_renderer.rb:18:in `find_template' from action_view/renderer/template_renderer.rb:32:in `block in determine_template' from action_view/lookup_context.rb:143:in `with_fallbacks' from action_view/renderer/abstract_renderer.rb:18:in `with_fallbacks' from action_view/renderer/template_renderer.rb:32:in `determine_template' from action_view/renderer/template_renderer.rb:8:in `render' from action_view/renderer/renderer.rb:42:in `render_template' from action_view/renderer/renderer.rb:23:in `render' from action_view/helpers/rendering_helper.rb:32:in `render' from app.rb:2:in `<main>' -
s.add_development_dependency 'actionpack' Uses actionpack only for testing Doesn't require actionpack
in runtime I'm sorry I told you a lie! actionview still depends on actionpack in runtime! -
Jeremy said, :+1: it should have a runtime dependency. It's
still closely coupled with Action Pack. -
Apps without rails / actionpack It turned out this was
impossible At least, I'm sure nobody has tried this before -
Why was ActionView extracted? Rails apps without ActionView Apps without
rails / actionpack -
ProTip: How to find Rails bugs Try something weird on
Rails like this -
app.rb - Workaround Bundler.require # copied from AD/http/mime_type.rb (and cut
the last line) require_relative 'mime_type' puts ActionView::Base.new.render(file: 'hello.txt') #=> hello -
Error! % bundle ex ruby app.rb action_view/path_set.rb:46:in `find': Missing template
/hello.html.erb with {:locale=>[:en], :formats=>[:html, :text, :js, :css, :xml, :json], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby]}. Searched in: (ActionView::MissingTemplate) from action_view/lookup_context.rb:121:in `find' from action_view/renderer/abstract_renderer.rb:18:in `find_template' from action_view/renderer/template_renderer.rb:40:in `determine_template' from action_view/renderer/template_renderer.rb:8:in `render' from action_view/renderer/renderer.rb:42:in `render_template' from action_view/renderer/renderer.rb:23:in `render' from action_view/helpers/rendering_helper.rb:32:in `render' from app.rb:13:in `<main>' -
app.rb - render :template Bundler.require require_relative 'mime_type' # need to
give a view_path puts ActionView::Base.new('.').render(template: 'hello.html.erb') #=> hello, erb! -
render template: 'hello' No need to specify ".html.erb" actionview would
find a template having suitable format and handler (and locale and variants) -
app.rb - render :template Bundler.require require_relative 'mime_type' # no need
to specify ".html.erb" puts ActionView::Base.new('.').render(template: 'hello') #=> hello, erb! -
How does the actual Rails controller render? % rails new
someapp % cd someapp % rails g controller welcome index % echo "<% puts caller %>" >> app/views/ welcome/index.html.erb % rails s % open http://localhost:3000/welcome/index -
The callers actionview/lib/action_view/template.rb:145:in `block in render' activesupport/lib/active_support/notifications.rb:166:in `instrument' actionview/lib/action_view/template.rb:329:in `instrument'
actionview/lib/action_view/template.rb:143:in `render' actionview/lib/action_view/renderer/template_renderer.rb:54:in `block (2 levels) in render_template' actionview/lib/action_view/renderer/abstract_renderer.rb:39:in `block in instrument' activesupport/lib/active_support/notifications.rb:164:in `block in instrument' activesupport/lib/active_support/notifications/instrumenter.rb:20:in `instrument' activesupport/lib/active_support/notifications.rb:164:in `instrument' actionview/lib/action_view/renderer/abstract_renderer.rb:39:in `instrument' actionview/lib/action_view/renderer/template_renderer.rb:53:in `block in render_template' actionview/lib/action_view/renderer/template_renderer.rb:61:in `render_with_layout' actionview/lib/action_view/renderer/template_renderer.rb:52:in `render_template' actionview/lib/action_view/renderer/template_renderer.rb:14:in `render' actionview/lib/action_view/renderer/renderer.rb:42:in `render_template' actionview/lib/action_view/renderer/renderer.rb:23:in `render' actionview/lib/action_view/rendering.rb:100:in `_render_template' actionpack/lib/action_controller/metal/streaming.rb:217:in `_render_template' actionview/lib/action_view/rendering.rb:83:in `render_to_body' actionpack/lib/action_controller/metal/rendering.rb:32:in `render_to_body' actionpack/lib/action_controller/metal/renderers.rb:37:in `render_to_body' actionpack/lib/abstract_controller/rendering.rb:25:in `render' -
What is `view_context`? module ActionView module Rendering def view_context view_context_class.new(view_renderer,
view_assigns, self) end def view_context_class @_view_context_class ||= self.class.view_context_class ennnd -
What is `view_context`? module ActionView module Rendering module ClassMethods def
view_context_class @view_context_class ||= begin include_path_helpers = supports_path? routes = respond_to?(:_routes) && _routes helpers = respond_to?(:_helpers) && _helpers Class.new(ActionView::Base) do if routes include routes.url_helpers(include_path_helpers) include routes.mounted_helpers end if helpers include helpers ennnnnnnd -
`view_context` is not a direct instance of ActionView::Base class an
instance of an anonymous child class of ActionView::Base -
What is `view_renderer`? module ActionView class Base attr_accessor :view_renderer def
initialize(context = nil, assigns = {}, controller = nil, formats = nil) ... @view_renderer = ActionView::Renderer.new(lookup_context) ... ennnd -
Back to rendering.rb module ActionView module Rendering def _render_template(options) ...
view_renderer.render(view_context, options) ennnd -
renderer/renderer.rb module ActionView class Renderer def render(context, options) if options.key?(:partial)
... else render_template(context, options) ennnnd -
renderer/template_renderer.rb module ActionView class TemplateRenderer < AbstractRenderer def render(context, options)
@view = context ... template = determine_template(options) ... render_template(template, options[:layout], options[:locals]) ennnd -
Then another render_template after another render after render_template after render.
This render method determines a template, then renders it -
renderer/template_renderer.rb module ActionView class TemplateRenderer < AbstractRenderer def determine_template(options) ...
elsif options.key?(:text) Template::Text.new(options[:text], formats.first) ... elsif options.key?(:inline) handler = Template.handler_for_extension(options[:type] || "erb") Template.new(options[:inline], "inline template", handler, :locals => keys) elsif options.key?(:template) ... find_template(options[:template], options[:prefixes], false, keys, @details) ennnnnd -
template/text.rb module ActionView class Template class Text def initialize(string, type
= nil) @string = string.to_s ... end def to_str @string end def render(*args) to_str ennnnd -
find_template Very very long journey LookupContext#find => PathSet#find => Resolver#find_all
Finally finds a template Templates found are cached -
template/resolver.rb handler, format, variant = extract_handler_and_format_and_variant(template, formats) contents = File.binread(template)
Template.new(contents, File.expand_path(template), handler, :virtual_path => path.virtual, :format => format, :variant => variant, :updated_at => mtime(template) ) -
Template resolution Read the content from the file Create a
handler instance Return an instance of Template that has the content and the handler -
renderer/template_renderer.rb module ActionView class TemplateRenderer < AbstractRenderer def render_template(template, layout_name
= nil, locals = nil) ... template.render(view, locals) { |*name| view._layout_for(*name) } end -
template.rb module ActionView class Template def render(view, locals, buffer=nil, &block)
... compile!(view) view.send(method_name, locals, buffer, &block) ... ennnd -
template.rb module ActionView class Template def compile!(view) return if @compiled
... mod = view.singleton_class ... compile(mod) ... ennnd -
template.rb module ActionView class Template def compile(mod) ... method_name =
self.method_name code = @handler.call(self) ... source = <<-end_src def #{method_name}(local_assigns, output_buffer) _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code} ensure @virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer end end_src ... mod.module_eval(source, identifier, 0) ... ennnd -
compile @handler.call(template) returns a Ruby code The code is defined
on the view context object as a Ruby method -
template.rb module ActionView class Template def render(view, locals, buffer=nil, &block)
... compile!(view) view.send(method_name, locals, buffer, &block) ... ennnd -
Back to `render` Then the defined method is called Template
rendering is not `eval` but a method execution -
Template Handler API A template handler has to respond_to #call
that takes a Template instance #call returns a compiled HTML String -
app.rb - confirming that a method was added Bundler.require require_relative
'mime_type' view = Class.new(ActionView::Base).new('.'). methods = view.public_methods view.render(template: 'hello') p view.public_methods - methods #=> [:___sers_a_matsuda_tmp_actionview_test_hello_html_erb___149 3524731793660833_70284245975760] -
template/handlers/erb.rb module ActionView class Template module Handlers class ERB class_attribute
:erb_implementation self.erb_implementation = Erubis def call(template) ... self.class.erb_implementation.new( erb, :escape => (self.class.escape_whitelist.include? template.type), :trim => (self.class.erb_trim_mode == "-") ).src end -
self.erb_implementation = Erubis Erubis is an ERB compatible eRuby library
Looks as if erb_implementation is configurable -
self.erb_implementation = ERB (?) But if you set this to
`ERB` the actionview tests will never pass. Here's another unknown "meaningless option". Seki-san and I once tried to fix this before Ruby 2.0 release, but we failed... Weird. -
ERB.new(erb).src % ruby -rerb -e "puts ERB.new('<%= 1 + 1
%>').src" #coding:UTF-8 _erbout = ''; _erbout.concat(( 1 + 1 ).to_s); _erbout.force_encoding(__ENCODING__) -
ERB.new(erb).src It gets an ERB template string and returns a
ruby code that generates the result HTML -
app.rb - rendering haml Bundler.require require_relative 'mime_type' require 'haml/template' view
= Class.new(ActionView::Base).new('.') puts view.render(template: 'hello', handlers: 'haml') -
app.rb - rendering haml with weird monkey-patches Bundler.require require_relative 'mime_type'
module ActionPack module VERSION MAJOR = 4 end end module Rails def self.env ActiveSupport::StringInquirer.new('production') end end require 'haml/template' view = Class.new(ActionView::Base).new('.') puts view.render(template: 'hello', handlers: 'haml') -
app.rb - rendering slim with weird monkey-patches Bundler.require require_relative 'mime_type'
module ActionPack module VERSION MAJOR = 4 end end module Rails def self.env ActiveSupport::StringInquirer.new('production') end end require 'slim' view = Class.new(ActionView::Base).new('.') puts view.render(template: 'hello', handlers: 'slim') -
Gemfile source 'https://rubygems.org/' gem 'actionview', path: '~/src/rails/actionview', require: 'action_view' gem
'haml' gem 'slim', require: false gem 'benchmark-ips' -
Benchmark! Bundler.require require_relative 'mime_type' module ActionPack module VERSION MAJOR =
4 end end module Rails def self.env ActiveSupport::StringInquirer.new('production') end end require 'haml/template' require 'slim' view = Class.new(ActionView::Base).new('.') # discard the first execution that contains template lookup and compilation view.render(template: 'hello', handlers: 'erb') view.render(template: 'hello', handlers: 'haml') view.render(template: 'hello', handlers: 'slim') Benchmark.ips do |x| x.report('erb') { view.render(template: 'hello', handlers: 'erb') } x.report('haml') { view.render(template: 'hello', handlers: 'haml') } x.report('slim') { view.render(template: 'hello', handlers: 'slim') } end -
Benchmark results % bundle ex ruby app.rb Calculating ------------------------------------- erb
1839 i/100ms haml 1440 i/100ms slim 1920 i/100ms ------------------------------------------------- erb 19261.7 (±4.2%) i/s - 97467 in 5.069805s haml 14662.8 (±5.0%) i/s - 73440 in 5.022658s slim 19673.9 (±3.6%) i/s - 99840 in 5.081665s -
Results % bx ruby app.rb Calculating ------------------------------------- erb 1824 i/100ms
haml(gem) 1440 i/100ms haml(fork) 1553 i/100ms slim 1875 i/100ms ------------------------------------------------- erb 19614.9 (±3.4%) i/s - 98496 in 5.027551s haml(gem) 14662.8 (±5.0%) i/s - 73440 in 5.022658s haml(fork) 16193.5 (±4.2%) i/s - 82309 in 5.092455s slim 20193.5 (±3.5%) i/s - 101250 in 5.020527s