diff --git a/.gitignore b/.gitignore index 91e2b6a..e9f6489 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .bundle pkg/* Gemfile.lock +gemfiles/*.lock diff --git a/Appraisals b/Appraisals new file mode 100644 index 0000000..f7fc564 --- /dev/null +++ b/Appraisals @@ -0,0 +1,15 @@ +appraise "sprockets-3" do + gem "sprockets", "3.0.0" +end + +appraise "sprockets-2" do + gem "sprockets", "2.3.3" +end + +appraise "tilt-2" do + gem "tilt", "2.0.1" +end + +appraise "tilt-1" do + gem "tilt", "1.4.1" +end diff --git a/CHANGELOG.md b/CHANGELOG.md index 943814f..bf928be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,100 @@ +## 0.23.9 (2021-03-18) + +* Update Handlebars to v4.7.7 + +## 0.23.8 (2019-02-24) + +* Update Handlebars to v4.7.3 + +## 0.23.7 (2019-11-20) + +* Update Handlebars to v4.5.3 + +## 0.23.6 (2019-11-14) + +* Update Handlebars to v4.5.2 + +## 0.23.5 (2019-09-25) + +* Update Handlebars to v4.3.1 + +## 0.23.4 (2019-06-03) + +* Update Handlebars to v4.1.2 + +## 0.23.3 (2019-02-20) + +* Update Handlebars to v4.1.0 + +## 0.23.2 (2017-05-07) + +* Remove requirement for MultiJSON + +## 0.23.1 (2016-08-04) + +* Fixes for Issue on Boot with Rails 3 and 4 + +## 0.23.0 (2016-01-26) + +* Fixes for Railties and certain versions of Rails +* Only load if asset compilation is enabled +* Add support for Sprockets 3.x + 4.x - @tjgrathwell + +## 0.22.0 (2015-11-23) + +* Update Handlebars to v4.0.5 + +## 0.21.0 (2015-02-24) + +* Update Handlebars to v4.0.2 + +## 0.20.2 (2015-02-24) + +* Relax Sprockets Dependencies - @rounders +* Add option to CHOMP underscores on partials - @GreyKn + +## 0.20.1 (2015-02-24) + +* Actually revert to native HB.js + +## 0.20 (2015-02-23) + +* Fix issues with window object, revert to native HB.js +* Fix extension handling bug on some versions of rails + +## 0.19 (2015-02-18) + +* Upgrade to Handlebars v3.0 +* Re-fix the issue regarding sprockets + +## 0.18.1 (2015-02-18) + +* Fix issue regarding sprockets versioning of assets + +## 0.18 (2014-09-08) + +* Update to handlebars v2.0.0 - @AlexRiedler + +## 0.17.2 (2014-09-08) + +* Support for Ruby v1.8 - @blainekasten + +## 0.17.1 (2014-06-28) + +* Fix engine initialization error on rails during assets precompile - @AlexRiedler + +## 0.17 (2014-06-22) + +* Massive revamp - @AlexRiedler +* Changed how sprockets is registered - @AlexRiedler +* AMD loading - @AlexRiedler, based on @pboling changes (THANKS!) +* Fix extension being too liberal issues - @langalex + +## 0.16 (2014-05-27) + +* README clarification - @jithugopal +* Upgrade `handlebars` to 1.3 - @neodude + ## 0.15 (2013-12-06) * Allow use of ember and other handlebars simultaneously - @rbhitchcock diff --git a/README.md b/README.md index 754caad..86e6e0d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,12 @@ Yea, I think so too. That is why I wrote **handlebars_assets**. Give your Handle Using `sprockets` with Sinatra or another framework? **handlebars_assets** works outside of Rails too (as of v0.2.0) +# BREAKING CHANGES AS OF v0.17 + +@AlexRiedler has made some larger changes to this repository for going forward; If you have existing monkey patches they may not work, and the configuration schema has changed slightly to handle multiple extensions for the same compilation pipeline. + +If you have any problems, please post an issue! I will attempt to fix ASAP + # BREAKING CHANGE AS OF v0.9.0 My pull request to allow `/` in partials was pulled into Handlebars. The hack that converted partial names to underscored paths (`shared/_time` -> `_shared_time`) is no longer necessary and has been removed. You should change all the partial references in your app when upgrading from a version prior to v0.9.0. @@ -286,6 +292,7 @@ Les Hill, follow me on [Github](https://github.com/leshill) and [Twitter](https: * Parker Selbert (@sorentwo) : README section for `multiple_frameworks` * Francisco QV (@panchoqv) : README clarification * Dylan Markow (@dmarkow) : README cleanup +* Peter Boling (@pboling) : AMD Loading # Contributing diff --git a/Rakefile b/Rakefile index 15940a3..f77e199 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,7 @@ +require "rubygems" +require "bundler/setup" require "bundler/gem_tasks" -require 'rake/testtask' +require "rake/testtask" Rake::TestTask.new do |t| t.libs << 'test' diff --git a/gemfiles/rails_4_1.gemfile b/gemfiles/rails_4_1.gemfile new file mode 100644 index 0000000..3a96594 --- /dev/null +++ b/gemfiles/rails_4_1.gemfile @@ -0,0 +1,8 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "rails", "4.1.14" +gem "rails-sprockets", "2.3.3" + +gemspec :path => "../" diff --git a/gemfiles/rails_4_2.gemfile b/gemfiles/rails_4_2.gemfile new file mode 100644 index 0000000..cc2618d --- /dev/null +++ b/gemfiles/rails_4_2.gemfile @@ -0,0 +1,8 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "rails", "4.2.5" +gem "rails-sprockets", "2.3.3" + +gemspec :path => "../" diff --git a/gemfiles/rails_5.gemfile b/gemfiles/rails_5.gemfile new file mode 100644 index 0000000..8285f70 --- /dev/null +++ b/gemfiles/rails_5.gemfile @@ -0,0 +1,8 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "rails", "5.0.0.beta1" +gem "rails-sprockets", "3.0.0" + +gemspec :path => "../" diff --git a/gemfiles/sprockets_2.gemfile b/gemfiles/sprockets_2.gemfile new file mode 100644 index 0000000..e0f5788 --- /dev/null +++ b/gemfiles/sprockets_2.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "sprockets", "2.3.3" + +gemspec :path => "../" diff --git a/gemfiles/sprockets_3.gemfile b/gemfiles/sprockets_3.gemfile new file mode 100644 index 0000000..52b0e11 --- /dev/null +++ b/gemfiles/sprockets_3.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "sprockets", "3.0.0" + +gemspec :path => "../" diff --git a/gemfiles/tilt_1.gemfile b/gemfiles/tilt_1.gemfile new file mode 100644 index 0000000..2236fe6 --- /dev/null +++ b/gemfiles/tilt_1.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "tilt", "1.4.1" + +gemspec :path => "../" diff --git a/gemfiles/tilt_2.gemfile b/gemfiles/tilt_2.gemfile new file mode 100644 index 0000000..8af0eca --- /dev/null +++ b/gemfiles/tilt_2.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "tilt", "2.0.1" + +gemspec :path => "../" diff --git a/handlebars_assets.gemspec b/handlebars_assets.gemspec index df97c18..2b51658 100644 --- a/handlebars_assets.gemspec +++ b/handlebars_assets.gemspec @@ -6,10 +6,11 @@ Gem::Specification.new do |s| s.name = "handlebars_assets" s.version = HandlebarsAssets::VERSION s.authors = ["Les Hill"] + s.licenses = ["MIT"] s.email = ["leshill@gmail.com"] - s.homepage = "" + s.homepage = "https://github.com/leshill/handlebars_assets" s.summary = "Compile Handlebars templates in the Rails asset pipeline." - s.description = "Compile Handlebars templates in the Rails asset pipeline." + s.description = "A Railties Gem to compile hbs assets" s.rubyforge_project = "handlebars_assets" @@ -18,14 +19,15 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] - s.add_runtime_dependency "execjs", ">= 1.2.9" - s.add_runtime_dependency "tilt" - s.add_runtime_dependency "multi_json" - s.add_runtime_dependency "sprockets", ">= 2.0.3" + s.add_runtime_dependency "execjs", "~> 2.0" + s.add_runtime_dependency "tilt", ">= 1.2" + s.add_runtime_dependency "sprockets", ">= 2.0.0" - s.add_development_dependency "debugger" - s.add_development_dependency "haml" - s.add_development_dependency "rake" - s.add_development_dependency "slim" - s.add_development_dependency 'json', '~> 1.7.7' + s.add_development_dependency "minitest", '~> 5.5' + s.add_development_dependency "haml", '~> 4.0' + s.add_development_dependency "rake", '~> 10.0' + s.add_development_dependency "slim", '~> 3.0' + s.add_development_dependency "appraisal" + + s.post_install_message = "Remember to rake assets:clean or rake assets:purge on update! this is required because of handlebars updates" end diff --git a/lib/handlebars_assets.rb b/lib/handlebars_assets.rb index 462157a..2177dae 100644 --- a/lib/handlebars_assets.rb +++ b/lib/handlebars_assets.rb @@ -1,23 +1,58 @@ -require "handlebars_assets/version" +require 'handlebars_assets/version' module HandlebarsAssets - PATH = File.expand_path("../../vendor/assets/javascripts", __FILE__) + autoload(:Config, 'handlebars_assets/config') + autoload(:Handlebars, 'handlebars_assets/handlebars') + autoload(:HandlebarsTemplate, 'handlebars_assets/handlebars_template') + autoload(:HandlebarsProcessor, 'handlebars_assets/handlebars_template') + + PATH = File.expand_path('../../vendor/assets/javascripts', __FILE__) def self.path PATH end - autoload(:Config, 'handlebars_assets/config') - autoload(:Handlebars, 'handlebars_assets/handlebars') - autoload(:TiltHandlebars, 'handlebars_assets/tilt_handlebars') + def self.configure + yield Config + end + + def self.register_extensions(sprockets_environment) + if Gem::Version.new(Sprockets::VERSION) < Gem::Version.new('3') + Config.handlebars_extensions.each do |ext| + sprockets_environment.register_engine(ext, HandlebarsTemplate) + end + if Config.haml_enabled? && Config.haml_available? + Config.hamlbars_extensions.each do |ext| + sprockets_environment.register_engine(ext, HandlebarsTemplate) + end + end + if Config.slim_enabled? && Config.slim_available? + Config.slimbars_extensions.each do |ext| + sprockets_environment.register_engine(ext, HandlebarsTemplate) + end + end + else + sprockets_environment.register_mime_type 'text/x-handlebars-template', extensions: Config.handlebars_extensions + if Config.slim_enabled? && Config.slim_available? + sprockets_environment.register_mime_type 'text/x-handlebars-template', extensions: Config.slimbars_extensions + end + if Config.haml_enabled? && Config.haml_available? + sprockets_environment.register_mime_type 'text/x-handlebars-template', extensions: Config.hamlbars_extensions + end + sprockets_environment.register_transformer 'text/x-handlebars-template', 'application/javascript', HandlebarsProcessor + end + end - if defined?(Rails) && defined?(::Rails::Engine) - require 'handlebars_assets/engine' - else - require 'sprockets' - Sprockets.register_engine '.hbs', TiltHandlebars - Sprockets.register_engine '.handlebars', TiltHandlebars - Sprockets.register_engine('.hamlbars', TiltHandlebars) if HandlebarsAssets::Config.haml_available? - Sprockets.register_engine('.slimbars', TiltHandlebars) if HandlebarsAssets::Config.slim_available? + def self.add_to_asset_versioning(sprockets_environment) + sprockets_environment.version += "-#{HandlebarsAssets::VERSION}" end end + +# Register the engine (which will register extension in the app) +# or ASSUME using sprockets +if defined?(Rails) + require 'handlebars_assets/engine' +else + require 'sprockets' + ::HandlebarsAssets.register_extensions(Sprockets) +end diff --git a/lib/handlebars_assets/config.rb b/lib/handlebars_assets/config.rb index d3f22ad..f74d146 100644 --- a/lib/handlebars_assets/config.rb +++ b/lib/handlebars_assets/config.rb @@ -2,27 +2,32 @@ module HandlebarsAssets # Change config options in an initializer: # # HandlebarsAssets::Config.path_prefix = 'app/templates' + module Config extend self attr_writer :compiler, :compiler_path, :ember, :multiple_frameworks, :haml_options, :known_helpers, :known_helpers_only, :options, - :patch_files, :patch_path, :path_prefix, :slim_options, :template_namespace - - def configure - yield self - end + :patch_files, :patch_path, :path_prefix, :slim_options, :template_namespace, + :precompile, :haml_enabled, :slim_enabled, + :handlebars_extensions, :hamlbars_extensions, :slimbars_extensions, + :amd, :handlebars_amd_path, :amd_with_template_namespace, + :chomp_underscore_for_partials def compiler @compiler || 'handlebars.js' end + def self.configure + yield self + end + def compiler_path @compiler_path || HandlebarsAssets.path end def ember? - @ember + @ember || false end def multiple_frameworks? @@ -33,16 +38,35 @@ def haml_available? defined? ::Haml::Engine end + def haml_enabled? + @haml_enabled = true if @haml_enabled.nil? + @haml_enabled + end + def haml_options @haml_options || {} end + def slim_available? + defined? ::Slim::Engine + end + + def slim_enabled? + @slim_enabled = true if @slim_enabled.nil? + @slim_enabled + end + + def slim_options + @slim_options || {} + end + def known_helpers @known_helpers || [] end def known_helpers_only - @known_helpers_only || false + @known_helpers_only = false if @known_helpers_only.nil? + @known_helpers_only end def options @@ -58,21 +82,58 @@ def patch_path end def path_prefix - @path_prefix || 'templates' + @path_prefix ||= 'templates' end - def slim_available? - defined? ::Slim::Engine - end - - def slim_options - @slim_options || {} + def precompile + @precompile = true if @precompile.nil? + @precompile end def template_namespace @template_namespace || 'HandlebarsTemplates' end + def handlebars_extensions + @hbs_extensions ||= ['.hbs', '.handlebars'] + end + + def hamlbars_extensions + @hamlbars_extensions ||= ['.hamlbars'] + end + + def slimbars_extensions + @slimbars_extensions ||= ['.slimbars'] + end + + def ember_extensions + @ember_extensions ||= ['.ember'] + end + + def amd? + @amd || false + end + + # indicate whether the template should + # be added to the global template namespace + def amd_with_template_namespace + @amd_with_template_namespace || false + end + + # path specified by the require.js paths + # during configuration for the handlebars + def handlebars_amd_path + @handlebars_amd_path || 'handlebars' + end + + # Indicate if leading underscore should be allowed + # when creating partial definition. + # Allows compatibility with handlebars-rails + # https://github.com/cowboyd/handlebars-rails/blob/f73a2d11df8aa2c21d335b8f04a8c5b59ae6a790/lib/handlebars-rails/tilt.rb#L18 + def chomp_underscore_for_partials? + @chomp_underscore_for_partials || false + end + private def generate_known_helpers_hash diff --git a/lib/handlebars_assets/engine.rb b/lib/handlebars_assets/engine.rb index aaef0b4..0d175a0 100644 --- a/lib/handlebars_assets/engine.rb +++ b/lib/handlebars_assets/engine.rb @@ -1,11 +1,13 @@ module HandlebarsAssets + # NOTE: must be an engine because we are including assets in the gem class Engine < ::Rails::Engine - initializer "sprockets.handlebars", :after => "sprockets.environment", :group => :all do |app| - next unless app.assets - app.assets.register_engine('.hbs', TiltHandlebars) - app.assets.register_engine('.handlebars', TiltHandlebars) - app.assets.register_engine('.hamlbars', TiltHandlebars) if HandlebarsAssets::Config.haml_available? - app.assets.register_engine('.slimbars', TiltHandlebars) if HandlebarsAssets::Config.slim_available? + initializer "handlebars_assets.assets.register", :group => :all do |app| + app.config.assets.configure do |sprockets_env| + ::HandlebarsAssets::register_extensions(sprockets_env) + if Gem::Version.new(Sprockets::VERSION) < Gem::Version.new('3') + ::HandlebarsAssets::add_to_asset_versioning(sprockets_env) + end + end end end end diff --git a/lib/handlebars_assets/handlebars.rb b/lib/handlebars_assets/handlebars.rb index 7e2eb20..8ae8c75 100644 --- a/lib/handlebars_assets/handlebars.rb +++ b/lib/handlebars_assets/handlebars.rb @@ -32,7 +32,7 @@ def context end def source - @source ||= path.read + @source ||= "if (!window) { var window = {}; }\n#{path.read}" end def patch_path diff --git a/lib/handlebars_assets/handlebars_template.rb b/lib/handlebars_assets/handlebars_template.rb new file mode 100644 index 0000000..17f2e83 --- /dev/null +++ b/lib/handlebars_assets/handlebars_template.rb @@ -0,0 +1,267 @@ +require 'tilt' +require 'json' + +module HandlebarsAssets + module Unindent + # http://bit.ly/aze9FV + # Strip leading whitespace from each line that is the same as the + # amount of whitespace on the first line of the string. + # Leaves _additional_ indentation on later lines intact. + def unindent(heredoc) + heredoc.gsub(/^#{heredoc[/\A\s*/]}/, '') + end + end + + # Sprockets <= 3 + class HandlebarsTemplate < Tilt::Template + def self.default_mime_type + 'application/javascript' + end + + def initialize_engine + HandlebarsRenderer.initialize_engine + end + + def prepare + @engine = renderer.choose_engine(data) + end + + def evaluate(scope, locals, &block) + source = @engine.render(scope, locals, &block) + renderer.compile(source) + end + + private + + def renderer + @renderer ||= HandlebarsRenderer.new(path: @file) + end + end + + # Sprockets 4 + class HandlebarsProcessor + + def self.instance + @instance ||= new + end + + def self.call(input) + instance.call(input) + end + + def self.cache_key + instance.cache_key + end + + attr_reader :cache_key + + def initialize(options = {}) + @cache_key = [self.class.name, ::HandlebarsAssets::VERSION, options].freeze + end + + def call(input) + renderer = HandlebarsRenderer.new(path: input[:filename]) + engine = renderer.choose_engine(input[:data]) + renderer.compile(engine.render) + end + end + + class NoOpEngine + def initialize(data) + @data = data + end + + def render(*args) + @data + end + end + + class HandlebarsRenderer + include Unindent + + def self.initialize_engine + return if @initialized + + begin + require 'haml' + rescue LoadError + # haml not available + end + begin + require 'slim' + rescue LoadError + # slim not available + end + + @initialized = true + end + + def initialize(options) + self.class.initialize_engine + @template_path = TemplatePath.new(options[:path]) + end + + def choose_engine(data) + if @template_path.is_haml? + Haml::Engine.new(data, HandlebarsAssets::Config.haml_options) + elsif @template_path.is_slim? + Slim::Template.new(HandlebarsAssets::Config.slim_options) { data } + else + NoOpEngine.new(data) + end + end + + def compile(source) + # remove trailing \n on file, for some reason the directives pipeline adds this + source.chomp!($/) + + # handle the case of multiple frameworks combined with ember + # DEFER: use extension setup for ember + if (HandlebarsAssets::Config.multiple_frameworks? && @template_path.is_ember?) || + (HandlebarsAssets::Config.ember? && !HandlebarsAssets::Config.multiple_frameworks?) + compile_ember(source) + else + compile_default(source) + end + end + + def compile_ember(source) + "window.Ember.TEMPLATES[#{@template_path.name}] = Ember.Handlebars.compile(#{JSON.dump(source)});" + end + + def compile_default(source) + template = + if HandlebarsAssets::Config.precompile + compiled_hbs = Handlebars.precompile(source, HandlebarsAssets::Config.options) + "Handlebars.template(#{compiled_hbs})" + else + "Handlebars.compile(#{JSON.dump(source)})" + end + + template_namespace = HandlebarsAssets::Config.template_namespace + + if HandlebarsAssets::Config.amd? + handlebars_amd_path = HandlebarsAssets::Config.handlebars_amd_path + if HandlebarsAssets::Config.amd_with_template_namespace + if @template_path.is_partial? + unindent <<-PARTIAL + define(['#{handlebars_amd_path}'],function(Handlebars){ + var t = #{template}; + Handlebars.registerPartial(#{@template_path.name}, t); + return t; + ;}) + PARTIAL + else + unindent <<-TEMPLATE + define(['#{handlebars_amd_path}'],function(Handlebars){ + return #{template}; + }); + TEMPLATE + end + else + if @template_path.is_partial? + unindent <<-PARTIAL + define(['#{handlebars_amd_path}'],function(Handlebars){ + var t = #{template}; + Handlebars.registerPartial(#{@template_path.name}, t); + return t; + ;}) + PARTIAL + else + unindent <<-TEMPLATE + define(['#{handlebars_amd_path}'],function(Handlebars){ + this.#{template_namespace} || (this.#{template_namespace} = {}); + this.#{template_namespace}[#{@template_path.name}] = #{template}; + return this.#{template_namespace}[#{@template_path.name}]; + }); + TEMPLATE + end + end + else + if @template_path.is_partial? + unindent <<-PARTIAL + (function() { + Handlebars.registerPartial(#{@template_path.name}, #{template}); + }).call(this); + PARTIAL + else + unindent <<-TEMPLATE + (function() { + this.#{template_namespace} || (this.#{template_namespace} = {}); + this.#{template_namespace}[#{@template_path.name}] = #{template}; + return this.#{template_namespace}[#{@template_path.name}]; + }).call(this); + TEMPLATE + end + end + end + + protected + + class TemplatePath + def initialize(path) + @full_path = path + end + + def check_extension(ext) + result = false + if ext.start_with? '.' + ext = "\\#{ext}" + result ||= !(@full_path =~ /#{ext}(\..*)*$/).nil? + else + result ||= !(@full_path =~ /\.#{ext}(\..*)*$/).nil? + end + result + end + + def is_haml? + result = false + ::HandlebarsAssets::Config.hamlbars_extensions.each do |ext| + result ||= check_extension(ext) + end + result + end + + def is_slim? + result = false + ::HandlebarsAssets::Config.slimbars_extensions.each do |ext| + result ||= check_extension(ext) + end + result + end + + def is_ember? + result = false + ::HandlebarsAssets::Config.ember_extensions.each do |ext| + result ||= check_extension(ext) + end + result + end + + def is_partial? + @full_path.gsub(%r{.*/}, '').start_with?('_') + end + + def name + template_name + end + + private + + def relative_path + path = @full_path.match(/.*#{HandlebarsAssets::Config.path_prefix}\/((.*\/)*([^.]*)).*$/)[1] + if is_partial? && ::HandlebarsAssets::Config.chomp_underscore_for_partials? + #handle case if partial is in root level of template folder + path.gsub!(%r~^_~, '') + #handle case if partial is in a subfolder within the template folder + path.gsub!(%r~/_~, '/') + end + path + end + + def template_name + relative_path.dump + end + end + end +end diff --git a/lib/handlebars_assets/tilt_handlebars.rb b/lib/handlebars_assets/tilt_handlebars.rb deleted file mode 100644 index d677591..0000000 --- a/lib/handlebars_assets/tilt_handlebars.rb +++ /dev/null @@ -1,129 +0,0 @@ -require 'tilt' -require 'multi_json' - -module HandlebarsAssets - module Unindent - # http://bit.ly/aze9FV - # Strip leading whitespace from each line that is the same as the - # amount of whitespace on the first line of the string. - # Leaves _additional_ indentation on later lines intact. - def unindent(heredoc) - heredoc.gsub /^#{heredoc[/\A\s*/]}/, '' - end - end - - class TiltHandlebars < Tilt::Template - - include Unindent - - def self.default_mime_type - 'application/javascript' - end - - def evaluate(scope, locals, &block) - template_path = TemplatePath.new(scope) - - source = if template_path.is_haml? - Haml::Engine.new(data, HandlebarsAssets::Config.haml_options).render(scope, locals) - elsif template_path.is_slim? - Slim::Template.new(HandlebarsAssets::Config.slim_options) { data }.render(scope, locals) - else - data - end - - if HandlebarsAssets::Config.multiple_frameworks? && HandlebarsAssets::Config.ember? - if template_path.is_ember? - compile_ember(source, template_path) - else - compile_default(source, template_path) - end - elsif HandlebarsAssets::Config.ember? && !HandlebarsAssets::Config.multiple_frameworks? - compile_ember(source, template_path) - else - compile_default(source, template_path) - end - end - - def initialize_engine - begin - require 'haml' - rescue LoadError - # haml not available - end - begin - require 'slim' - rescue LoadError - # slim not available - end - end - - def compile_ember(source, template_path) - "window.Ember.TEMPLATES[#{template_path.name}] = Ember.Handlebars.compile(#{::MultiJson.dump source});" - end - - def compile_default(source, template_path) - compiled_hbs = Handlebars.precompile(source, HandlebarsAssets::Config.options) - - template_namespace = HandlebarsAssets::Config.template_namespace - - if template_path.is_partial? - unindent <<-PARTIAL - (function() { - Handlebars.registerPartial(#{template_path.name}, Handlebars.template(#{compiled_hbs})); - }).call(this); - PARTIAL - else - unindent <<-TEMPLATE - (function() { - this.#{template_namespace} || (this.#{template_namespace} = {}); - this.#{template_namespace}[#{template_path.name}] = Handlebars.template(#{compiled_hbs}); - return this.#{template_namespace}[#{template_path.name}]; - }).call(this); - TEMPLATE - end - end - - protected - - def prepare; end - - class TemplatePath - def initialize(scope) - self.full_path = scope.pathname - self.template_path = scope.logical_path - end - - def is_haml? - full_path.to_s.end_with?('.hamlbars') - end - - def is_slim? - full_path.to_s.end_with?('.slimbars') - end - - def is_partial? - template_path.gsub(%r{.*/}, '').start_with?('_') - end - - def is_ember? - full_path.to_s =~ %r{.ember(.hbs|.hamlbars|.slimbars)?$} - end - - def name - template_name - end - - private - - attr_accessor :full_path, :template_path - - def relative_path - template_path.gsub(/^#{HandlebarsAssets::Config.path_prefix}\/(.*)$/i, "\\1") - end - - def template_name - relative_path.dump - end - end - end -end diff --git a/lib/handlebars_assets/version.rb b/lib/handlebars_assets/version.rb index ba8204f..9592aff 100644 --- a/lib/handlebars_assets/version.rb +++ b/lib/handlebars_assets/version.rb @@ -1,3 +1,3 @@ module HandlebarsAssets - VERSION = "0.15" + VERSION = "0.23.9" end diff --git a/test/handlebars_assets/compiling_test.rb b/test/handlebars_assets/compiling_test.rb index e627969..e3dc992 100644 --- a/test/handlebars_assets/compiling_test.rb +++ b/test/handlebars_assets/compiling_test.rb @@ -1,7 +1,7 @@ require 'test_helper' module HandlebarsAssets - class CompilingTest < Test::Unit::TestCase + class CompilingTest < ::MiniTest::Test def teardown HandlebarsAssets::Config.reset! diff --git a/test/handlebars_assets/hamlbars_test.rb b/test/handlebars_assets/hamlbars_test.rb index 4fe00d3..7afce25 100644 --- a/test/handlebars_assets/hamlbars_test.rb +++ b/test/handlebars_assets/hamlbars_test.rb @@ -1,12 +1,12 @@ require 'test_helper' module HandlebarsAssets - class HamlbarsTest < Test::Unit::TestCase + class HamlbarsTest < ::Minitest::Test include SprocketsScope include CompilerSupport def compile_haml(source) - Haml::Engine.new(source, HandlebarsAssets::Config.haml_options).render + (Haml::Engine.new(source, HandlebarsAssets::Config.haml_options).render).chomp end def teardown @@ -20,7 +20,7 @@ def test_render_haml scope = make_scope root, file source = "%p This is {{handlebars}}" - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } + template = HandlebarsAssets::HandlebarsTemplate.new(scope.pathname.to_s) { source } assert_equal hbs_compiled('test_render', compile_haml(source)), template.render(scope, {}) end diff --git a/test/handlebars_assets/handlebars_processor_test.rb b/test/handlebars_assets/handlebars_processor_test.rb new file mode 100644 index 0000000..c740ad5 --- /dev/null +++ b/test/handlebars_assets/handlebars_processor_test.rb @@ -0,0 +1,17 @@ +require 'test_helper' +require_relative 'shared/adapter_tests' + +module HandlebarsAssets + class HandlebarsProcessorTest < Minitest::Test + include AdapterTests + + def teardown + HandlebarsAssets::Config.reset! + HandlebarsAssets::Handlebars.reset! + end + + def render_it(scope, source) + HandlebarsAssets::HandlebarsProcessor.call(filename: scope.pathname.to_s, data: source) + end + end +end diff --git a/test/handlebars_assets/shared/adapter_tests.rb b/test/handlebars_assets/shared/adapter_tests.rb new file mode 100644 index 0000000..64f306f --- /dev/null +++ b/test/handlebars_assets/shared/adapter_tests.rb @@ -0,0 +1,199 @@ +require 'test_helper' +require 'haml' +require 'slim' + +module AdapterTests + include CompilerSupport + include SprocketsScope + + def compile_haml(source) + ::Haml::Engine.new(source, HandlebarsAssets::Config.haml_options).render + end + + def compile_slim(source) + ::Slim::Template.new(HandlebarsAssets::Config.slim_options) { source }.render + end + + def test_render + root = '/myapp/app/assets/templates' + file = 'test_render.hbs' + scope = make_scope root, file + source = "This is {{handlebars}}" + + assert_equal hbs_compiled('test_render', source), render_it(scope, source) + end + + # Sprockets does not add nested root paths (i.e. + # app/assets/javascripts/templates is rooted at app/assets/javascripts) + def test_template_misnaming + root = '/myapp/app/assets/javascripts' + file = 'templates/test_template_misnaming.hbs' + scope = make_scope root, file + source = "This is {{handlebars}}" + + assert_equal hbs_compiled('test_template_misnaming', source), render_it(scope, source) + end + + def test_path_prefix + root = '/myapp/app/assets/javascripts' + file = 'app/templates/test_path_prefix.hbs' + scope = make_scope root, file + source = "This is {{handlebars}}" + + HandlebarsAssets::Config.path_prefix = 'app/templates' + + assert_equal hbs_compiled('test_path_prefix', source), render_it(scope, source) + end + + def test_underscore_partials + root = '/myapp/app/assets/javascripts' + file1 = 'app/templates/_test_underscore.hbs' + scope1 = make_scope root, file1 + file2 = 'app/templates/some/thing/_test_underscore.hbs' + scope2 = make_scope root, file2 + source = "This is {{handlebars}}" + + HandlebarsAssets::Config.path_prefix = 'app/templates' + + assert_equal hbs_compiled_partial('_test_underscore', source), render_it(scope1, source) + + assert_equal hbs_compiled_partial('some/thing/_test_underscore', source), render_it(scope2, source) + end + + def test_chomped_underscore_partials + assert_equal HandlebarsAssets::Config.chomp_underscore_for_partials?, false + + HandlebarsAssets::Config.chomp_underscore_for_partials = true + assert_equal HandlebarsAssets::Config.chomp_underscore_for_partials?, true + + root = '/myapp/app/assets/javascripts' + file1 = 'app/templates/_test_underscore.hbs' + scope1 = make_scope root, file1 + file2 = 'app/templates/some/thing/_test_underscore.hbs' + scope2 = make_scope root, file2 + source = "This is {{handlebars}}" + + HandlebarsAssets::Config.path_prefix = 'app/templates' + + assert_equal hbs_compiled_partial('test_underscore', source), render_it(scope1, source) + + assert_equal hbs_compiled_partial('some/thing/test_underscore', source), render_it(scope2, source) + + end + + def test_without_known_helpers_opt + root = '/myapp/app/assets/templates' + file = 'test_without_known.hbs' + scope = make_scope root, file + source = "{{#with author}}By {{first_name}} {{last_name}}{{/with}}" + + assert_equal hbs_compiled('test_without_known', source), render_it(scope, source) + end + + def test_known_helpers_opt + root = '/myapp/app/assets/templates' + file = 'test_known.hbs' + scope = make_scope root, file + source = "{{#with author}}By {{first_name}} {{last_name}}{{/with}}" + + HandlebarsAssets::Config.known_helpers_only = true + + assert_equal hbs_compiled('test_known', source), render_it(scope, source) + end + + def test_with_custom_helpers + root = '/myapp/app/assets/templates' + file = 'test_custom_helper.hbs' + scope = make_scope root, file + source = "{{#custom author}}By {{first_name}} {{last_name}}{{/custom}}" + + assert_equal hbs_compiled('test_custom_helper', source), render_it(scope, source) + end + + def test_with_custom_known_helpers + root = '/myapp/app/assets/templates' + file = 'test_custom_known_helper.hbs' + scope = make_scope root, file + source = "{{#custom author}}By {{first_name}} {{last_name}}{{/custom}}" + + HandlebarsAssets::Config.known_helpers_only = true + HandlebarsAssets::Config.known_helpers = %w(custom) + + assert_equal hbs_compiled('test_custom_known_helper', source), render_it(scope, source) + end + + def test_template_namespace + root = '/myapp/app/assets/javascripts/templates' + file = 'test_template_namespace.hbs' + scope = make_scope root, file + source = "This is {{handlebars}}" + + HandlebarsAssets::Config.template_namespace = 'JST' + + assert_equal hbs_compiled('test_template_namespace', source), render_it(scope, source) + end + + def test_ember_render + root = '/myapp/app/assets/templates' + file = 'test_render.hbs' + scope = make_scope root, file + source = "This is {{handlebars}}" + + HandlebarsAssets::Config.ember = true + HandlebarsAssets::Config.multiple_frameworks = false + + expected_compiled = %{window.Ember.TEMPLATES["test_render"] = Ember.Handlebars.compile("This is {{handlebars}}");}; + assert_equal expected_compiled, render_it(scope, source) + end + + def test_multiple_frameworks_with_ember_render + root = '/myapp/app/assets/templates' + non_ember = 'test_render.hbs' + non_ember_but_with_ember = 'test_member.hbs' + ember_ext_no_hbs = 'test_render.ember' + ember_ext = 'test_render.ember.hbs' + ember_with_haml = 'test_render.ember.hamlbars' + ember_with_slim = 'test_render.ember.slimbars' + ember_ext_with_erb = 'test_render.ember.hbs.erb' + + HandlebarsAssets::Config.ember = true + HandlebarsAssets::Config.multiple_frameworks = true + + # File without ember extension should compile to default namespace + scope = make_scope root, non_ember + source = "This is {{handlebars}}" + assert_equal hbs_compiled('test_render', source), render_it(scope, source) + + # File without ember extension but with ember in it should compile to default namespace + scope = make_scope root, non_ember_but_with_ember + source = "This is {{handlebars}}" + assert_equal hbs_compiled('test_member', source), render_it(scope, source) + + # File with ember extension should compile to ember specific namespace + expected_compiled = %{window.Ember.TEMPLATES["test_render"] = Ember.Handlebars.compile("This is {{handlebars}}");}; + scope = make_scope root, ember_ext_no_hbs + assert_equal expected_compiled, render_it(scope, source) + + # File with ember and erb extension should compile to ember specific namespace + expected_compiled = %{window.Ember.TEMPLATES["test_render"] = Ember.Handlebars.compile("This is {{handlebars}}");}; + scope = make_scope root, ember_ext_with_erb + assert_equal expected_compiled, render_it(scope, source) + + # File with ember.hbs extension should compile to ember specific namespace + expected_compiled = %{window.Ember.TEMPLATES["test_render"] = Ember.Handlebars.compile("This is {{handlebars}}");}; + scope = make_scope root, ember_ext + assert_equal expected_compiled, render_it(scope, source) + + # File with ember.hamlbars extension should compile to ember specific namespace + expected_compiled = %{window.Ember.TEMPLATES["test_render"] = Ember.Handlebars.compile("

