class Barber::Precompiler

Public Class Methods

compile(template, options = nil) click to toggle source
# File lib/barber/precompiler.rb, line 7
def compile(template, options = nil)
  instance.compile(template, options)
end
compiler_version() click to toggle source
# File lib/barber/precompiler.rb, line 11
def compiler_version
  instance.compiler_version
end
handlebars_available?() click to toggle source
# File lib/barber/precompiler.rb, line 15
def handlebars_available?
  require 'handlebars/source' # handlebars precompilation is optional feature.
rescue LoadError => e
  raise e unless ['cannot load such file -- handlebars/source', 'no such file to load -- handlebars/source'].include?(e.message)
ensure
  return !!defined?(Handlebars::Source)
end

Private Class Methods

instance() click to toggle source
# File lib/barber/precompiler.rb, line 25
def instance
  @instance ||= new
end

Public Instance Methods

compile(template, options = nil) click to toggle source
# File lib/barber/precompiler.rb, line 30
def compile(template, options = nil)
  context.call precompile_function, sanitize(template), options
rescue ExecJS::ProgramError => ex
  raise Barber::PrecompilerError.new(template, ex)
end
compiler_version() click to toggle source
# File lib/barber/precompiler.rb, line 51
def compiler_version
  "handlebars:#{handlebars_version}"
end
handlebars() click to toggle source
# File lib/barber/precompiler.rb, line 47
def handlebars
  @handlebars ||= File.new(Handlebars::Source.bundled_path)
end
precompile_function() click to toggle source
# File lib/barber/precompiler.rb, line 36
def precompile_function
  "Barber.precompile"
end
sources() click to toggle source
# File lib/barber/precompiler.rb, line 40
def sources
  sources = []
  sources << precompiler
  sources << handlebars if self.class.handlebars_available?
  sources
end

Private Instance Methods

context() click to toggle source
# File lib/barber/precompiler.rb, line 98
def context
  @context ||= ExecJS.compile source
end
handlebars_version() click to toggle source
# File lib/barber/precompiler.rb, line 137
def handlebars_version
  @handlebars_version ||= context.eval('Handlebars.VERSION')
end
precompiler() click to toggle source
# File lib/barber/precompiler.rb, line 94
def precompiler
  @precompiler ||= File.new(File.expand_path("../javascripts/handlebars_precompiler.js", __FILE__))
end
sanitize(template) click to toggle source

This method handles different types of user input. The precompiler can be called from many different places which create interesting conditions. Case 1: Rake-Pipeline-Web-Filters: calls with a JSON encoded string. Case 2: Sanitize using a RegExp Case 3: “Normal” input. Reading from a file or something like that.

Each one of these cases is covered by a test case. If you find another breaking use case please address it here with a regression test.

# File lib/barber/precompiler.rb, line 65
def sanitize(template)
  begin
    if template =~ /\A".+"\Z/m
      # Case 1
      sanitize_with_json(template)
    else
      # Case 2
      sanitize_with_regexp(template)
    end
  rescue JSON::ParserError
    # Case 3
    template
  end
end
sanitize_with_json(template) click to toggle source
# File lib/barber/precompiler.rb, line 80
def sanitize_with_json(template)
  JSON.load(%Q|{"template":#{template}}|)['template']
end
sanitize_with_regexp(template) click to toggle source
# File lib/barber/precompiler.rb, line 84
def sanitize_with_regexp(template)
  if template.respond_to? :gsub
    sanitized = template.gsub(/\\n/,"\n")
    sanitized = sanitized.gsub(/\\["']/, '"')
    sanitized
  else
    template
  end
end
source() click to toggle source
# File lib/barber/precompiler.rb, line 102
def source
  source_fixes + sources.map(&:read).join("\n;\n")
end
source_fixes() click to toggle source
# File lib/barber/precompiler.rb, line 106
    def source_fixes
      shims = []

      # hbs 3 has no reference to `window` and `self`.
      shims << <<-JS
if (typeof window === 'undefined') {
  window = this;
}
if (typeof self === 'undefined') {
  self = this;
}
      JS

      # Workaround for `ExecJS::RubyRhinoRuntime`. It throws an error: `Invalid JavaScript value of type org.mozilla.javascript.MemberBox` on inherit `Error`.
      shims << <<-JS
if (Object.defineProperty) {
  Object.defineProperty(Error, 'stackTraceLimit', {configurable: false});
  Object.defineProperty(Error, 'prepareStackTrace', {configurable: false});
}
      JS

      # Workaround for `ExecJS::RubyRhinoRuntime`. It has no `setTimeout` but backburner references `setTimeout`.
      shims << <<-JS
if (typeof setTimeout === 'undefined') {
  setTimeout = function(fn) { fn(); };
}
      JS

      shims.join("\n")
    end