Module Haml::Util
In: lib/haml/util/subset_map.rb
lib/haml/util.rb

A module containing various useful functions.

Methods

Classes and Modules

Class Haml::Util::StaticConditionalContext
Class Haml::Util::SubsetMap

Constants

RUBY_VERSION = ::RUBY_VERSION.split(".").map {|s| s.to_i}   An array of ints representing the Ruby version number. @api public

Public Instance methods

Returns whether this environment is using ActionPack version 3.0.0 or greater.

@return [Boolean]

[Source]

     # File lib/haml/util.rb, line 283
283:     def ap_geq_3?
284:       # The ActionPack module is always loaded automatically in Rails >= 3
285:       return false unless defined?(ActionPack) && defined?(ActionPack::VERSION)
286: 
287:       version =
288:         if defined?(ActionPack::VERSION::MAJOR)
289:           ActionPack::VERSION::MAJOR
290:         else
291:           # Rails 1.2
292:           ActionPack::VERSION::Major
293:         end
294: 
295:       version >= 3
296:     end

Returns whether this environment is using ActionPack version 3.0.0.beta.3 or greater.

@return [Boolean]

[Source]

     # File lib/haml/util.rb, line 302
302:     def ap_geq_3_beta_3?
303:       # The ActionPack module is always loaded automatically in Rails >= 3
304:       return false unless defined?(ActionPack) && defined?(ActionPack::VERSION)
305: 
306:       version =
307:         if defined?(ActionPack::VERSION::MAJOR)
308:           ActionPack::VERSION::MAJOR
309:         else
310:           # Rails 1.2
311:           ActionPack::VERSION::Major
312:         end
313:       version >= 3 &&
314:         ((defined?(ActionPack::VERSION::TINY) &&
315:           ActionPack::VERSION::TINY.is_a?(Fixnum) &&
316:           ActionPack::VERSION::TINY >= 1) ||
317:          (defined?(ActionPack::VERSION::BUILD) &&
318:           ActionPack::VERSION::BUILD =~ /beta(\d+)/ &&
319:           $1.to_i >= 3))
320:     end

Assert that a given object (usually a String) is HTML safe according to Rails’ XSS handling, if it‘s loaded.

@param text [Object]

[Source]

     # File lib/haml/util.rb, line 362
362:     def assert_html_safe!(text)
363:       return unless rails_xss_safe? && text && !text.to_s.html_safe?
364:       raise Haml::Error.new("Expected #{text.inspect} to be HTML-safe.")
365:     end

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`.

@param name [to_s] The name of the class to get.

  For example, `:Error` will return `ActionView::TemplateError`
  or `ActionView::Template::Error`.

[Source]

     # File lib/haml/util.rb, line 330
330:     def av_template_class(name)
331:       return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
332:       return ActionView::Template.const_get(name.to_s)
333:     end

Returns information about the caller of the previous method.

@param entry [String] An entry in the `caller` list, or a similarly formatted string @return [[String, Fixnum, (String, nil)]] An array containing the filename, line, and method name of the caller.

  The method name may be nil

[Source]

     # File lib/haml/util.rb, line 219
219:     def caller_info(entry = caller[1])
220:       info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
221:       info[1] = info[1].to_i
222:       info
223:     end

Checks that the encoding of a string is valid in Ruby 1.9 and cleans up potential encoding gotchas like the UTF-8 BOM. If it‘s not, yields an error string describing the invalid character and the line on which it occurrs.

@param str [String] The string of which to check the encoding @yield [msg] A block in which an encoding error can be raised.

  Only yields if there is an encoding error

@yieldparam msg [String] The error message to be raised @return [String] `str`, potentially with encoding gotchas like BOMs removed

[Source]

     # File lib/haml/util.rb, line 403
403:     def check_encoding(str)
404:       if ruby1_8?
405:         return str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM
406:       elsif str.valid_encoding?
407:         # Get rid of the Unicode BOM if possible
408:         if str.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?$/
409:           return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '')
410:         else
411:           return str
412:         end
413:       end
414: 
415:       encoding = str.encoding
416:       newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding("binary"))
417:       str.force_encoding("binary").split(newlines).each_with_index do |line, i|
418:         begin
419:           line.encode(encoding)
420:         rescue Encoding::UndefinedConversionError => e
421:           yield <<MSG.rstrip, i + 1
422: Invalid #{encoding.name} character #{e.error_char.dump}
423: MSG
424:         end
425:       end
426:       return str
427:     end

This is used for methods in {Haml::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.

@overload def_static_method(klass, name, args, *vars, erb) @param klass [Module] The class on which to define the static method @param name [to_s] The (base) name of the static method @param args [Array<Symbol>] The names of the arguments to the defined methods

  (**not** to the ERB template)

@param vars [Array<Symbol>] The names of the static boolean variables

  to be made available to the ERB template

@param erb [String] The template for the method code

[Source]

     # File lib/haml/util.rb, line 564
564:     def def_static_method(klass, name, args, *vars)
565:       erb = vars.pop
566:       info = caller_info
567:       powerset(vars).each do |set|
568:         context = StaticConditionalContext.new(set).instance_eval {binding}
569:         klass.class_eval("def \#{static_method_name(name, *vars.map {|v| set.include?(v)})}(\#{args.join(', ')})\n  \#{ERB.new(erb).result(context)}\nend\n", info[0], info[1])
570:       end
571:     end

A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.

@param enum [Enumerable] The enumerable to get the enumerator for @param n [Fixnum] The size of each cons @return [Enumerator] The consed enumerator

[Source]

     # File lib/haml/util.rb, line 460
460:     def enum_cons(enum, n)
461:       ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
462:     end

A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.

@param enum [Enumerable] The enumerable to get the enumerator for @param n [Fixnum] The size of each slice @return [Enumerator] The consed enumerator

[Source]

     # File lib/haml/util.rb, line 469
469:     def enum_slice(enum, n)
470:       ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
471:     end

A version of `Enumerable#enum_with_index` that works in Ruby 1.8 and 1.9.

