diff --git a/lib/haml/engine.rb b/lib/haml/engine.rb index 4a708021..aec5ba13 100644 --- a/lib/haml/engine.rb +++ b/lib/haml/engine.rb @@ -120,19 +120,11 @@ module Haml :filters => { 'sass' => Sass::Engine, 'plain' => Haml::Filters::Plain, - 'preserve' => Haml::Filters::Preserve } - } - - if !NOT_LOADED.include? 'redcloth' - @options[:filters].merge!({ - 'redcloth' => RedCloth, + 'preserve' => Haml::Filters::Preserve, + 'redcloth' => Haml::Filters::RedCloth, 'textile' => Haml::Filters::Textile, - 'markdown' => Haml::Filters::Markdown - }) - end - if !NOT_LOADED.include? 'bluecloth' - @options[:filters]['markdown'] = Haml::Filters::Markdown - end + 'markdown' => Haml::Filters::Markdown } + } @options.rec_merge! l_options diff --git a/lib/haml/filters.rb b/lib/haml/filters.rb index 5a693a07..c548aa12 100644 --- a/lib/haml/filters.rb +++ b/lib/haml/filters.rb @@ -7,22 +7,12 @@ require 'erb' require 'sass/engine' require 'stringio' -volatile_requires = ['rubygems', 'redcloth', 'bluecloth'] -NOT_LOADED = [] unless defined?(NOT_LOADED) -volatile_requires.each do |file| - begin - require file - rescue LoadError - NOT_LOADED.push file - end -end +begin + require 'rubygems' +rescue LoadError; end class ERB; alias_method :render, :result; end -unless NOT_LOADED.include? 'bluecloth' - class BlueCloth; alias_method :render, :to_html; end -end - module Haml module Filters class Plain @@ -60,26 +50,66 @@ module Haml end end - unless NOT_LOADED.include? 'bluecloth' - Markdown = BlueCloth unless defined?(Markdown) + class LazyLoaded + def initialize(*reqs) + reqs[0...-1].each do |req| + begin + @required = req + require @required + return + rescue LoadError; end # RCov doesn't see this, but it is run + end + + begin + @required = reqs[-1] + require @required + rescue LoadError => e + classname = self.class.to_s.gsub(/\w+::/, '') + + if reqs.size == 1 + raise HamlError.new("Can't run #{classname} filter; required file '#{reqs.first}' not found") + else + raise HamlError.new("Can't run #{classname} filter; required #{reqs.map { |r| "'#{r}'" }.join(' or ')}, but none were found") + end + end + end + end + + class RedCloth < LazyLoaded + def initialize(text) + super('redcloth') + @engine = ::RedCloth.new(text) + end + + def render + @engine.to_html + end + end + + # Uses RedCloth to provide only Textile (not Markdown) parsing + class Textile < RedCloth + def render + @engine.to_html(:textile) + end end - unless NOT_LOADED.include? 'redcloth' - class ::RedCloth; alias_method :render, :to_html; end - - # Uses RedCloth to provide only Textile (not Markdown) parsing - class Textile < RedCloth - def render - self.to_html(:textile) + # Uses BlueCloth or RedCloth to provide only Markdown (not Textile) parsing + class Markdown < LazyLoaded + def initialize(text) + super('bluecloth', 'redcloth') + + if @required == 'bluecloth' + @engine = ::BlueCloth.new(text) + else + @engine = ::RedCloth.new(text) end end - unless defined?(Markdown) - # Uses RedCloth to provide only Markdown (not Textile) parsing - class Markdown < RedCloth - def render - self.to_html(:markdown) - end + def render + if @engine.is_a?(::BlueCloth) + @engine.to_html + else + @engine.to_html(:markdown) end end end diff --git a/test/haml/engine_test.rb b/test/haml/engine_test.rb index 9bcc1a7d..14830c6c 100644 --- a/test/haml/engine_test.rb +++ b/test/haml/engine_test.rb @@ -194,60 +194,41 @@ class EngineTest < Test::Unit::TestCase end def test_no_bluecloth - old_markdown = false - if defined?(Haml::Filters::Markdown) - old_markdown = Haml::Filters::Markdown - end - Kernel.module_eval do - alias_method :haml_old_require, :gem_original_require - - def gem_original_require(file) + def gem_original_require_with_bluecloth(file) raise LoadError if file == 'bluecloth' - haml_old_require(file) + gem_original_require_without_bluecloth(file) + end + alias_method :gem_original_require_without_bluecloth, :gem_original_require + alias_method :gem_original_require, :gem_original_require_with_bluecloth + end + + begin + assert_equal("