This is {{handlebars}}

");}; + scope = make_scope root, ember_with_haml + source = "%p This is {{handlebars}}" + assert_equal expected_compiled, render_it(scope, source) + + # File with ember.slimbars extension should compile to ember specific namespace + expected_compiled = %{window.Ember.TEMPLATES["test_render"] = Ember.Handlebars.compile("

This is {{handlebars}}

");}; + source = "p This is {{handlebars}}" + scope = make_scope root, ember_with_slim + assert_equal expected_compiled, render_it(scope, source) + end +end diff --git a/test/handlebars_assets/slimbars_test.rb b/test/handlebars_assets/slimbars_test.rb index 1087900..f162d81 100644 --- a/test/handlebars_assets/slimbars_test.rb +++ b/test/handlebars_assets/slimbars_test.rb @@ -1,7 +1,7 @@ require 'test_helper' module HandlebarsAssets - class SlimbarsTest < Test::Unit::TestCase + class SlimbarsTest < ::Minitest::Test include SprocketsScope include CompilerSupport @@ -20,7 +20,7 @@ def test_render_slim scope = make_scope root, file source = "p This is {{handlebars}}" - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } + template = HandlebarsAssets::HandlebarsTemplate.new(scope.pathname.to_s) { source } assert_equal hbs_compiled('test_render', compile_slim(source)), template.render(scope, {}) end diff --git a/test/handlebars_assets/tilt_handlebars_test.rb b/test/handlebars_assets/tilt_handlebars_test.rb index d1b1c25..b700fcf 100644 --- a/test/handlebars_assets/tilt_handlebars_test.rb +++ b/test/handlebars_assets/tilt_handlebars_test.rb @@ -1,196 +1,18 @@ require 'test_helper' +require_relative 'shared/adapter_tests' module HandlebarsAssets - class TiltHandlebarsTest < Test::Unit::TestCase - include CompilerSupport - include SprocketsScope + class HandlebarsTemplateTest < Minitest::Test + include AdapterTests def teardown HandlebarsAssets::Config.reset! HandlebarsAssets::Handlebars.reset! end - def compile_haml(source) - Haml::Engine.new(source, HandlebarsAssets::Config.haml_options).render - end - - def compile_slim(source) - Slim::Template.new(HandlebarsAssets::Config.slim_options) { source }.render - end - - def test_render - root = '/myapp/app/assets/templates' - file = 'test_render.hbs' - scope = make_scope root, file - source = "This is {{handlebars}}" - - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } - - assert_equal hbs_compiled('test_render', source), template.render(scope, {}) - end - - # Sprockets does not add nested root paths (i.e. - # app/assets/javascripts/templates is rooted at app/assets/javascripts) - def test_template_misnaming - root = '/myapp/app/assets/javascripts' - file = 'templates/test_template_misnaming.hbs' - scope = make_scope root, file - source = "This is {{handlebars}}" - - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } - - assert_equal hbs_compiled('test_template_misnaming', source), template.render(scope, {}) - end - - def test_path_prefix - root = '/myapp/app/assets/javascripts' - file = 'app/templates/test_path_prefix.hbs' - scope = make_scope root, file - source = "This is {{handlebars}}" - - HandlebarsAssets::Config.path_prefix = 'app/templates' - - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } - - assert_equal hbs_compiled('test_path_prefix', source), template.render(scope, {}) - end - - def test_underscore_partials - root = '/myapp/app/assets/javascripts' - file1 = 'app/templates/_test_underscore.hbs' - scope1 = make_scope root, file1 - file2 = 'app/templates/some/thing/_test_underscore.hbs' - scope2 = make_scope root, file2 - source = "This is {{handlebars}}" - - HandlebarsAssets::Config.path_prefix = 'app/templates' - - template1 = HandlebarsAssets::TiltHandlebars.new(scope1.pathname.to_s) { source } - - assert_equal hbs_compiled_partial('_test_underscore', source), template1.render(scope1, {}) - - template2 = HandlebarsAssets::TiltHandlebars.new(scope2.pathname.to_s) { source } - - assert_equal hbs_compiled_partial('some/thing/_test_underscore', source), template2.render(scope2, {}) - end - - def test_without_known_helpers_opt - root = '/myapp/app/assets/templates' - file = 'test_without_known.hbs' - scope = make_scope root, file - source = "{{#with author}}By {{first_name}} {{last_name}}{{/with}}" - - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } - - assert_equal hbs_compiled('test_without_known', source), template.render(scope, {}) - end - - def test_known_helpers_opt - root = '/myapp/app/assets/templates' - file = 'test_known.hbs' - scope = make_scope root, file - source = "{{#with author}}By {{first_name}} {{last_name}}{{/with}}" - - HandlebarsAssets::Config.known_helpers_only = true - - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } - - assert_equal hbs_compiled('test_known', source), template.render(scope, {}) - end - - def test_with_custom_helpers - root = '/myapp/app/assets/templates' - file = 'test_custom_helper.hbs' - scope = make_scope root, file - source = "{{#custom author}}By {{first_name}} {{last_name}}{{/custom}}" - - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } - - assert_equal hbs_compiled('test_custom_helper', source), template.render(scope, {}) - end - - def test_with_custom_known_helpers - root = '/myapp/app/assets/templates' - file = 'test_custom_known_helper.hbs' - scope = make_scope root, file - source = "{{#custom author}}By {{first_name}} {{last_name}}{{/custom}}" - - HandlebarsAssets::Config.known_helpers_only = true - HandlebarsAssets::Config.known_helpers = %w(custom) - - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } - - assert_equal hbs_compiled('test_custom_known_helper', source), template.render(scope, {}) - end - - def test_template_namespace - root = '/myapp/app/assets/javascripts' - file = 'test_template_namespace.hbs' - scope = make_scope root, file - source = "This is {{handlebars}}" - - HandlebarsAssets::Config.template_namespace = 'JST' - - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } - - assert_equal hbs_compiled('test_template_namespace', source), template.render(scope, {}) - end - - def test_ember_render - root = '/myapp/app/assets/templates' - file = 'test_render.hbs' - scope = make_scope root, file - source = "This is {{handlebars}}" - - HandlebarsAssets::Config.ember = true - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } - - expected_compiled = %{window.Ember.TEMPLATES["test_render"] = Ember.Handlebars.compile("This is {{handlebars}}");}; - assert_equal expected_compiled, template.render(scope, {}) - end - - def test_multiple_frameworks_with_ember_render - root = '/myapp/app/assets/templates' - non_ember = 'test_render.hbs' - ember_ext_no_hbs = 'test_render.ember' - ember_ext = 'test_render.ember.hbs' - ember_with_haml = 'test_render.ember.hamlbars' - ember_with_slim = 'test_render.ember.slimbars' - - HandlebarsAssets::Config.ember = true - HandlebarsAssets::Config.multiple_frameworks = true - - # File without ember extension should compile to default namespace - scope = make_scope root, non_ember - source = "This is {{handlebars}}" - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } - assert_equal hbs_compiled('test_render', source), template.render(scope, {}) - - # File with ember extension should compile to ember specific namespace - expected_compiled = %{window.Ember.TEMPLATES["test_render"] = Ember.Handlebars.compile("This is {{handlebars}}");}; - scope = make_scope root, ember_ext_no_hbs - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } - assert_equal expected_compiled, template.render(scope, {}) - - # File with ember.hbs extension should compile to ember specific namespace - expected_compiled = %{window.Ember.TEMPLATES["test_render"] = Ember.Handlebars.compile("This is {{handlebars}}");}; - scope = make_scope root, ember_ext - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { source } - assert_equal expected_compiled, template.render(scope, {}) - - # File with ember.hamlbars extension should compile to ember specific namespace - expected_compiled = %{window.Ember.TEMPLATES["test_render"] = Ember.Handlebars.compile("

This is {{handlebars}}

\\n");}; - scope = make_scope root, ember_with_haml - source = "%p This is {{handlebars}}" - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { compile_haml(source) } - assert_equal expected_compiled, template.render(scope, {}) - - # File with ember.slimbars extension should compile to ember specific namespace - expected_compiled = %{window.Ember.TEMPLATES["test_render"] = Ember.Handlebars.compile("

This is {{handlebars}}

");}; - source = "p This is {{handlebars}}" - scope = make_scope root, ember_with_slim - template = HandlebarsAssets::TiltHandlebars.new(scope.pathname.to_s) { compile_slim(source) } - assert_equal expected_compiled, template.render(scope, {}) + def render_it(scope, source) + template = HandlebarsAssets::HandlebarsTemplate.new(scope.pathname.to_s) { source } + template.render(scope, {}) end end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 2824d82..8518570 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,9 +1,9 @@ require 'handlebars_assets' require 'handlebars_assets/config' -require 'handlebars_assets/tilt_handlebars' +require 'handlebars_assets/handlebars_template' require 'handlebars_assets/handlebars' -require 'test/unit' +require 'minitest/autorun' module SprocketsScope # Try to act like sprockets. @@ -23,29 +23,29 @@ module CompilerSupport def compile_hbs(source) compiler_src = Pathname(HandlebarsAssets::Config.compiler_path).join(HandlebarsAssets::Config.compiler).read - ExecJS.compile(compiler_src).call('Handlebars.precompile', source, HandlebarsAssets::Config.options) + ExecJS.compile("var window = {}; " + compiler_src).call('Handlebars.precompile', source, HandlebarsAssets::Config.options) end def hbs_compiled(template_name, source) - compiled_hbs = compile_hbs(source) + compiled_hbs = compile_hbs(source).strip template_namespace = HandlebarsAssets::Config.template_namespace - unindent <<-END_EXPECTED - (function() { - this.#{template_namespace} || (this.#{template_namespace} = {}); - this.#{template_namespace}[#{template_name.dump}] = Handlebars.template(#{compiled_hbs}); - return this.#{template_namespace}[#{template_name.dump}]; - }).call(this); + <<-END_EXPECTED +(function() { + this.#{template_namespace} || (this.#{template_namespace} = {}); + this.#{template_namespace}[#{template_name.dump}] = Handlebars.template(#{compiled_hbs}); + return this.#{template_namespace}[#{template_name.dump}]; +}).call(this); END_EXPECTED end def hbs_compiled_partial(partial_name, source) compiled_hbs = compile_hbs(source) - unindent <<-END_EXPECTED - (function() { - Handlebars.registerPartial(#{partial_name.dump}, Handlebars.template(#{compiled_hbs})); - }).call(this); + <<-END_EXPECTED +(function() { + Handlebars.registerPartial(#{partial_name.dump}, Handlebars.template(#{compiled_hbs})); +}).call(this); END_EXPECTED end end @@ -55,6 +55,7 @@ module Config extend self def reset! + @chomp_underscore_for_partials = nil @compiler = nil @compiler_path = nil @haml_options = nil