@param enum [Enumerable] The enumerable to get the enumerator for @return [Enumerator] The with-index enumerator

[Source]

     # File lib/haml/util.rb, line 451
451:     def enum_with_index(enum)
452:       ruby1_8? ? enum.enum_with_index : enum.each_with_index
453:     end

Flattens the first `n` nested arrays in a cross-version manner.

@param arr [Array] The array to flatten @param n [Fixnum] The number of levels to flatten @return [Array] The flattened array

[Source]

     # File lib/haml/util.rb, line 486
486:     def flatten(arr, n)
487:       return arr.flatten(n) unless ruby1_8_6?
488:       return arr if n == 0
489:       arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
490:     end

The same as `Kernel#warn`, but is silenced by \{silence_haml_warnings}.

@param msg [String]

[Source]

     # File lib/haml/util.rb, line 250
250:     def haml_warn(msg)
251:       return if @@silence_warnings
252:       warn(msg)
253:     end

Checks to see if a class has a given method. For example:

    Haml::Util.has?(:public_instance_method, String, :gsub) #=> true

Method collections like `Class#instance_methods` return strings in Ruby 1.8 and symbols in Ruby 1.9 and on, so this handles checking for them in a compatible way.

@param attr [to_s] The (singular) name of the method-collection method

  (e.g. `:instance_methods`, `:private_methods`)

@param klass [Module] The class to check the methods of which to check @param method [String, Symbol] The name of the method do check for @return [Boolean] Whether or not the given collection has the given method

[Source]

     # File lib/haml/util.rb, line 443
443:     def has?(attr, klass, method)
444:       klass.send("#{attr}s").include?(ruby1_8? ? method.to_s : method.to_sym)
445:     end

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`.

@param text [String, nil] @return [String, nil] `text`, marked as HTML-safe

[Source]

     # File lib/haml/util.rb, line 352
352:     def html_safe(text)
353:       return unless text
354:       return text.html_safe if defined?(ActiveSupport::SafeBuffer)
355:       text.html_safe!
356:     end

Intersperses a value in an enumerable, as would be done with `Array#join` but without concatenating the array together afterwards.

@param enum [Enumerable] @param val @return [Array]

[Source]

     # File lib/haml/util.rb, line 146
146:     def intersperse(enum, val)
147:       enum.inject([]) {|a, e| a << e << val}[0...-1]
148:     end

Computes a single longest common subsequence for `x` and `y`. If there are more than one longest common subsequences, the one returned is that which starts first in `x`.

@param x [Array] @param y [Array] @yield [a, b] An optional block to use in place of a check for equality

  between elements of `x` and `y`.

@yieldreturn [Object, nil] If the two values register as equal,

  this will return the value to use in the LCS array.

@return [Array] The LCS

[Source]

     # File lib/haml/util.rb, line 207
207:     def lcs(x, y, &block)
208:       x = [nil, *x]
209:       y = [nil, *y]
210:       block ||= proc {|a, b| a == b && a}
211:       lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
212:     end

Maps the key-value pairs of a hash according to a block. For example:

    map_hash({:foo => "bar", :baz => "bang"}) {|k, v| [k.to_s, v.to_sym]}
      #=> {"foo" => :bar, "baz" => :bang}

