diff --git a/README.textile b/README.textile index e8781447366..fbf28b62ce2 100644 --- a/README.textile +++ b/README.textile @@ -177,6 +177,18 @@ converted. Layouts don't need to. Haml content is intentionally not filtered, so you can use any Ruby code. +If you want to define methods you can call from your Haml files, create +a _helpers.rb file in the root of your blog and put the methods there, +inside a module named Helpers. + +Jekyll provides some helpers out of the box: + + h(string) + HTML entity-escapes the input string. + + link_to(text, url) + Creates a link to the URL with the linked text (or markup). + h3. Local Server When previewing complex sites locally, simply opening the site in a web diff --git a/lib/jekyll/convertible.rb b/lib/jekyll/convertible.rb index af5cc02cb95..f2a12556cb2 100644 --- a/lib/jekyll/convertible.rb +++ b/lib/jekyll/convertible.rb @@ -59,6 +59,18 @@ def content_type end return 'unknown' end + + # Sets up a context for Haml and renders in it. The context has accessors + # matching the passed-in hash, e.g. "site", "page" and "content", and has + # helper modules mixed in. + # + # Returns String. + def render_haml_in_context(haml_engine, params={}) + context = ClosedStruct.new(params) + context.extend(HamlHelpers) + context.extend(::Helpers) if defined?(::Helpers) + haml_engine.render(context) + end # Add any necessary layouts to this convertible document # +layouts+ is a Hash of {"name" => "layout"} @@ -72,11 +84,10 @@ def do_layout(payload, layouts) payload["content_type"] = self.content_type if self.content_type == "haml" - context = OpenStruct.new(:site => self.site, :page => OpenStruct.new(payload["page"])) - context.extend(HamlHelpers) - self.transform - self.content = self.content.render(context) + self.content = render_haml_in_context(self.content, + :site => self.site, + :page => ClosedStruct.new(payload["page"])) else self.content = Liquid::Template.parse(self.content).render(payload, info) self.transform @@ -91,12 +102,10 @@ def do_layout(payload, layouts) payload = payload.deep_merge({"content" => self.output, "page" => layout.data}) if site.config['haml'] && layout.content.is_a?(Haml::Engine) - context = OpenStruct.new( - :page => OpenStruct.new(payload["page"]), - :site => OpenStruct.new(payload["site"]), + self.output = render_haml_in_context(layout.content, + :site => ClosedStruct.new(payload["site"]), + :page => ClosedStruct.new(payload["page"]), :content => payload["content"]) - context.extend(HamlHelpers) - self.output = layout.content.render(context) else self.output = Liquid::Template.parse(layout.content).render(payload, info) end diff --git a/lib/jekyll/core_ext.rb b/lib/jekyll/core_ext.rb index 18159fbf480..5ac7323ec86 100644 --- a/lib/jekyll/core_ext.rb +++ b/lib/jekyll/core_ext.rb @@ -19,4 +19,11 @@ def deep_merge(hash) target end -end \ No newline at end of file +end + +require 'ostruct' +class ClosedStruct < OpenStruct + def method_missing(symbol, *args) + @table.has_key?(symbol) ? super : raise(NoMethodError, "undefined method `#{symbol}' for #{self}") + end +end diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index 0238cc86271..013d03ba847 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -50,8 +50,9 @@ def setup if self.config['haml'] begin require 'haml' - require 'ostruct' require 'jekyll/haml_helpers' + helpers = File.join(source, '_helpers.rb') + require helpers if File.exist?(helpers) puts 'Enabled Haml' rescue LoadError puts 'You must have the haml gem installed first'