Foo

\t

- a\n- b

\n", + Haml::Engine.new(":markdown\n Foo\n ===\n - a\n - b").to_html) + rescue Haml::HamlError => e + if e.message == "Can't run Markdown filter; required 'bluecloth' or 'redcloth', but none were found" + puts "\nCouldn't require 'bluecloth' or 'redcloth'; skipping a test." + else + raise e end end - - if old_markdown - Haml::Filters.instance_eval do - remove_const 'Markdown' - end - end - - # This is purposefully redundant, so it doesn't stop - # haml/filters from being required later on. - require 'haml/../haml/filters' - - assert_equal("

Foo

\t

- a\n- b

\n", - Haml::Engine.new(":markdown\n Foo\n ===\n - a\n - b").to_html) - - Haml::Filters.instance_eval do - remove_const 'Markdown' - end - - Haml::Filters.const_set('Markdown', old_markdown) if old_markdown Kernel.module_eval do - alias_method :gem_original_require, :haml_old_require + alias_method :gem_original_require, :gem_original_require_without_bluecloth end - - NOT_LOADED.delete 'bluecloth' end def test_no_redcloth Kernel.module_eval do - alias_method :haml_old_require2, :gem_original_require - - def gem_original_require(file) + def gem_original_require_with_redcloth(file) raise LoadError if file == 'redcloth' - haml_old_require2(file) + gem_original_require_without_redcloth(file) end + alias_method :gem_original_require_without_redcloth, :gem_original_require + alias_method :gem_original_require, :gem_original_require_with_redcloth end - # This is purposefully redundant, so it doesn't stop - # haml/filters from being required later on. - require 'haml/../haml/../haml/filters' - begin Haml::Engine.new(":redcloth\n _foo_").to_html rescue Haml::HamlError @@ -256,10 +237,30 @@ class EngineTest < Test::Unit::TestCase end Kernel.module_eval do - alias_method :gem_original_require2, :haml_old_require + alias_method :gem_original_require, :gem_original_require_without_redcloth + end + end + + def test_no_redcloth_or_bluecloth + Kernel.module_eval do + def gem_original_require_with_redcloth_and_bluecloth(file) + raise LoadError if file == 'redcloth' || file == 'bluecloth' + gem_original_require_without_redcloth_and_bluecloth(file) + end + alias_method :gem_original_require_without_redcloth_and_bluecloth, :gem_original_require + alias_method :gem_original_require, :gem_original_require_with_redcloth_and_bluecloth end - NOT_LOADED.delete 'redcloth' + begin + Haml::Engine.new(":markdown\n _foo_").to_html + rescue Haml::HamlError + else + assert(false, "No exception raised!") + end + + Kernel.module_eval do + alias_method :gem_original_require, :gem_original_require_without_redcloth_and_bluecloth + end end def test_local_assigns_dont_modify_class diff --git a/test/haml/template_test.rb b/test/haml/template_test.rb index 5ee864a8..8979a874 100644 --- a/test/haml/template_test.rb +++ b/test/haml/template_test.rb @@ -49,11 +49,14 @@ class TemplateTest < Test::Unit::TestCase assert_equal(pair.first, pair.last, message) end end - test.call(@base.render(name)) - - # If eval's suppressed, the partial won't render anyway :p. - unless Haml::Template.options[:suppress_eval] - test.call(@base.render(:file => "partialize", :locals => { :name => name })) + begin + test.call(@base.render(name)) + rescue ActionView::TemplateError => e + if e.message =~ /Can't run [\w:]+ filter; required (one of|file) ((?:'\w+'(?: or )?)+)(, but none were found| not found)/ + puts "\nCouldn't require #{$2}; skipping a test." + else + raise e + end end end