@param hash [Hash] The hash to map @yield [key, value] A block in which the key-value pairs are transformed @yieldparam [key] The hash key @yieldparam [value] The hash value @yieldreturn [(Object, Object)] The new value for the `[key, value]` pair @return [Hash] The mapped hash @see map_keys @see map_vals

[Source]

    # File lib/haml/util.rb, line 85
85:     def map_hash(hash, &block)
86:       to_hash(hash.map(&block))
87:     end

Maps the keys in a hash according to a block. For example:

    map_keys({:foo => "bar", :baz => "bang"}) {|k| k.to_s}
      #=> {"foo" => "bar", "baz" => "bang"}

@param hash [Hash] The hash to map @yield [key] A block in which the keys are transformed @yieldparam key [Object] The key that should be mapped @yieldreturn [Object] The new value for the key @return [Hash] The mapped hash @see map_vals @see map_hash

[Source]

    # File lib/haml/util.rb, line 50
50:     def map_keys(hash)
51:       to_hash(hash.map {|k, v| [yield(k), v]})
52:     end

Maps the values in a hash according to a block. For example:

    map_values({:foo => "bar", :baz => "bang"}) {|v| v.to_sym}
      #=> {:foo => :bar, :baz => :bang}

@param hash [Hash] The hash to map @yield [value] A block in which the values are transformed @yieldparam value [Object] The value that should be mapped @yieldreturn [Object] The new value for the value @return [Hash] The mapped hash @see map_keys @see map_hash

[Source]

    # File lib/haml/util.rb, line 67
67:     def map_vals(hash)
68:       to_hash(hash.map {|k, v| [k, yield(v)]})
69:     end

Concatenates all strings that are adjacent in an array, while leaving other elements as they are. For example:

    merge_adjacent_strings([1, "foo", "bar", 2, "baz"])
      #=> [1, "foobar", 2, "baz"]

@param enum [Enumerable] @return [Array] The enumerable with strings merged

[Source]

     # File lib/haml/util.rb, line 129
129:     def merge_adjacent_strings(enum)
130:       e = enum.inject([]) do |a, e|
131:         if e.is_a?(String) && a.last.is_a?(String)
132:           a.last << e
133:         else
134:           a << e
135:         end
136:         a
137:       end
138:     end

Returns the ASCII code of the given character.

@param c [String] All characters but the first are ignored. @return [Fixnum] The ASCII code of `c`.

[Source]

     # File lib/haml/util.rb, line 477
477:     def ord(c)
478:       ruby1_8? ? c[0] : c.ord
479:     end

Return an array of all possible paths through the given arrays.

@param arrs [Array<Array>] @return [Array<Arrays>]

@example paths([[1, 2], [3, 4], [5]]) #=>

  # [[1, 3, 5],
  #  [2, 3, 5],
  #  [1, 4, 5],
  #  [2, 4, 5]]

[Source]

     # File lib/haml/util.rb, line 190
190:     def paths(arrs)
191:       arrs.inject([[]]) do |paths, arr|
192:         flatten(arr.map {|e| paths.map {|path| path + [e]}}, 1)
193:       end
194:     end

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

    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]]

@param arr [Enumerable] @return [Set<Set>] The subsets of `arr`

[Source]

     # File lib/haml/util.rb, line 98
 98:     def powerset(arr)
 99:       arr.inject([Set.new].to_set) do |powerset, el|
100:         new_powerset = Set.new
101:         powerset.each do |subset|
102:           new_powerset << subset
103:           new_powerset << subset + [el]
104:         end
105:         new_powerset
106:       end
107:     end

Returns the environment of the Rails application, if this is running in a Rails context. Returns `nil` if no such environment is defined.

@return [String, nil]

[Source]

     # File lib/haml/util.rb, line 273
273:     def rails_env
274:       return Rails.env.to_s if defined?(Rails.root)
275:       return RAILS_ENV.to_s if defined?(RAILS_ENV)
276:       return nil
277:     end

Returns the root of the Rails application, if this is running in a Rails context. Returns `nil` if no such root is defined.

@return [String, nil]

[Source]

     # File lib/haml/util.rb, line 262
262:     def rails_root
263:       return Rails.root.to_s if defined?(Rails.root)
264:       return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
265:       return nil
266:     end

The class for the Rails SafeBuffer XSS protection class. This varies depending on Rails version.

@return [Class]

[Source]

     # File lib/haml/util.rb, line 371
371:     def rails_safe_buffer_class
372:       return ActionView::SafeBuffer if defined?(ActionView::SafeBuffer)
373:       ActiveSupport::SafeBuffer
374:     end

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.

@return [Boolean]

[Source]

     # File lib/haml/util.rb, line 342
