Class: Ember::Template

Inherits:
Object
  • Object
show all
Defined in:
lib/ember/template.rb

Defined Under Namespace

Classes: Program

Constant Summary

OPERATION_EVAL_EXPRESSION =
'='
OPERATION_COMMENT_LINE =
'#'
OPERATION_BEGIN_LAMBDA =
'|'
OPERATION_EVAL_TEMPLATE_FILE =
'+'
OPERATION_EVAL_TEMPLATE_STRING =
'~'
OPERATION_INSERT_PLAIN_FILE =
'<'
OPERATIONS =
[
  OPERATION_COMMENT_LINE,
  OPERATION_BEGIN_LAMBDA,
  OPERATION_EVAL_EXPRESSION,
  OPERATION_EVAL_TEMPLATE_FILE,
  OPERATION_EVAL_TEMPLATE_STRING,
  OPERATION_INSERT_PLAIN_FILE,
]
SILENT_OPERATIONS =
[
  OPERATION_COMMENT_LINE,
  OPERATION_BEGIN_LAMBDA,
]
VOCAL_OPERATIONS =
OPERATIONS - SILENT_OPERATIONS
DIRECTIVE_HEAD =
'<%'
DIRECTIVE_BODY =
'(?:(?#
  there is nothing here, before the alternation,
  because we want to match the "<%%>" base case
)|[^%](?:.(?!<%))*?)'
DIRECTIVE_TAIL =
'-?%>'
SHORTHAND_HEAD =
'%'
SHORTHAND_BODY =
'(?:(?#
  there is nothing here, before the alternation,
  because we want to match the "<%%>" base case
)|[^%].*)'
SHORTHAND_TAIL =
'$'
NEWLINE =
'\r?\n'
SPACING =
'[[:blank:]]*'
MARGIN_REGEXP =
/^#{SPACING}(?=\S)/o
LAMBDA_BEGIN_REGEXP =
/\b(do)\b\s*(\|.*?\|)?\s*$/
BLOCK_BEGIN_REGEXP =
build_keyword_regexp[
  # generic
  :begin,

  # conditional
  :if, :unless, :case,

  # loops
  :for, :while, :until,

  # scopes
  :def, :class, :module
]
BLOCK_CONTINUE_REGEXP =
build_keyword_regexp[
  # generic
  :rescue, :ensure,

  # conditional
  :else, :elsif, :when
]
BLOCK_END_REGEXP =
build_keyword_regexp[
  # generic
  :end
]

Class Method Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Template) initialize(input, options = {})

Builds a processor that evaluates eRuby directives in the given input according to the given options.

This processor transforms the given input into an executable Ruby program (provided by the #program method) which is then executed by the #render method on demand.

eRuby directives that contribute to the output of the given template are called “vocal” directives. Those that do not are called “silent” directives.

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :result_variable (String) — default: "_erbout"

    Name of the variable which stores the result of template evaluation during template evaluation.

  • :continue_result (Boolean) — default: false

    Append to the result variable if it already exists?

  • :source_file (String) — default: "SOURCE"

    Name of the file which contains the given input. This is shown in stack traces when reporting error messages.

  • :source_line (Integer) — default: 1

    Line number at which the given input exists in the :source_file. This is shown in stack traces when reporting error messages.

  • :shorthand (Boolean) — default: false

    Treat lines beginning with “%” as eRuby directives?

  • :infer_end (Boolean) — default: false

    Add missing <% end %> statements based on indentation?

  • :unindent (Boolean) — default: false

    Unindent the content of eRuby blocks--that is everything between the <% do %> and <% end %> tags--hierarchically?



50
51
52
53
54
# File 'lib/ember/template.rb', line 50

def initialize input, options = {}
  @options = options
  @render_context_id = object_id
  @compile = compile(input.to_s)
end

Class Method Details

+ (Object) buffer_from_block(content_block)

Returns the template evaluation result buffer associated with the given content block.



101
102
103
104
105
# File 'lib/ember/template.rb', line 101

def buffer_from_block content_block
  context = content_block.binding
  result_variable = eval(Program::RESULT_VARIABLE_BACKDOOR, context)
  eval result_variable.to_s, context
end

+ (Object) content_from_block(content_block, *content_block_args)

Invokes the given block while passing the given arguments to it and then returns an Array of things that the given block tried to append to its template evaluation result buffer.

If the given block did not try to append anything, then the result of invoking the given block is returned as an Array.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/ember/template.rb', line 115

def content_from_block content_block, *content_block_args
  context = content_block.binding

  orig_result_variable = eval(Program::RESULT_VARIABLE_BACKDOOR, context)
  temp_result_variable = "#{orig_result_variable}_#{context.object_id.abs}"
  eval "#{temp_result_variable} = #{orig_result_variable}", context

  begin
    # the content block appends to the result buffer when invoked.
    # so we temporarily replace the result buffer with an empty one,
    # invoke the content block, and viola!  we now have its content.
    block_content = eval("#{orig_result_variable} = []", context)
    return_value = content_block.call(*content_block_args)
  ensure
    eval "#{orig_result_variable} = #{temp_result_variable}", context
  end

  if block_content.empty?
    # no content was appended to the result buffer when the content
    # block was invoked because it did not contain any vocal directives.
    # so we return the return value of content block invocation instead.
    Array(return_value)
  else
    block_content
  end
end

+ (Object) load_file(path, options = {})

Builds a template whose body is read from the given source.

If the source is a relative path, it will be resolved relative to options[:source_file] if that is a valid path.



81
82
83
84
# File 'lib/ember/template.rb', line 81

def load_file path, options = {}
  path = resolve_path(path, options)
  new File.read(path), options.merge(:source_file => path)
end

+ (Object) read_file(path, options = {})

Returns the contents of the given file, which can be relative to the current template in which this command is being executed.

If the source is a relative path, it will be resolved relative to options[:source_file] if that is a valid path.



93
94
95
# File 'lib/ember/template.rb', line 93

def read_file path, options = {}
  File.read resolve_path(path, options)
end

+ (Object) wrap_content_block(content_block, *content_block_args, &wrapper)

Passes the given content block’s content (obtained by invoking the given content block with the given arguments) as an argument to given wrapper block. The result of the wrapper block is (1) appended to the template evaluation result buffer associated with the given content block and is (2) returned by this method.

Raises:

  • (ArgumentError)


149
150
151
152
153
154
155
156
157
158
# File 'lib/ember/template.rb', line 149

def wrap_content_block content_block, *content_block_args, &wrapper
  raise ArgumentError, 'wrapper block must be given' unless wrapper

  buffer = buffer_from_block(content_block)
  content = content_from_block(content_block, *content_block_args)

  wrapped_content = wrapper.call(content)
  buffer << wrapped_content
  wrapped_content
end

Instance Method Details

- (Object) program

Ruby source code assembled from the eRuby template provided as input to the constructor of this class.



60
61
62
# File 'lib/ember/template.rb', line 60

def program
  @compile
end

- (Object) render(context = Object.new.instance_eval('binding'))

Returns the result of executing the Ruby program for this template (provided by the #program method) inside the given context binding.



68
69
70
71
72
# File 'lib/ember/template.rb', line 68

def render context = Object.new.instance_eval('binding')
  eval @compile, context,
    (@options[:source_file] || :SOURCE).to_s,
    (@options[:source_line] || 1).to_i
end