Module: Haml::Util

Extended by:
Util
Included in:
Buffer, Compiler, Engine, Parser, Util
Defined in:
lib/haml/util.rb,
lib/haml/template.rb

Overview

A module containing various useful functions.

Defined Under Namespace

Classes: StaticConditionalContext

Instance Method Summary collapse

Instance Method Details

#av_template_class(name)

Returns an ActionView::Template* class. In pre-3.0 versions of Rails, most of these classes were of the form ActionView::TemplateFoo, while afterwards they were of the form ActionView;:Template::Foo.

Parameters:

  • name (#to_s)

    The name of the class to get. For example, :Error will return ActionView::TemplateError or ActionView::Template::Error.



65
66
67
68
# File 'lib/haml/util.rb', line 65

def av_template_class(name)
  return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
  return ActionView::Template.const_get(name.to_s)
end

#balance(scanner, start, finish, count = 0) ⇒ (String, String)

Moves a scanner through a balanced pair of characters. For example:

Foo (Bar (Baz bang) bop) (Bang (bop bip))
^                       ^
from                    to

Parameters:

  • scanner (StringScanner)

    The string scanner to move

  • start (Character)

    The character opening the balanced pair. A Fixnum in 1.8, a String in 1.9

  • finish (Character)

    The character closing the balanced pair. A Fixnum in 1.8, a String in 1.9

  • count (Fixnum) (defaults to: 0)

    The number of opening characters matched before calling this method

Returns:

  • ((String, String))

    The string matched within the balanced pair and the rest of the string. ["Foo (Bar (Baz bang) bop)", " (Bang (bop bip))"] in the example above.



296
297
298
299
300
301
302
303
304
305
306
# File 'lib/haml/util.rb', line 296

def balance(scanner, start, finish, count = 0)
  str = ''
  scanner = StringScanner.new(scanner) unless scanner.is_a? StringScanner
  regexp = Regexp.new("(.*?)[\\#{start.chr}\\#{finish.chr}]", Regexp::MULTILINE)
  while scanner.scan(regexp)
    str << scanner.matched
    count += 1 if scanner.matched[-1] == start
    count -= 1 if scanner.matched[-1] == finish
    return [str.strip, scanner.rest] if count == 0
  end
end

#caller_info(entry = caller[1]) ⇒ [String, Fixnum, (String, nil)]

Returns information about the caller of the previous method.

Parameters:

  • entry (String) (defaults to: caller[1])

    An entry in the #caller list, or a similarly formatted string

Returns:

  • ([String, Fixnum, (String, nil)])

    An array containing the filename, line, and method name of the caller. The method name may be nil



39
40
41
42
43
44
45
# File 'lib/haml/util.rb', line 39

def caller_info(entry = caller[1])
  info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
  info[1] = info[1].to_i
  # This is added by Rubinius to designate a block, but we don't care about it.
  info[2].sub!(/ \{\}\Z/, '') if info[2]
  info
end

#check_encoding(str)



103
104
105
# File 'lib/haml/util.rb', line 103

def check_encoding(str)
  str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM
end

#check_haml_encoding(str) {|msg| ... } ⇒ String

Like #check_encoding, but also checks for a Ruby-style -# coding: comment at the beginning of the template and uses that encoding if it exists.

The Haml encoding rules are simple. If a -# coding: comment exists, we assume that that’s the original encoding of the document. Otherwise, we use whatever encoding Ruby has.

Haml uses the same rules for parsing coding comments as Ruby. This means that it can understand Emacs-style comments (e.g. -*- encoding: "utf-8" -*-), and also that it cannot understand non-ASCII-compatible encodings such as UTF-16 and UTF-32.

Parameters:

  • str (String)

    The Haml template of which to check the encoding

Yields:

  • (msg)

    A block in which an encoding error can be raised. Only yields if there is an encoding error

Yield Parameters:

  • msg (String)

    The error message to be raised

Returns:

  • (String)

    The original string encoded properly

Raises:

  • (ArgumentError)

    if the document declares an unknown encoding



154
155
156
# File 'lib/haml/util.rb', line 154

def check_haml_encoding(str, &block)
  check_encoding(str, &block)
end

#contains_interpolation?(str) ⇒ Boolean

Returns:

  • (Boolean)


325
326
327
# File 'lib/haml/util.rb', line 325

def contains_interpolation?(str)
  str.include?('#{')
end

#def_static_method(klass, name, args, *vars, erb)

This is used for methods in Buffer that need to be very fast, and take a lot of boolean parameters that are known at compile-time. Instead of passing the parameters in normally, a separate method is defined for every possible combination of those parameters; these are then called using #static_method_name.

To define a static method, an ERB template for the method is provided. All conditionals based on the static parameters are done as embedded Ruby within this template. For example:

def_static_method(Foo, :my_static_method, [:foo, :bar], :baz, :bang, <<RUBY)
  <% if baz && bang %>
    return foo + bar
  <% elsif baz || bang %>
    return foo - bar
  <% else %>
    return 17
  <% end %>
RUBY

#static_method_name can be used to call static methods.

Parameters:

  • klass (Module)

    The class on which to define the static method

  • name (#to_s)

    The (base) name of the static method

  • args (Array<Symbol>)

    The names of the arguments to the defined methods (not to the ERB template)

  • vars (Array<Symbol>)

    The names of the static boolean variables to be made available to the ERB template



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/haml/util.rb', line 238

def def_static_method(klass, name, args, *vars)
  erb = vars.pop
  info = caller_info
  erb_object = (defined?(Erubis::TinyEruby) && Erubis::TinyEruby || ERB).new(erb)
  powerset(vars).each do |set|
    context = StaticConditionalContext.new(set).instance_eval {binding}
    method_content = erb_object.result(context)

    klass.class_eval(<<METHOD, info[0], info[1])
      def #{static_method_name(name, *vars.map {|v| set.include?(v)})}(#{args.join(', ')})
        #{method_content}
      end
METHOD
  end
end

#handle_interpolation(str) {|scan| ... } ⇒ String

Scans through a string looking for the interoplation-opening #{ and, when it’s found, yields the scanner to the calling code so it can handle it properly.

The scanner will have any backslashes immediately in front of the #{ as the second capture group (scan[2]), and the text prior to that as the first (scan[1]).

Yield Parameters:

  • scan (StringScanner)

    The scanner scanning through the string

Returns:

  • (String)

    The text remaining in the scanner after all #{s have been processed



273
274
275
276
277
# File 'lib/haml/util.rb', line 273

def handle_interpolation(str)
  scan = StringScanner.new(str)
  yield scan while scan.scan(/(.*?)(\\*)\#\{/)
  scan.rest
end

#html_safe(text) ⇒ String?

Returns the given text, marked as being HTML-safe. With older versions of the Rails XSS-safety mechanism, this destructively modifies the HTML-safety of text.

Parameters:

  • text (String, nil)

Returns:

  • (String, nil)

    text, marked as HTML-safe



87
88
89
90
# File 'lib/haml/util.rb', line 87

def html_safe(text)
  return unless text
  text.html_safe
end

#human_indentation(indentation) ⇒ String

Formats a string for use in error messages about indentation.

Parameters:

  • indentation (String)

    The string used for indentation

Returns:

  • (String)

    The name of the indentation (e.g. "12 spaces", "1 tab")



312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/haml/util.rb', line 312

def human_indentation(indentation)
  if !indentation.include?(?\t)
    noun = 'space'
  elsif !indentation.include?(?\s)
    noun = 'tab'
  else
    return indentation.inspect
  end

  singular = indentation.length == 1
  "#{indentation.length} #{noun}#{'s' unless singular}"
end

#inspect_obj(obj) ⇒ String

Like Object#inspect, but preserves non-ASCII characters rather than escaping them under Ruby 1.9.2. This is necessary so that the precompiled Haml template can be #encoded into @options[:encoding] before being evaluated.

Parameters:

  • obj (Object)

Returns:

  • (String)


181
182
183
# File 'lib/haml/util.rb', line 181

def inspect_obj(obj)
  return obj.inspect
end

#powerset(arr) ⇒ Set<Set>

Computes the powerset of the given array. This is the set of all subsets of the array.

Examples:

powerset([1, 2, 3]) #=>
  Set[Set[], Set[1], Set[2], Set[3], Set[1, 2], Set[2, 3], Set[1, 3], Set[1, 2, 3]]

Parameters:

  • arr (Enumerable)

Returns:

  • (Set<Set>)

    The subsets of arr



23
24
25
26
27
28
29
30
31
32
# File 'lib/haml/util.rb', line 23

def powerset(arr)
  arr.inject([Set.new].to_set) do |powerset, el|
    new_powerset = Set.new
    powerset.each do |subset|
      new_powerset << subset
      new_powerset << subset + [el]
    end
    new_powerset
  end
end

#rails_xss_safe?Boolean

Whether or not ActionView’s XSS protection is available and enabled, as is the default for Rails 3.0+, and optional for version 2.3.5+. Overridden in haml/template.rb if this is the case.

Returns:

  • (Boolean)


77
78
79
# File 'lib/haml/util.rb', line 77

def rails_xss_safe?
  false
end

#silence_warnings { ... }

Silence all output to STDERR within a block.

Yields:

  • A block in which no output will be printed to STDERR



50
51
52
53
54
55
# File 'lib/haml/util.rb', line 50

def silence_warnings
  the_real_stderr, $stderr = $stderr, StringIO.new
  yield
ensure
  $stderr = the_real_stderr
end

#static_method_name(name, *vars) ⇒ String

Computes the name for a method defined via #def_static_method.

Parameters:

  • name (String)

    The base name of the static method

  • vars (Array<Boolean>)

    The static variable assignment

Returns:

  • (String)

    The real name of the static method



259
260
261
# File 'lib/haml/util.rb', line 259

def static_method_name(name, *vars)
  :"#{name}_#{vars.map {|v| !!v}.join('_')}"
end

#unescape_interpolation(str, escape_html = nil)



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/haml/util.rb', line 329

def unescape_interpolation(str, escape_html = nil)
  res = ''
  rest = Haml::Util.handle_interpolation str.dump do |scan|
    escapes = (scan[2].size - 1) / 2
    res << scan.matched[0...-3 - escapes]
    if escapes % 2 == 1
      res << '#{'
    else
      content = eval('"' + balance(scan, ?{, ?}, 1)[0][0...-1] + '"')
      content = "Haml::Helpers.html_escape((#{content}))" if escape_html
      res << '#{' + content + "}"# Use eval to get rid of string escapes
    end
  end
  res + rest
end