2006-12-03 23:56:47 +00:00
|
|
|
require 'haml/helpers'
|
|
|
|
require 'haml/buffer'
|
2007-11-23 09:15:00 +00:00
|
|
|
require 'haml/precompiler'
|
2007-01-19 17:03:47 +00:00
|
|
|
require 'haml/filters'
|
2007-01-27 10:29:14 +00:00
|
|
|
require 'haml/error'
|
2007-03-25 08:20:33 +00:00
|
|
|
require 'haml/util'
|
2006-08-06 03:18:54 +00:00
|
|
|
|
2006-10-14 22:24:53 +00:00
|
|
|
module Haml
|
2006-12-17 16:45:07 +00:00
|
|
|
# This is the class where all the parsing and processing of the Haml
|
2006-10-14 22:24:53 +00:00
|
|
|
# template is done. It can be directly used by the user by creating a
|
2006-12-17 16:45:07 +00:00
|
|
|
# new instance and calling <tt>to_html</tt> to render the template. For example:
|
2006-11-04 06:36:16 +00:00
|
|
|
#
|
2007-02-27 18:57:17 +00:00
|
|
|
# template = File.read('templates/really_cool_template.haml')
|
2006-10-14 22:24:53 +00:00
|
|
|
# haml_engine = Haml::Engine.new(template)
|
|
|
|
# output = haml_engine.to_html
|
|
|
|
# puts output
|
2006-08-06 03:18:54 +00:00
|
|
|
class Engine
|
2007-11-23 09:15:00 +00:00
|
|
|
include Precompiler
|
|
|
|
|
2006-10-27 21:20:19 +00:00
|
|
|
# Allow reading and writing of the options hash
|
|
|
|
attr :options, true
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2006-11-21 05:54:26 +00:00
|
|
|
# Creates a new instace of Haml::Engine that will compile the given
|
2007-04-01 08:17:28 +00:00
|
|
|
# template string when <tt>render</tt> is called.
|
2006-12-18 01:31:11 +00:00
|
|
|
# See README for available options.
|
2006-10-27 21:20:19 +00:00
|
|
|
#
|
2006-10-30 06:59:57 +00:00
|
|
|
#--
|
|
|
|
# When adding options, remember to add information about them
|
2006-12-18 01:31:11 +00:00
|
|
|
# to README!
|
2006-10-30 06:59:57 +00:00
|
|
|
#++
|
2006-10-30 06:10:26 +00:00
|
|
|
#
|
2007-05-10 08:12:20 +00:00
|
|
|
def initialize(template, l_options = {})
|
2006-10-27 21:20:19 +00:00
|
|
|
@options = {
|
2006-10-30 06:10:26 +00:00
|
|
|
:suppress_eval => false,
|
2006-11-04 08:35:06 +00:00
|
|
|
:attr_wrapper => "'",
|
2007-01-19 17:03:47 +00:00
|
|
|
:locals => {},
|
2007-11-18 09:57:58 +00:00
|
|
|
:autoclose => ['meta', 'img', 'link', 'br', 'hr', 'input', 'area'],
|
2007-01-19 17:03:47 +00:00
|
|
|
:filters => {
|
|
|
|
'sass' => Sass::Engine,
|
2007-02-06 08:08:32 +00:00
|
|
|
'plain' => Haml::Filters::Plain,
|
2007-08-11 21:04:51 +00:00
|
|
|
'preserve' => Haml::Filters::Preserve,
|
|
|
|
'redcloth' => Haml::Filters::RedCloth,
|
2007-01-19 17:03:47 +00:00
|
|
|
'textile' => Haml::Filters::Textile,
|
2007-08-11 21:04:51 +00:00
|
|
|
'markdown' => Haml::Filters::Markdown }
|
|
|
|
}
|
2007-01-19 17:03:47 +00:00
|
|
|
|
2007-05-10 08:12:20 +00:00
|
|
|
@options.rec_merge! l_options
|
2007-01-19 17:03:47 +00:00
|
|
|
|
2007-03-27 20:24:29 +00:00
|
|
|
unless @options[:suppress_eval]
|
|
|
|
@options[:filters].merge!({
|
|
|
|
'erb' => ERB,
|
|
|
|
'ruby' => Haml::Filters::Ruby
|
|
|
|
})
|
|
|
|
end
|
2007-05-10 08:12:20 +00:00
|
|
|
@options[:filters].rec_merge! l_options[:filters] if l_options[:filters]
|
2007-03-27 20:24:29 +00:00
|
|
|
|
2007-01-03 07:27:11 +00:00
|
|
|
@template = template.strip #String
|
2006-10-14 22:24:53 +00:00
|
|
|
@to_close_stack = []
|
2006-11-18 07:00:10 +00:00
|
|
|
@output_tabs = 0
|
|
|
|
@template_tabs = 0
|
2007-01-27 07:10:49 +00:00
|
|
|
@index = 0
|
2006-10-21 20:07:01 +00:00
|
|
|
|
2006-11-04 06:36:16 +00:00
|
|
|
# This is the base tabulation of the currently active
|
|
|
|
# flattened block. -1 signifies that there is no such block.
|
|
|
|
@flat_spaces = -1
|
|
|
|
|
2007-09-19 06:11:47 +00:00
|
|
|
# Only do the first round of pre-compiling if we really need to.
|
2007-11-23 07:42:59 +00:00
|
|
|
precompile
|
2007-11-23 07:51:08 +00:00
|
|
|
rescue Haml::Error => e
|
|
|
|
e.add_backtrace_entry(@index, @options[:filename])
|
|
|
|
raise e
|
2006-09-12 04:14:21 +00:00
|
|
|
end
|
2006-09-29 19:59:21 +00:00
|
|
|
|
2006-11-21 05:54:26 +00:00
|
|
|
# Processes the template and returns the result as a string.
|
2007-11-23 12:14:02 +00: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.
|
|
|
|
# Note that Haml modifies the context,
|
|
|
|
# extending it with Haml::Helpers
|
|
|
|
# and performing various other modifications.
|
|
|
|
#
|
|
|
|
# If a block is passed to render,
|
|
|
|
# that block is run when +yield+ is called
|
|
|
|
# within the template.
|
|
|
|
#
|
|
|
|
# Note that due to some Ruby quirks,
|
|
|
|
# if scope is a Binding or Proc object and a block is given,
|
|
|
|
# the evaluation context may not be quite what the user expects.
|
|
|
|
# In particular, it's equivalent to passing <tt>eval("self", scope)</tt> as scope.
|
|
|
|
# This won't have an effect in most cases,
|
|
|
|
# but if you're relying on local variables defined in the context of scope,
|
|
|
|
# they won't work.
|
2007-11-23 12:38:56 +00:00
|
|
|
def render(scope = Object.new, &block)
|
|
|
|
buffer = Haml::Buffer.new(@options)
|
|
|
|
compile scope, buffer, &block
|
|
|
|
buffer.buffer
|
2006-10-21 20:07:01 +00:00
|
|
|
end
|
2007-02-01 06:59:08 +00:00
|
|
|
alias_method :to_html, :render
|
|
|
|
|
2007-11-23 09:15:00 +00:00
|
|
|
private
|
2006-08-06 03:18:54 +00:00
|
|
|
|
2006-10-14 22:24:53 +00:00
|
|
|
# Takes <tt>@precompiled</tt>, a string buffer of Ruby code, and
|
2007-11-23 12:23:28 +00:00
|
|
|
# evaluates it in the context of <tt>scope</tt>.
|
2007-11-23 07:02:07 +00:00
|
|
|
# The code in <tt>@precompiled</tt> populates
|
2007-11-23 12:23:28 +00:00
|
|
|
# <tt>buffer</tt> with the compiled XHTML code.
|
|
|
|
def compile(scope, buffer)
|
2007-11-23 12:14:02 +00:00
|
|
|
if scope.is_a?(Binding) || scope.is_a?(Proc)
|
2007-11-23 07:02:07 +00:00
|
|
|
scope_object = eval("self", scope)
|
2007-11-23 12:14:02 +00:00
|
|
|
scope = scope_object.instance_eval{binding} if block_given?
|
2007-11-23 07:02:07 +00:00
|
|
|
else
|
|
|
|
scope_object = scope
|
2007-11-23 12:14:02 +00:00
|
|
|
scope = scope_object.instance_eval{binding}
|
2007-11-23 07:02:07 +00:00
|
|
|
end
|
2007-09-19 06:11:47 +00:00
|
|
|
|
2007-11-23 12:14:02 +00:00
|
|
|
scope_object.send(:instance_variable_set, '@_haml_locals', @options[:locals])
|
|
|
|
set_locals = @options[:locals].keys.map { |k| "#{k} = @_haml_locals[#{k.inspect}]" }.join("\n")
|
|
|
|
eval(set_locals, scope)
|
|
|
|
|
2007-11-23 07:02:07 +00:00
|
|
|
scope_object.extend Haml::Helpers
|
|
|
|
scope_object.instance_eval do
|
2006-10-14 22:24:53 +00:00
|
|
|
@haml_stack ||= Array.new
|
|
|
|
@haml_stack.push(buffer)
|
2007-11-23 07:02:07 +00:00
|
|
|
end
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2006-10-14 22:24:53 +00:00
|
|
|
begin
|
2007-11-23 12:14:02 +00:00
|
|
|
eval(@precompiled, scope, '(haml-eval)')
|
2006-10-14 22:24:53 +00:00
|
|
|
rescue Exception => e
|
2007-11-23 09:43:26 +00:00
|
|
|
raise add_exception_info(e, scope_object)
|
2006-10-14 22:24:53 +00:00
|
|
|
end
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2006-10-14 22:24:53 +00:00
|
|
|
# Get rid of the current buffer
|
2007-11-23 07:02:07 +00:00
|
|
|
scope_object.instance_eval do
|
2006-10-14 22:24:53 +00:00
|
|
|
@haml_stack.pop
|
2006-11-04 06:36:16 +00:00
|
|
|
end
|
2006-07-19 22:23:01 +00:00
|
|
|
end
|
2007-11-23 09:43:26 +00:00
|
|
|
|
|
|
|
def add_exception_info(e, scope_object)
|
|
|
|
metaclass = class << e; self; end
|
|
|
|
metaclass.send(:include, Haml::Error)
|
|
|
|
|
|
|
|
eval_line = e.backtrace[0].scan(/:([0-9]*)/)[0][0].to_i
|
|
|
|
line_marker = @precompiled.split("\n")[0...eval_line].grep(/#haml_lineno: [0-9]+/)[-1]
|
|
|
|
e.add_backtrace_entry(line_marker ? line_marker.scan(/[0-9]+/)[0].to_i : -1, @options[:filename])
|
|
|
|
|
|
|
|
# Format Ruby compiler errors nicely
|
|
|
|
message = e.message.scan(/compile error\n\(haml-eval\):[0-9]*: (.*)/)[0]
|
|
|
|
metaclass.send(:define_method, :message) { "compile error: #{message}" } if message
|
|
|
|
|
|
|
|
return e
|
|
|
|
end
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
2006-09-14 19:12:45 +00:00
|
|
|
end
|