Module | Haml::Util |
In: |
lib/haml/util/subset_map.rb
lib/haml/util.rb |
A module containing various useful functions.
RUBY_VERSION | = | ::RUBY_VERSION.split(".").map {|s| s.to_i} | An array of ints representing the Ruby version number. @api public |
Returns whether this environment is using ActionPack version 3.0.0 or greater.
@return [Boolean]
# File lib/haml/util.rb, line 286 286: def ap_geq_3? 287: # The ActionPack module is always loaded automatically in Rails >= 3 288: return false unless defined?(ActionPack) && defined?(ActionPack::VERSION) 289: 290: version = 291: if defined?(ActionPack::VERSION::MAJOR) 292: ActionPack::VERSION::MAJOR 293: else 294: # Rails 1.2 295: ActionPack::VERSION::Major 296: end 297: 298: version >= 3 299: end
Returns whether this environment is using ActionPack version 3.0.0.beta.3 or greater.
@return [Boolean]
# File lib/haml/util.rb, line 305 305: def ap_geq_3_beta_3? 306: # The ActionPack module is always loaded automatically in Rails >= 3 307: return false unless defined?(ActionPack) && defined?(ActionPack::VERSION) 308: 309: version = 310: if defined?(ActionPack::VERSION::MAJOR) 311: ActionPack::VERSION::MAJOR 312: else 313: # Rails 1.2 314: ActionPack::VERSION::Major 315: end 316: version >= 3 && 317: ((defined?(ActionPack::VERSION::TINY) && 318: ActionPack::VERSION::TINY.is_a?(Fixnum) && 319: ActionPack::VERSION::TINY >= 1) || 320: (defined?(ActionPack::VERSION::BUILD) && 321: ActionPack::VERSION::BUILD =~ /beta(\d+)/ && 322: $1.to_i >= 3)) 323: end
Assert that a given object (usually a String) is HTML safe according to Rails’ XSS handling, if it‘s loaded.
@param text [Object]
# File lib/haml/util.rb, line 365 365: def assert_html_safe!(text) 366: return unless rails_xss_safe? && text && !text.to_s.html_safe? 367: raise Haml::Error.new("Expected #{text.inspect} to be HTML-safe.") 368: 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`.
# File lib/haml/util.rb, line 333 333: def av_template_class(name) 334: return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}") 335: return ActionView::Template.const_get(name.to_s) 336: 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
# 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
# File lib/haml/util.rb, line 406 406: def check_encoding(str) 407: if ruby1_8? 408: return str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM 409: elsif str.valid_encoding? 410: # Get rid of the Unicode BOM if possible 411: if str.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?$/ 412: return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '') 413: else 414: return str 415: end 416: end 417: 418: encoding = str.encoding 419: newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding("binary")) 420: str.force_encoding("binary").split(newlines).each_with_index do |line, i| 421: begin 422: line.encode(encoding) 423: rescue Encoding::UndefinedConversionError => e 424: yield <<MSG.rstrip, i + 1 425: Invalid #{encoding.name} character #{e.error_char.dump} 426: MSG 427: end 428: end 429: return str 430: 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
# File lib/haml/util.rb, line 567 567: def def_static_method(klass, name, args, *vars) 568: erb = vars.pop 569: info = caller_info 570: powerset(vars).each do |set| 571: context = StaticConditionalContext.new(set).instance_eval {binding} 572: 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]) 573: end 574: 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
# File lib/haml/util.rb, line 463 463: def enum_cons(enum, n) 464: ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n) 465: 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
# File lib/haml/util.rb, line 472 472: def enum_slice(enum, n) 473: ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n) 474: 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
# File lib/haml/util.rb, line 454 454: def enum_with_index(enum) 455: ruby1_8? ? enum.enum_with_index : enum.each_with_index 456: 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
# File lib/haml/util.rb, line 489 489: def flatten(arr, n) 490: return arr.flatten(n) unless ruby1_8_6? 491: return arr if n == 0 492: arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e} 493: end
The same as `Kernel#warn`, but is silenced by \{silence_haml_warnings}.
@param msg [String]
# 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
# File lib/haml/util.rb, line 446 446: def has?(attr, klass, method) 447: klass.send("#{attr}s").include?(ruby1_8? ? method.to_s : method.to_sym) 448: 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
# File lib/haml/util.rb, line 355 355: def html_safe(text) 356: return unless text 357: return text.html_safe if defined?(ActiveSupport::SafeBuffer) 358: text.html_safe! 359: 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]
# 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
# 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
# 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
# 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
# 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
# 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`.
# File lib/haml/util.rb, line 480 480: def ord(c) 481: ruby1_8? ? c[0] : c.ord 482: 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]]
# 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`
# 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]
# File lib/haml/util.rb, line 276 276: def rails_env 277: return Rails.env.to_s if defined?(Rails.root) 278: return RAILS_ENV.to_s if defined?(RAILS_ENV) 279: return nil 280: 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]
# File lib/haml/util.rb, line 262 262: def rails_root 263: if defined?(Rails.root) 264: return Rails.root.to_s if Rails.root 265: raise "ERROR: Rails.root is nil!" 266: end 267: return RAILS_ROOT.to_s if defined?(RAILS_ROOT) 268: return nil 269: end
The class for the Rails SafeBuffer XSS protection class. This varies depending on Rails version.
@return [Class]
# File lib/haml/util.rb, line 374 374: def rails_safe_buffer_class 375: return ActionView::SafeBuffer if defined?(ActionView::SafeBuffer) 376: ActiveSupport::SafeBuffer 377: 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]
# File lib/haml/util.rb, line 345 345: def rails_xss_safe? 346: false 347: 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]
# 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]
# File lib/haml/util.rb, line 384 384: def ruby1_8? 385: Haml::Util::RUBY_VERSION[0] == 1 && Haml::Util::RUBY_VERSION[1] < 9 386: end
Whether or not this is running under Ruby 1.8.6 or lower. Note that lower versions are not officially supported.
@return [Boolean]
# File lib/haml/util.rb, line 392 392: def ruby1_8_6? 393: ruby1_8? && Haml::Util::RUBY_VERSION[2] < 7 394: 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
# File lib/haml/util.rb, line 511 511: def set_eql?(set1, set2) 512: return set1.eql?(set2) unless ruby1_8_6? 513: set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash}) 514: 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`
# File lib/haml/util.rb, line 500 500: def set_hash(set) 501: return set.hash unless ruby1_8_6? 502: set.map {|e| e.hash}.uniq.sort.hash 503: end
Silences all Haml warnings within a block.
@yield A block in which no Haml warnings will be printed
# 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
# 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
# File lib/haml/util.rb, line 586 586: def static_method_name(name, *vars) 587: "#{name}_#{vars.map {|v| !!v}.join('_')}" 588: 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`
# 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
# 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
# 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