2006-12-03 18:56:47 -05:00
|
|
|
require 'haml/helpers'
|
|
|
|
require 'haml/buffer'
|
2007-11-23 04:15:00 -05:00
|
|
|
require 'haml/precompiler'
|
2007-01-19 12:03:47 -05:00
|
|
|
require 'haml/filters'
|
2007-01-27 05:29:14 -05:00
|
|
|
require 'haml/error'
|
2006-08-05 23:18:54 -04:00
|
|
|
|
2006-10-14 18:24:53 -04:00
|
|
|
module Haml
|
2009-04-30 16:52:01 -04:00
|
|
|
# This is the frontend for using Haml programmatically.
|
|
|
|
# It can be directly used by the user by creating a
|
|
|
|
# new instance and calling \{#render} to render the template.
|
|
|
|
# For example:
|
2006-11-04 01:36:16 -05:00
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# template = File.read('templates/really_cool_template.haml')
|
|
|
|
# haml_engine = Haml::Engine.new(template)
|
|
|
|
# output = haml_engine.render
|
|
|
|
# puts output
|
2006-08-05 23:18:54 -04:00
|
|
|
class Engine
|
2007-11-23 04:15:00 -05:00
|
|
|
include Precompiler
|
|
|
|
|
2009-04-30 16:52:01 -04:00
|
|
|
# The options hash.
|
2009-06-18 16:40:57 -04:00
|
|
|
# See {file:HAML_REFERENCE.md#haml_options the Haml options documentation}.
|
2009-04-30 16:52:01 -04:00
|
|
|
#
|
|
|
|
# @return [Hash<Symbol, Object>]
|
2009-04-30 16:34:54 -04:00
|
|
|
attr_accessor :options
|
2006-11-04 01:36:16 -05:00
|
|
|
|
2009-04-30 16:52:01 -04:00
|
|
|
# The indentation used in the Haml document,
|
|
|
|
# or `nil` if the indentation is ambiguous
|
2008-05-31 23:33:05 -04:00
|
|
|
# (for example, for a single-level document).
|
2009-04-30 16:52:01 -04:00
|
|
|
#
|
|
|
|
# @return [String]
|
2009-04-30 16:34:54 -04:00
|
|
|
attr_accessor :indentation
|
2008-05-31 23:33:05 -04:00
|
|
|
|
2009-04-30 16:52:01 -04:00
|
|
|
# @return [Boolean] Whether or not the format is XHTML.
|
2008-02-26 13:57:45 -05:00
|
|
|
def xhtml?
|
2008-02-27 09:20:44 -05:00
|
|
|
not html?
|
2008-02-26 13:57:45 -05:00
|
|
|
end
|
|
|
|
|
2009-04-30 16:52:01 -04:00
|
|
|
# @return [Boolean] Whether or not the format is any flavor of HTML.
|
2008-02-27 09:16:21 -05:00
|
|
|
def html?
|
|
|
|
html4? or html5?
|
|
|
|
end
|
|
|
|
|
2009-04-30 16:52:01 -04:00
|
|
|
# @return [Boolean] Whether or not the format is HTML4.
|
2008-02-26 13:57:45 -05:00
|
|
|
def html4?
|
2008-02-29 14:00:03 -05:00
|
|
|
@options[:format] == :html4
|
2008-02-26 13:57:45 -05:00
|
|
|
end
|
|
|
|
|
2009-04-30 16:52:01 -04:00
|
|
|
# @return [Boolean] Whether or not the format is HTML5.
|
2008-02-27 09:16:21 -05:00
|
|
|
def html5?
|
2008-02-29 14:00:03 -05:00
|
|
|
@options[:format] == :html5
|
2008-02-27 09:16:21 -05:00
|
|
|
end
|
|
|
|
|
2009-07-01 03:19:46 -04:00
|
|
|
# The source code that is evaluated to produce the Haml document.
|
|
|
|
#
|
|
|
|
# In Ruby 1.9, this is automatically converted to the correct encoding
|
|
|
|
# (see {file:HAML_REFERENCE.md#encoding-option the `:encoding` option}).
|
|
|
|
#
|
|
|
|
# @return [String]
|
|
|
|
def precompiled
|
|
|
|
return @precompiled if ruby1_8?
|
|
|
|
return @precompiled.encode(Encoding.find(@options[:encoding]))
|
|
|
|
end
|
|
|
|
|
2009-04-30 16:52:01 -04:00
|
|
|
# Precompiles the Haml template.
|
2006-10-30 01:10:26 -05:00
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# @param template [String] The Haml template
|
|
|
|
# @param options [Hash<Symbol, Object>] An options hash;
|
2009-06-18 16:40:57 -04:00
|
|
|
# see {file:HAML_REFERENCE.md#haml_options the Haml options documentation}
|
2009-04-30 16:52:01 -04:00
|
|
|
# @raise [Haml::Error] if there's a Haml syntax error in the template
|
2007-11-23 11:40:44 -05:00
|
|
|
def initialize(template, options = {})
|
2006-10-27 17:20:19 -04:00
|
|
|
@options = {
|
2006-10-30 01:10:26 -05:00
|
|
|
:suppress_eval => false,
|
2006-11-04 03:35:06 -05:00
|
|
|
:attr_wrapper => "'",
|
2008-02-26 13:57:45 -05:00
|
|
|
|
2009-10-05 22:25:13 -04:00
|
|
|
# Don't forget to update the docs in doc-src/HAML_REFERENCE.md
|
|
|
|
# if you update these
|
2008-03-02 19:24:47 -05:00
|
|
|
:autoclose => %w[meta img link br hr input area param col base],
|
2009-10-05 22:25:13 -04:00
|
|
|
:preserve => %w[textarea pre code],
|
2008-03-02 19:24:47 -05:00
|
|
|
|
2008-02-13 04:30:21 -05:00
|
|
|
:filename => '(haml)',
|
2008-05-02 02:54:10 -04:00
|
|
|
:line => 1,
|
2008-02-24 17:10:30 -05:00
|
|
|
:ugly => false,
|
2008-03-14 19:39:19 -04:00
|
|
|
:format => :xhtml,
|
2009-07-01 03:19:46 -04:00
|
|
|
:escape_html => false,
|
2007-08-11 17:04:51 -04:00
|
|
|
}
|
2009-07-01 03:19:46 -04:00
|
|
|
unless ruby1_8?
|
|
|
|
@options[:encoding] = Encoding.default_internal || "utf-8"
|
|
|
|
end
|
2008-04-29 23:06:35 -04:00
|
|
|
@options.merge! options
|
2008-08-29 20:40:59 -04:00
|
|
|
@index = 0
|
2007-01-19 12:03:47 -05:00
|
|
|
|
2008-02-29 14:00:03 -05:00
|
|
|
unless [:xhtml, :html4, :html5].include?(@options[:format])
|
|
|
|
raise Haml::Error, "Invalid format #{@options[:format].inspect}"
|
2008-02-26 13:57:45 -05:00
|
|
|
end
|
|
|
|
|
2009-07-01 03:19:46 -04:00
|
|
|
if @options[:encoding] && @options[:encoding].is_a?(Encoding)
|
|
|
|
@options[:encoding] = @options[:encoding].name
|
|
|
|
end
|
|
|
|
|
2008-12-27 02:16:49 -05:00
|
|
|
# :eod is a special end-of-document marker
|
|
|
|
@template = (template.rstrip).split(/\r\n|\r|\n/) + [:eod, :eod]
|
2008-05-28 23:09:46 -04:00
|
|
|
@template_index = 0
|
2006-10-14 18:24:53 -04:00
|
|
|
@to_close_stack = []
|
2006-11-18 02:00:10 -05:00
|
|
|
@output_tabs = 0
|
|
|
|
@template_tabs = 0
|
2008-05-31 23:33:05 -04:00
|
|
|
@flat = false
|
2008-04-29 00:28:01 -04:00
|
|
|
@newlines = 0
|
2008-05-28 22:48:39 -04:00
|
|
|
@precompiled = ''
|
2008-12-28 00:06:53 -05:00
|
|
|
@to_merge = []
|
2008-05-28 22:48:39 -04:00
|
|
|
@tab_change = 0
|
2009-01-07 19:13:20 -05:00
|
|
|
@temp_count = 0
|
2006-11-04 01:36:16 -05:00
|
|
|
|
2007-11-23 02:42:59 -05:00
|
|
|
precompile
|
2008-05-29 04:30:29 -04:00
|
|
|
rescue Haml::Error => e
|
|
|
|
e.backtrace.unshift "#{@options[:filename]}:#{(e.line ? e.line + 1 : @index) + @options[:line] - 1}" if @index
|
2007-11-25 22:26:16 -05:00
|
|
|
raise
|
2006-09-12 00:14:21 -04:00
|
|
|
end
|
2006-09-29 15:59:21 -04:00
|
|
|
|
2006-11-21 00:54:26 -05:00
|
|
|
# Processes the template and returns the result as a string.
|
2007-11-23 07:14:02 -05:00
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# `scope` is the context in which the template is evaluated.
|
|
|
|
# If it's a `Binding` or `Proc` object,
|
|
|
|
# Haml uses it as the second argument to `Kernel#eval`;
|
|
|
|
# otherwise, Haml just uses its `#instance_eval` context.
|
2008-04-08 02:09:17 -04:00
|
|
|
#
|
2007-11-24 03:35:10 -05:00
|
|
|
# Note that Haml modifies the evaluation context
|
2009-04-30 16:52:01 -04:00
|
|
|
# (either the scope object or the `self` object of the scope binding).
|
|
|
|
# It extends {Haml::Helpers}, and various instance variables are set
|
|
|
|
# (all prefixed with `haml_`).
|
2007-11-24 03:35:10 -05:00
|
|
|
# For example:
|
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# s = "foobar"
|
|
|
|
# Haml::Engine.new("%p= upcase").render(s) #=> "<p>FOOBAR</p>"
|
2007-11-24 03:35:10 -05:00
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# # s now extends Haml::Helpers
|
|
|
|
# s.responds_to?(:html_attrs) #=> true
|
2007-11-24 03:35:10 -05:00
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# `locals` is a hash of local variables to make available to the template.
|
2007-11-24 03:35:10 -05:00
|
|
|
# For example:
|
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# Haml::Engine.new("%p= foo").render(Object.new, :foo => "Hello, world!") #=> "<p>Hello, world!</p>"
|
2007-11-23 07:14:02 -05:00
|
|
|
#
|
|
|
|
# If a block is passed to render,
|
2009-04-30 16:52:01 -04:00
|
|
|
# that block is run when `yield` is called
|
2007-11-23 07:14:02 -05:00
|
|
|
# within the template.
|
|
|
|
#
|
2007-11-24 03:35:10 -05:00
|
|
|
# Due to some Ruby quirks,
|
2009-04-30 16:52:01 -04:00
|
|
|
# if `scope` is a `Binding` or `Proc` object and a block is given,
|
2007-11-23 07:14:02 -05:00
|
|
|
# the evaluation context may not be quite what the user expects.
|
2009-04-30 16:52:01 -04:00
|
|
|
# In particular, it's equivalent to passing `eval("self", scope)` as `scope`.
|
2007-11-23 07:14:02 -05:00
|
|
|
# This won't have an effect in most cases,
|
2009-04-30 16:52:01 -04:00
|
|
|
# but if you're relying on local variables defined in the context of `scope`,
|
2007-11-23 07:14:02 -05:00
|
|
|
# they won't work.
|
2009-04-30 16:52:01 -04:00
|
|
|
#
|
|
|
|
# @param scope [Binding, Proc, Object] The context in which the template is evaluated
|
|
|
|
# @param locals [Hash<Symbol, Object>] Local variables that will be made available
|
|
|
|
# to the template
|
|
|
|
# @param block [#to_proc] A block that can be yielded to within the template
|
|
|
|
# @return [String] The rendered template
|
2007-11-24 03:35:10 -05:00
|
|
|
def render(scope = Object.new, locals = {}, &block)
|
2008-04-24 12:52:26 -04:00
|
|
|
buffer = Haml::Buffer.new(scope.instance_variable_get('@haml_buffer'), options_for_buffer)
|
2006-08-05 23:18:54 -04:00
|
|
|
|
2007-11-23 07:14:02 -05:00
|
|
|
if scope.is_a?(Binding) || scope.is_a?(Proc)
|
2007-11-23 02:02:07 -05:00
|
|
|
scope_object = eval("self", scope)
|
2007-11-23 07:14:02 -05:00
|
|
|
scope = scope_object.instance_eval{binding} if block_given?
|
2007-11-23 02:02:07 -05:00
|
|
|
else
|
|
|
|
scope_object = scope
|
2007-11-23 07:14:02 -05:00
|
|
|
scope = scope_object.instance_eval{binding}
|
2007-11-23 02:02:07 -05:00
|
|
|
end
|
2007-09-19 02:11:47 -04:00
|
|
|
|
2007-11-24 03:35:10 -05:00
|
|
|
set_locals(locals.merge(:_hamlout => buffer, :_erbout => buffer.buffer), scope, scope_object)
|
2007-11-23 07:14:02 -05:00
|
|
|
|
2007-11-23 02:02:07 -05:00
|
|
|
scope_object.instance_eval do
|
2007-11-23 11:32:03 -05:00
|
|
|
extend Haml::Helpers
|
2008-04-24 12:52:26 -04:00
|
|
|
@haml_buffer = buffer
|
2007-11-23 02:02:07 -05:00
|
|
|
end
|
2006-11-04 01:36:16 -05:00
|
|
|
|
2009-07-01 03:19:46 -04:00
|
|
|
eval(precompiled, scope, @options[:filename], @options[:line])
|
2006-11-04 01:36:16 -05:00
|
|
|
|
2006-10-14 18:24:53 -04:00
|
|
|
# Get rid of the current buffer
|
2007-11-23 02:02:07 -05:00
|
|
|
scope_object.instance_eval do
|
2008-04-24 12:52:26 -04:00
|
|
|
@haml_buffer = buffer.upper
|
2006-11-04 01:36:16 -05:00
|
|
|
end
|
2007-11-23 11:55:30 -05:00
|
|
|
|
2008-04-24 15:59:03 -04:00
|
|
|
buffer.buffer
|
2006-07-19 18:23:01 -04:00
|
|
|
end
|
2007-11-23 11:55:30 -05:00
|
|
|
alias_method :to_html, :render
|
|
|
|
|
2007-11-23 12:26:05 -05:00
|
|
|
# Returns a proc that, when called,
|
|
|
|
# renders the template and returns the result as a string.
|
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# `scope` works the same as it does for render.
|
2007-11-23 12:26:05 -05:00
|
|
|
#
|
2007-11-24 03:35:10 -05:00
|
|
|
# The first argument of the returned proc is a hash of local variable names to values.
|
|
|
|
# However, due to an unfortunate Ruby quirk,
|
|
|
|
# the local variables which can be assigned must be pre-declared.
|
2009-04-30 16:52:01 -04:00
|
|
|
# This is done with the `local_names` argument.
|
2007-11-24 03:35:10 -05:00
|
|
|
# For example:
|
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# # This works
|
|
|
|
# Haml::Engine.new("%p= foo").render_proc(Object.new, :foo).call :foo => "Hello!"
|
|
|
|
# #=> "<p>Hello!</p>"
|
|
|
|
#
|
|
|
|
# # This doesn't
|
|
|
|
# Haml::Engine.new("%p= foo").render_proc.call :foo => "Hello!"
|
|
|
|
# #=> NameError: undefined local variable or method `foo'
|
2007-11-24 03:35:10 -05:00
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# The proc doesn't take a block; any yields in the template will fail.
|
2007-11-24 03:35:10 -05:00
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# @param scope [Binding, Proc, Object] The context in which the template is evaluated
|
|
|
|
# @param local_names [Array<Symbol>] The names of the locals that can be passed to the proc
|
|
|
|
# @return [Proc] The proc that will run the template
|
2007-11-24 03:35:10 -05:00
|
|
|
def render_proc(scope = Object.new, *local_names)
|
2007-11-23 12:26:05 -05:00
|
|
|
if scope.is_a?(Binding) || scope.is_a?(Proc)
|
|
|
|
scope_object = eval("self", scope)
|
|
|
|
else
|
|
|
|
scope_object = scope
|
|
|
|
scope = scope_object.instance_eval{binding}
|
|
|
|
end
|
|
|
|
|
2007-11-25 22:26:16 -05:00
|
|
|
eval("Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {};" +
|
2008-05-02 02:54:10 -04:00
|
|
|
precompiled_with_ambles(local_names) + "}\n", scope, @options[:filename], @options[:line])
|
2007-11-23 12:26:05 -05:00
|
|
|
end
|
|
|
|
|
2009-04-30 16:52:01 -04:00
|
|
|
# Defines a method on `object` with the given name
|
2007-11-23 21:32:18 -05:00
|
|
|
# that renders the template and returns the result as a string.
|
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# If `object` is a class or module,
|
2007-11-23 21:32:18 -05:00
|
|
|
# the method will instead by defined as an instance method.
|
|
|
|
# For example:
|
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# t = Time.now
|
|
|
|
# Haml::Engine.new("%p\n Today's date is\n .date= self.to_s").def_method(t, :render)
|
|
|
|
# t.render #=> "<p>\n Today's date is\n <div class='date'>Fri Nov 23 18:28:29 -0800 2007</div>\n</p>\n"
|
2007-11-23 21:32:18 -05:00
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# Haml::Engine.new(".upcased= upcase").def_method(String, :upcased_div)
|
|
|
|
# "foobar".upcased_div #=> "<div class='upcased'>FOOBAR</div>\n"
|
2008-04-08 02:09:17 -04:00
|
|
|
#
|
2007-11-24 03:35:10 -05:00
|
|
|
# The first argument of the defined method is a hash of local variable names to values.
|
|
|
|
# However, due to an unfortunate Ruby quirk,
|
|
|
|
# the local variables which can be assigned must be pre-declared.
|
2009-04-30 16:52:01 -04:00
|
|
|
# This is done with the `local_names` argument.
|
2007-11-24 03:35:10 -05:00
|
|
|
# For example:
|
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# # This works
|
|
|
|
# obj = Object.new
|
|
|
|
# Haml::Engine.new("%p= foo").def_method(obj, :render, :foo)
|
|
|
|
# obj.render(:foo => "Hello!") #=> "<p>Hello!</p>"
|
2007-11-24 03:35:10 -05:00
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# # This doesn't
|
|
|
|
# obj = Object.new
|
|
|
|
# Haml::Engine.new("%p= foo").def_method(obj, :render)
|
|
|
|
# obj.render(:foo => "Hello!") #=> NameError: undefined local variable or method `foo'
|
2008-04-08 02:09:17 -04:00
|
|
|
#
|
2007-11-24 03:35:10 -05:00
|
|
|
# Note that Haml modifies the evaluation context
|
2009-04-30 16:52:01 -04:00
|
|
|
# (either the scope object or the `self` object of the scope binding).
|
|
|
|
# It extends {Haml::Helpers}, and various instance variables are set
|
|
|
|
# (all prefixed with `haml_`).
|
|
|
|
#
|
|
|
|
# @param object [Object, Module] The object on which to define the method
|
|
|
|
# @param name [String, Symbol] The name of the method to define
|
|
|
|
# @param local_names [Array<Symbol>] The names of the locals that can be passed to the proc
|
2007-11-24 03:35:10 -05:00
|
|
|
def def_method(object, name, *local_names)
|
2007-11-23 21:32:18 -05:00
|
|
|
method = object.is_a?(Module) ? :module_eval : :instance_eval
|
|
|
|
|
2007-11-25 22:26:16 -05:00
|
|
|
object.send(method, "def #{name}(_haml_locals = {}); #{precompiled_with_ambles(local_names)}; end",
|
2008-05-02 02:54:10 -04:00
|
|
|
@options[:filename], @options[:line])
|
2007-11-23 21:32:18 -05:00
|
|
|
end
|
|
|
|
|
2009-04-30 16:52:01 -04:00
|
|
|
protected
|
2007-11-23 04:43:26 -05:00
|
|
|
|
2009-04-30 16:52:01 -04:00
|
|
|
# Returns a subset of \{#options}: those that {Haml::Buffer} cares about.
|
|
|
|
# All of the values here are such that when `#inspect` is called on the hash,
|
|
|
|
# it can be `Kernel#eval`ed to get the same result back.
|
|
|
|
#
|
2009-06-18 16:40:57 -04:00
|
|
|
# See {file:HAML_REFERENCE.md#haml_options the Haml options documentation}.
|
2009-05-01 14:41:05 -04:00
|
|
|
#
|
2009-04-30 16:52:01 -04:00
|
|
|
# @return [Hash<Symbol, Object>] The options hash
|
2007-11-23 11:55:48 -05:00
|
|
|
def options_for_buffer
|
2008-02-13 04:30:21 -05:00
|
|
|
{
|
2008-04-11 12:44:15 -04:00
|
|
|
:autoclose => @options[:autoclose],
|
2008-03-02 19:24:47 -05:00
|
|
|
:preserve => @options[:preserve],
|
2008-02-13 04:30:21 -05:00
|
|
|
:attr_wrapper => @options[:attr_wrapper],
|
2008-02-29 20:56:38 -05:00
|
|
|
:ugly => @options[:ugly],
|
2009-07-01 03:19:46 -04:00
|
|
|
:format => @options[:format],
|
|
|
|
:encoding => @options[:encoding],
|
2008-02-13 04:30:21 -05:00
|
|
|
}
|
2007-11-23 11:55:48 -05:00
|
|
|
end
|
2009-04-30 16:52:01 -04:00
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def set_locals(locals, scope, scope_object)
|
|
|
|
scope_object.send(:instance_variable_set, '@_haml_locals', locals)
|
|
|
|
set_locals = locals.keys.map { |k| "#{k} = @_haml_locals[#{k.inspect}]" }.join("\n")
|
|
|
|
eval(set_locals, scope)
|
|
|
|
end
|
2006-06-30 11:14:44 -04:00
|
|
|
end
|
2006-09-14 15:12:45 -04:00
|
|
|
end
|