342:     def rails_xss_safe?
343:       false
344:     end

Restricts a number to falling within a given range. Returns the number if it falls within the range, or the closest value in the range if it doesn‘t.

@param value [Numeric] @param range [Range<Numeric>] @return [Numeric]

[Source]

     # File lib/haml/util.rb, line 116
116:     def restrict(value, range)
117:       [[value, range.first].max, range.last].min
118:     end

Whether or not this is running under Ruby 1.8 or lower.

@return [Boolean]

[Source]

     # File lib/haml/util.rb, line 381
381:     def ruby1_8?
382:       Haml::Util::RUBY_VERSION[0] == 1 && Haml::Util::RUBY_VERSION[1] < 9
383:     end

Whether or not this is running under Ruby 1.8.6 or lower. Note that lower versions are not officially supported.

@return [Boolean]

[Source]

     # File lib/haml/util.rb, line 389
389:     def ruby1_8_6?
390:       ruby1_8? && Haml::Util::RUBY_VERSION[2] < 7
391:     end

Returns the path of a file relative to the Haml root directory.

@param file [String] The filename relative to the Haml root @return [String] The filename relative to the the working directory

[Source]

    # File lib/haml/util.rb, line 21
21:     def scope(file)
22:       File.join(Haml::ROOT_DIR, file)
23:     end

Tests the hash-equality of two sets in a cross-version manner. Aggravatingly, this is order-dependent in Ruby 1.8.6.

@param set1 [Set] @param set2 [Set] @return [Boolean] Whether or not the sets are hashcode equal

[Source]

     # File lib/haml/util.rb, line 508
508:     def set_eql?(set1, set2)
509:       return set1.eql?(set2) unless ruby1_8_6?
510:       set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
511:     end

Returns the hash code for a set in a cross-version manner. Aggravatingly, this is order-dependent in Ruby 1.8.6.

@param set [Set] @return [Fixnum] The order-independent hashcode of `set`

[Source]

     # File lib/haml/util.rb, line 497
497:     def set_hash(set)
498:       return set.hash unless ruby1_8_6?
499:       set.map {|e| e.hash}.uniq.sort.hash
500:     end

Silences all Haml warnings within a block.

@yield A block in which no Haml warnings will be printed

[Source]

     # File lib/haml/util.rb, line 239
239:     def silence_haml_warnings
240:       old_silence_warnings = @@silence_warnings
241:       @@silence_warnings = true
242:       yield
243:     ensure
244:       @@silence_warnings = old_silence_warnings
245:     end

Silence all output to STDERR within a block.

@yield A block in which no output will be printed to STDERR

[Source]

     # File lib/haml/util.rb, line 228
228:     def silence_warnings
229:       the_real_stderr, $stderr = $stderr, StringIO.new
230:       yield
231:     ensure
232:       $stderr = the_real_stderr
233:     end

Computes the name for a method defined via \{def_static_method}.

@param name [String] The base name of the static method @param vars [Array<Boolean>] The static variable assignment @return [String] The real name of the static method

[Source]

     # File lib/haml/util.rb, line 583
583:     def static_method_name(name, *vars)
584:       "#{name}_#{vars.map {|v| !!v}.join('_')}"
585:     end

Destructively strips whitespace from the beginning and end of the first and last elements, respectively, in the array (if those elements are strings).

@param arr [Array] @return [Array] `arr`

[Source]

     # File lib/haml/util.rb, line 173
173:     def strip_string_array(arr)
174:       arr.first.lstrip! if arr.first.is_a?(String)
175:       arr.last.rstrip! if arr.last.is_a?(String)
176:       arr
177:     end

Substitutes a sub-array of one array with another sub-array.

@param ary [Array] The array in which to make the substitution @param from [Array] The sequence of elements to replace with `to` @param to [Array] The sequence of elements to replace `from` with

[Source]

     # File lib/haml/util.rb, line 155
155:     def substitute(ary, from, to)
156:       res = ary.dup
157:       i = 0
158:       while i < res.size
159:         if res[i...i+from.size] == from
160:           res[i...i+from.size] = to
161:         end
162:         i += 1
163:       end
164:       res
165:     end

Converts an array of `[key, value]` pairs to a hash. For example:

    to_hash([[:foo, "bar"], [:baz, "bang"]])
      #=> {:foo => "bar", :baz => "bang"}

@param arr [Array<(Object, Object)>] An array of pairs @return [Hash] A hash

[Source]

    # File lib/haml/util.rb, line 33
33:     def to_hash(arr)
34:       arr.compact.inject({}) {|h, (k, v)| h[k] = v; h}
35:     end

[Validate]