PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/vendor/ruby/1.9.1/gems/sass-3.2.5/lib/sass/util.rb

https://github.com/KaylaStuebbe/uwsp-virtual-tour-server
Ruby | 853 lines | 409 code | 69 blank | 375 comment | 62 complexity | 7a1681de2e858ab7c2febfb32e4aa47a MD5 | raw file
Possible License(s): Apache-2.0, MIT, GPL-2.0, BSD-3-Clause
  1. require 'erb'
  2. require 'set'
  3. require 'enumerator'
  4. require 'stringio'
  5. require 'rbconfig'
  6. require 'sass/root'
  7. require 'sass/util/subset_map'
  8. module Sass
  9. # A module containing various useful functions.
  10. module Util
  11. extend self
  12. # An array of ints representing the Ruby version number.
  13. # @api public
  14. RUBY_VERSION = ::RUBY_VERSION.split(".").map {|s| s.to_i}
  15. # The Ruby engine we're running under. Defaults to `"ruby"`
  16. # if the top-level constant is undefined.
  17. # @api public
  18. RUBY_ENGINE = defined?(::RUBY_ENGINE) ? ::RUBY_ENGINE : "ruby"
  19. # Returns the path of a file relative to the Sass root directory.
  20. #
  21. # @param file [String] The filename relative to the Sass root
  22. # @return [String] The filename relative to the the working directory
  23. def scope(file)
  24. File.join(Sass::ROOT_DIR, file)
  25. end
  26. # Converts an array of `[key, value]` pairs to a hash.
  27. #
  28. # @example
  29. # to_hash([[:foo, "bar"], [:baz, "bang"]])
  30. # #=> {:foo => "bar", :baz => "bang"}
  31. # @param arr [Array<(Object, Object)>] An array of pairs
  32. # @return [Hash] A hash
  33. def to_hash(arr)
  34. Hash[arr.compact]
  35. end
  36. # Maps the keys in a hash according to a block.
  37. #
  38. # @example
  39. # map_keys({:foo => "bar", :baz => "bang"}) {|k| k.to_s}
  40. # #=> {"foo" => "bar", "baz" => "bang"}
  41. # @param hash [Hash] The hash to map
  42. # @yield [key] A block in which the keys are transformed
  43. # @yieldparam key [Object] The key that should be mapped
  44. # @yieldreturn [Object] The new value for the key
  45. # @return [Hash] The mapped hash
  46. # @see #map_vals
  47. # @see #map_hash
  48. def map_keys(hash)
  49. to_hash(hash.map {|k, v| [yield(k), v]})
  50. end
  51. # Maps the values in a hash according to a block.
  52. #
  53. # @example
  54. # map_values({:foo => "bar", :baz => "bang"}) {|v| v.to_sym}
  55. # #=> {:foo => :bar, :baz => :bang}
  56. # @param hash [Hash] The hash to map
  57. # @yield [value] A block in which the values are transformed
  58. # @yieldparam value [Object] The value that should be mapped
  59. # @yieldreturn [Object] The new value for the value
  60. # @return [Hash] The mapped hash
  61. # @see #map_keys
  62. # @see #map_hash
  63. def map_vals(hash)
  64. to_hash(hash.map {|k, v| [k, yield(v)]})
  65. end
  66. # Maps the key-value pairs of a hash according to a block.
  67. #
  68. # @example
  69. # map_hash({:foo => "bar", :baz => "bang"}) {|k, v| [k.to_s, v.to_sym]}
  70. # #=> {"foo" => :bar, "baz" => :bang}
  71. # @param hash [Hash] The hash to map
  72. # @yield [key, value] A block in which the key-value pairs are transformed
  73. # @yieldparam [key] The hash key
  74. # @yieldparam [value] The hash value
  75. # @yieldreturn [(Object, Object)] The new value for the `[key, value]` pair
  76. # @return [Hash] The mapped hash
  77. # @see #map_keys
  78. # @see #map_vals
  79. def map_hash(hash)
  80. # Using &block here completely hoses performance on 1.8.
  81. to_hash(hash.map {|k, v| yield k, v})
  82. end
  83. # Computes the powerset of the given array.
  84. # This is the set of all subsets of the array.
  85. #
  86. # @example
  87. # powerset([1, 2, 3]) #=>
  88. # Set[Set[], Set[1], Set[2], Set[3], Set[1, 2], Set[2, 3], Set[1, 3], Set[1, 2, 3]]
  89. # @param arr [Enumerable]
  90. # @return [Set<Set>] The subsets of `arr`
  91. def powerset(arr)
  92. arr.inject([Set.new].to_set) do |powerset, el|
  93. new_powerset = Set.new
  94. powerset.each do |subset|
  95. new_powerset << subset
  96. new_powerset << subset + [el]
  97. end
  98. new_powerset
  99. end
  100. end
  101. # Restricts a number to falling within a given range.
  102. # Returns the number if it falls within the range,
  103. # or the closest value in the range if it doesn't.
  104. #
  105. # @param value [Numeric]
  106. # @param range [Range<Numeric>]
  107. # @return [Numeric]
  108. def restrict(value, range)
  109. [[value, range.first].max, range.last].min
  110. end
  111. # Concatenates all strings that are adjacent in an array,
  112. # while leaving other elements as they are.
  113. #
  114. # @example
  115. # merge_adjacent_strings([1, "foo", "bar", 2, "baz"])
  116. # #=> [1, "foobar", 2, "baz"]
  117. # @param arr [Array]
  118. # @return [Array] The enumerable with strings merged
  119. def merge_adjacent_strings(arr)
  120. # Optimize for the common case of one element
  121. return arr if arr.size < 2
  122. arr.inject([]) do |a, e|
  123. if e.is_a?(String)
  124. if a.last.is_a?(String)
  125. a.last << e
  126. else
  127. a << e.dup
  128. end
  129. else
  130. a << e
  131. end
  132. a
  133. end
  134. end
  135. # Intersperses a value in an enumerable, as would be done with `Array#join`
  136. # but without concatenating the array together afterwards.
  137. #
  138. # @param enum [Enumerable]
  139. # @param val
  140. # @return [Array]
  141. def intersperse(enum, val)
  142. enum.inject([]) {|a, e| a << e << val}[0...-1]
  143. end
  144. # Substitutes a sub-array of one array with another sub-array.
  145. #
  146. # @param ary [Array] The array in which to make the substitution
  147. # @param from [Array] The sequence of elements to replace with `to`
  148. # @param to [Array] The sequence of elements to replace `from` with
  149. def substitute(ary, from, to)
  150. res = ary.dup
  151. i = 0
  152. while i < res.size
  153. if res[i...i+from.size] == from
  154. res[i...i+from.size] = to
  155. end
  156. i += 1
  157. end
  158. res
  159. end
  160. # Destructively strips whitespace from the beginning and end
  161. # of the first and last elements, respectively,
  162. # in the array (if those elements are strings).
  163. #
  164. # @param arr [Array]
  165. # @return [Array] `arr`
  166. def strip_string_array(arr)
  167. arr.first.lstrip! if arr.first.is_a?(String)
  168. arr.last.rstrip! if arr.last.is_a?(String)
  169. arr
  170. end
  171. # Return an array of all possible paths through the given arrays.
  172. #
  173. # @param arrs [Array<Array>]
  174. # @return [Array<Arrays>]
  175. #
  176. # @example
  177. # paths([[1, 2], [3, 4], [5]]) #=>
  178. # # [[1, 3, 5],
  179. # # [2, 3, 5],
  180. # # [1, 4, 5],
  181. # # [2, 4, 5]]
  182. def paths(arrs)
  183. arrs.inject([[]]) do |paths, arr|
  184. flatten(arr.map {|e| paths.map {|path| path + [e]}}, 1)
  185. end
  186. end
  187. # Computes a single longest common subsequence for `x` and `y`.
  188. # If there are more than one longest common subsequences,
  189. # the one returned is that which starts first in `x`.
  190. #
  191. # @param x [Array]
  192. # @param y [Array]
  193. # @yield [a, b] An optional block to use in place of a check for equality
  194. # between elements of `x` and `y`.
  195. # @yieldreturn [Object, nil] If the two values register as equal,
  196. # this will return the value to use in the LCS array.
  197. # @return [Array] The LCS
  198. def lcs(x, y, &block)
  199. x = [nil, *x]
  200. y = [nil, *y]
  201. block ||= proc {|a, b| a == b && a}
  202. lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
  203. end
  204. # Converts a Hash to an Array. This is usually identical to `Hash#to_a`,
  205. # with the following exceptions:
  206. #
  207. # * In Ruby 1.8, `Hash#to_a` is not deterministically ordered, but this is.
  208. # * In Ruby 1.9 when running tests, this is ordered in the same way it would
  209. # be under Ruby 1.8 (sorted key order rather than insertion order).
  210. #
  211. # @param hash [Hash]
  212. # @return [Array]
  213. def hash_to_a(hash)
  214. return hash.to_a unless ruby1_8? || defined?(Test::Unit)
  215. return hash.sort_by {|k, v| k}
  216. end
  217. # Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed
  218. # order. Unlike [#hash_to_a], the resulting order isn't sorted key order;
  219. # instead, it's the same order as `#group_by` has under Ruby 1.9 (key
  220. # appearance order).
  221. #
  222. # @param enum [Enumerable]
  223. # @return [Array<[Object, Array]>] An array of pairs.
  224. def group_by_to_a(enum, &block)
  225. return enum.group_by(&block).to_a unless ruby1_8?
  226. order = {}
  227. arr = []
  228. enum.group_by do |e|
  229. res = block[e]
  230. unless order.include?(res)
  231. order[res] = order.size
  232. end
  233. res
  234. end.each do |key, vals|
  235. arr[order[key]] = [key, vals]
  236. end
  237. arr
  238. end
  239. # Asserts that `value` falls within `range` (inclusive), leaving
  240. # room for slight floating-point errors.
  241. #
  242. # @param name [String] The name of the value. Used in the error message.
  243. # @param range [Range] The allowed range of values.
  244. # @param value [Numeric, Sass::Script::Number] The value to check.
  245. # @param unit [String] The unit of the value. Used in error reporting.
  246. # @return [Numeric] `value` adjusted to fall within range, if it
  247. # was outside by a floating-point margin.
  248. def check_range(name, range, value, unit='')
  249. grace = (-0.00001..0.00001)
  250. str = value.to_s
  251. value = value.value if value.is_a?(Sass::Script::Number)
  252. return value if range.include?(value)
  253. return range.first if grace.include?(value - range.first)
  254. return range.last if grace.include?(value - range.last)
  255. raise ArgumentError.new(
  256. "#{name} #{str} must be between #{range.first}#{unit} and #{range.last}#{unit}")
  257. end
  258. # Returns whether or not `seq1` is a subsequence of `seq2`. That is, whether
  259. # or not `seq2` contains every element in `seq1` in the same order (and
  260. # possibly more elements besides).
  261. #
  262. # @param seq1 [Array]
  263. # @param seq2 [Array]
  264. # @return [Boolean]
  265. def subsequence?(seq1, seq2)
  266. i = j = 0
  267. loop do
  268. return true if i == seq1.size
  269. return false if j == seq2.size
  270. i += 1 if seq1[i] == seq2[j]
  271. j += 1
  272. end
  273. end
  274. # Returns information about the caller of the previous method.
  275. #
  276. # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
  277. # @return [[String, Fixnum, (String, nil)]] An array containing the filename, line, and method name of the caller.
  278. # The method name may be nil
  279. def caller_info(entry = nil)
  280. # JRuby evaluates `caller` incorrectly when it's in an actual default argument.
  281. entry ||= caller[1]
  282. info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
  283. info[1] = info[1].to_i
  284. # This is added by Rubinius to designate a block, but we don't care about it.
  285. info[2].sub!(/ \{\}\Z/, '') if info[2]
  286. info
  287. end
  288. # Returns whether one version string represents a more recent version than another.
  289. #
  290. # @param v1 [String] A version string.
  291. # @param v2 [String] Another version string.
  292. # @return [Boolean]
  293. def version_gt(v1, v2)
  294. # Construct an array to make sure the shorter version is padded with nil
  295. Array.new([v1.length, v2.length].max).zip(v1.split("."), v2.split(".")) do |_, p1, p2|
  296. p1 ||= "0"
  297. p2 ||= "0"
  298. release1 = p1 =~ /^[0-9]+$/
  299. release2 = p2 =~ /^[0-9]+$/
  300. if release1 && release2
  301. # Integer comparison if both are full releases
  302. p1, p2 = p1.to_i, p2.to_i
  303. next if p1 == p2
  304. return p1 > p2
  305. elsif !release1 && !release2
  306. # String comparison if both are prereleases
  307. next if p1 == p2
  308. return p1 > p2
  309. else
  310. # If only one is a release, that one is newer
  311. return release1
  312. end
  313. end
  314. end
  315. # Returns whether one version string represents the same or a more
  316. # recent version than another.
  317. #
  318. # @param v1 [String] A version string.
  319. # @param v2 [String] Another version string.
  320. # @return [Boolean]
  321. def version_geq(v1, v2)
  322. version_gt(v1, v2) || !version_gt(v2, v1)
  323. end
  324. # Throws a NotImplementedError for an abstract method.
  325. #
  326. # @param obj [Object] `self`
  327. # @raise [NotImplementedError]
  328. def abstract(obj)
  329. raise NotImplementedError.new("#{obj.class} must implement ##{caller_info[2]}")
  330. end
  331. # Silence all output to STDERR within a block.
  332. #
  333. # @yield A block in which no output will be printed to STDERR
  334. def silence_warnings
  335. the_real_stderr, $stderr = $stderr, StringIO.new
  336. yield
  337. ensure
  338. $stderr = the_real_stderr
  339. end
  340. @@silence_warnings = false
  341. # Silences all Sass warnings within a block.
  342. #
  343. # @yield A block in which no Sass warnings will be printed
  344. def silence_sass_warnings
  345. old_level, Sass.logger.log_level = Sass.logger.log_level, :error
  346. yield
  347. ensure
  348. Sass.logger.log_level = old_level
  349. end
  350. # The same as `Kernel#warn`, but is silenced by \{#silence\_sass\_warnings}.
  351. #
  352. # @param msg [String]
  353. def sass_warn(msg)
  354. msg = msg + "\n" unless ruby1?
  355. Sass.logger.warn(msg)
  356. end
  357. ## Cross Rails Version Compatibility
  358. # Returns the root of the Rails application,
  359. # if this is running in a Rails context.
  360. # Returns `nil` if no such root is defined.
  361. #
  362. # @return [String, nil]
  363. def rails_root
  364. if defined?(::Rails.root)
  365. return ::Rails.root.to_s if ::Rails.root
  366. raise "ERROR: Rails.root is nil!"
  367. end
  368. return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
  369. return nil
  370. end
  371. # Returns the environment of the Rails application,
  372. # if this is running in a Rails context.
  373. # Returns `nil` if no such environment is defined.
  374. #
  375. # @return [String, nil]
  376. def rails_env
  377. return ::Rails.env.to_s if defined?(::Rails.env)
  378. return RAILS_ENV.to_s if defined?(RAILS_ENV)
  379. return nil
  380. end
  381. # Returns whether this environment is using ActionPack
  382. # version 3.0.0 or greater.
  383. #
  384. # @return [Boolean]
  385. def ap_geq_3?
  386. ap_geq?("3.0.0.beta1")
  387. end
  388. # Returns whether this environment is using ActionPack
  389. # of a version greater than or equal to that specified.
  390. #
  391. # @param version [String] The string version number to check against.
  392. # Should be greater than or equal to Rails 3,
  393. # because otherwise ActionPack::VERSION isn't autoloaded
  394. # @return [Boolean]
  395. def ap_geq?(version)
  396. # The ActionPack module is always loaded automatically in Rails >= 3
  397. return false unless defined?(ActionPack) && defined?(ActionPack::VERSION) &&
  398. defined?(ActionPack::VERSION::STRING)
  399. version_geq(ActionPack::VERSION::STRING, version)
  400. end
  401. # Returns an ActionView::Template* class.
  402. # In pre-3.0 versions of Rails, most of these classes
  403. # were of the form `ActionView::TemplateFoo`,
  404. # while afterwards they were of the form `ActionView;:Template::Foo`.
  405. #
  406. # @param name [#to_s] The name of the class to get.
  407. # For example, `:Error` will return `ActionView::TemplateError`
  408. # or `ActionView::Template::Error`.
  409. def av_template_class(name)
  410. return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
  411. return ActionView::Template.const_get(name.to_s)
  412. end
  413. ## Cross-OS Compatibility
  414. # Whether or not this is running on Windows.
  415. #
  416. # @return [Boolean]
  417. def windows?
  418. RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i
  419. end
  420. # Whether or not this is running on IronRuby.
  421. #
  422. # @return [Boolean]
  423. def ironruby?
  424. RUBY_ENGINE == "ironruby"
  425. end
  426. # Like `Dir.glob`, but works with backslash-separated paths on Windows.
  427. #
  428. # @param path [String]
  429. def glob(path, &block)
  430. path = path.gsub('\\', '/') if windows?
  431. Dir.glob(path, &block)
  432. end
  433. # Prepare a value for a destructuring assignment (e.g. `a, b =
  434. # val`). This works around a performance bug when using
  435. # ActiveSupport, and only needs to be called when `val` is likely
  436. # to be `nil` reasonably often.
  437. #
  438. # See [this bug report](http://redmine.ruby-lang.org/issues/4917).
  439. #
  440. # @param val [Object]
  441. # @return [Object]
  442. def destructure(val)
  443. val || []
  444. end
  445. ## Cross-Ruby-Version Compatibility
  446. # Whether or not this is running under a Ruby version under 2.0.
  447. #
  448. # @return [Boolean]
  449. def ruby1?
  450. Sass::Util::RUBY_VERSION[0] <= 1
  451. end
  452. # Whether or not this is running under Ruby 1.8 or lower.
  453. #
  454. # Note that IronRuby counts as Ruby 1.8,
  455. # because it doesn't support the Ruby 1.9 encoding API.
  456. #
  457. # @return [Boolean]
  458. def ruby1_8?
  459. # IronRuby says its version is 1.9, but doesn't support any of the encoding APIs.
  460. # We have to fall back to 1.8 behavior.
  461. ironruby? || (Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9)
  462. end
  463. # Whether or not this is running under Ruby 1.8.6 or lower.
  464. # Note that lower versions are not officially supported.
  465. #
  466. # @return [Boolean]
  467. def ruby1_8_6?
  468. ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
  469. end
  470. # Whether or not this is running under MacRuby.
  471. #
  472. # @return [Boolean]
  473. def macruby?
  474. RUBY_ENGINE == 'macruby'
  475. end
  476. # Checks that the encoding of a string is valid in Ruby 1.9
  477. # and cleans up potential encoding gotchas like the UTF-8 BOM.
  478. # If it's not, yields an error string describing the invalid character
  479. # and the line on which it occurrs.
  480. #
  481. # @param str [String] The string of which to check the encoding
  482. # @yield [msg] A block in which an encoding error can be raised.
  483. # Only yields if there is an encoding error
  484. # @yieldparam msg [String] The error message to be raised
  485. # @return [String] `str`, potentially with encoding gotchas like BOMs removed
  486. def check_encoding(str)
  487. if ruby1_8?
  488. return str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM
  489. elsif str.valid_encoding?
  490. # Get rid of the Unicode BOM if possible
  491. if str.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?$/
  492. return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '')
  493. else
  494. return str
  495. end
  496. end
  497. encoding = str.encoding
  498. newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding("binary"))
  499. str.force_encoding("binary").split(newlines).each_with_index do |line, i|
  500. begin
  501. line.encode(encoding)
  502. rescue Encoding::UndefinedConversionError => e
  503. yield <<MSG.rstrip, i + 1
  504. Invalid #{encoding.name} character #{e.error_char.dump}
  505. MSG
  506. end
  507. end
  508. return str
  509. end
  510. # Like {\#check\_encoding}, but also checks for a `@charset` declaration
  511. # at the beginning of the file and uses that encoding if it exists.
  512. #
  513. # The Sass encoding rules are simple.
  514. # If a `@charset` declaration exists,
  515. # we assume that that's the original encoding of the document.
  516. # Otherwise, we use whatever encoding Ruby has.
  517. # Then we convert that to UTF-8 to process internally.
  518. # The UTF-8 end result is what's returned by this method.
  519. #
  520. # @param str [String] The string of which to check the encoding
  521. # @yield [msg] A block in which an encoding error can be raised.
  522. # Only yields if there is an encoding error
  523. # @yieldparam msg [String] The error message to be raised
  524. # @return [(String, Encoding)] The original string encoded as UTF-8,
  525. # and the source encoding of the string (or `nil` under Ruby 1.8)
  526. # @raise [Encoding::UndefinedConversionError] if the source encoding
  527. # cannot be converted to UTF-8
  528. # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
  529. def check_sass_encoding(str, &block)
  530. return check_encoding(str, &block), nil if ruby1_8?
  531. # We allow any printable ASCII characters but double quotes in the charset decl
  532. bin = str.dup.force_encoding("BINARY")
  533. encoding = Sass::Util::ENCODINGS_TO_CHECK.find do |enc|
  534. re = Sass::Util::CHARSET_REGEXPS[enc]
  535. re && bin =~ re
  536. end
  537. charset, bom = $1, $2
  538. if charset
  539. charset = charset.force_encoding(encoding).encode("UTF-8")
  540. if endianness = encoding[/[BL]E$/]
  541. begin
  542. Encoding.find(charset + endianness)
  543. charset << endianness
  544. rescue ArgumentError # Encoding charset + endianness doesn't exist
  545. end
  546. end
  547. str.force_encoding(charset)
  548. elsif bom
  549. str.force_encoding(encoding)
  550. end
  551. str = check_encoding(str, &block)
  552. return str.encode("UTF-8"), str.encoding
  553. end
  554. unless ruby1_8?
  555. # @private
  556. def _enc(string, encoding)
  557. string.encode(encoding).force_encoding("BINARY")
  558. end
  559. # We could automatically add in any non-ASCII-compatible encodings here,
  560. # but there's not really a good way to do that
  561. # without manually checking that each encoding
  562. # encodes all ASCII characters properly,
  563. # which takes long enough to affect the startup time of the CLI.
  564. ENCODINGS_TO_CHECK = %w[UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE]
  565. CHARSET_REGEXPS = Hash.new do |h, e|
  566. h[e] =
  567. begin
  568. # /\A(?:\uFEFF)?@charset "(.*?)"|\A(\uFEFF)/
  569. Regexp.new(/\A(?:#{_enc("\uFEFF", e)})?#{
  570. _enc('@charset "', e)}(.*?)#{_enc('"', e)}|\A(#{
  571. _enc("\uFEFF", e)})/)
  572. rescue Encoding::ConverterNotFoundError => _
  573. nil # JRuby on Java 5 doesn't support UTF-32
  574. rescue
  575. # /\A@charset "(.*?)"/
  576. Regexp.new(/\A#{_enc('@charset "', e)}(.*?)#{_enc('"', e)}/)
  577. end
  578. end
  579. end
  580. # Checks to see if a class has a given method.
  581. # For example:
  582. #
  583. # Sass::Util.has?(:public_instance_method, String, :gsub) #=> true
  584. #
  585. # Method collections like `Class#instance_methods`
  586. # return strings in Ruby 1.8 and symbols in Ruby 1.9 and on,
  587. # so this handles checking for them in a compatible way.
  588. #
  589. # @param attr [#to_s] The (singular) name of the method-collection method
  590. # (e.g. `:instance_methods`, `:private_methods`)
  591. # @param klass [Module] The class to check the methods of which to check
  592. # @param method [String, Symbol] The name of the method do check for
  593. # @return [Boolean] Whether or not the given collection has the given method
  594. def has?(attr, klass, method)
  595. klass.send("#{attr}s").include?(ruby1_8? ? method.to_s : method.to_sym)
  596. end
  597. # A version of `Enumerable#enum_with_index` that works in Ruby 1.8 and 1.9.
  598. #
  599. # @param enum [Enumerable] The enumerable to get the enumerator for
  600. # @return [Enumerator] The with-index enumerator
  601. def enum_with_index(enum)
  602. ruby1_8? ? enum.enum_with_index : enum.each_with_index
  603. end
  604. # A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.
  605. #
  606. # @param enum [Enumerable] The enumerable to get the enumerator for
  607. # @param n [Fixnum] The size of each cons
  608. # @return [Enumerator] The consed enumerator
  609. def enum_cons(enum, n)
  610. ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
  611. end
  612. # A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.
  613. #
  614. # @param enum [Enumerable] The enumerable to get the enumerator for
  615. # @param n [Fixnum] The size of each slice
  616. # @return [Enumerator] The consed enumerator
  617. def enum_slice(enum, n)
  618. ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
  619. end
  620. # Destructively removes all elements from an array that match a block, and
  621. # returns the removed elements.
  622. #
  623. # @param array [Array] The array from which to remove elements.
  624. # @yield [el] Called for each element.
  625. # @yieldparam el [*] The element to test.
  626. # @yieldreturn [Boolean] Whether or not to extract the element.
  627. # @return [Array] The extracted elements.
  628. def extract!(array)
  629. out = []
  630. array.reject! do |e|
  631. next false unless yield e
  632. out << e
  633. true
  634. end
  635. out
  636. end
  637. # Returns the ASCII code of the given character.
  638. #
  639. # @param c [String] All characters but the first are ignored.
  640. # @return [Fixnum] The ASCII code of `c`.
  641. def ord(c)
  642. ruby1_8? ? c[0] : c.ord
  643. end
  644. # Flattens the first `n` nested arrays in a cross-version manner.
  645. #
  646. # @param arr [Array] The array to flatten
  647. # @param n [Fixnum] The number of levels to flatten
  648. # @return [Array] The flattened array
  649. def flatten(arr, n)
  650. return arr.flatten(n) unless ruby1_8_6?
  651. return arr if n == 0
  652. arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
  653. end
  654. # Returns the hash code for a set in a cross-version manner.
  655. # Aggravatingly, this is order-dependent in Ruby 1.8.6.
  656. #
  657. # @param set [Set]
  658. # @return [Fixnum] The order-independent hashcode of `set`
  659. def set_hash(set)
  660. return set.hash unless ruby1_8_6?
  661. set.map {|e| e.hash}.uniq.sort.hash
  662. end
  663. # Tests the hash-equality of two sets in a cross-version manner.
  664. # Aggravatingly, this is order-dependent in Ruby 1.8.6.
  665. #
  666. # @param set1 [Set]
  667. # @param set2 [Set]
  668. # @return [Boolean] Whether or not the sets are hashcode equal
  669. def set_eql?(set1, set2)
  670. return set1.eql?(set2) unless ruby1_8_6?
  671. set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
  672. end
  673. # Like `Object#inspect`, but preserves non-ASCII characters rather than escaping them under Ruby 1.9.2.
  674. # This is necessary so that the precompiled Haml template can be `#encode`d into `@options[:encoding]`
  675. # before being evaluated.
  676. #
  677. # @param obj {Object}
  678. # @return {String}
  679. def inspect_obj(obj)
  680. return obj.inspect unless version_geq(::RUBY_VERSION, "1.9.2")
  681. return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
  682. return obj.inspect unless obj.is_a?(String)
  683. '"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
  684. end
  685. # Extracts the non-string vlaues from an array containing both strings and non-strings.
  686. # These values are replaced with escape sequences.
  687. # This can be undone using \{#inject\_values}.
  688. #
  689. # This is useful e.g. when we want to do string manipulation
  690. # on an interpolated string.
  691. #
  692. # The precise format of the resulting string is not guaranteed.
  693. # However, it is guaranteed that newlines and whitespace won't be affected.
  694. #
  695. # @param arr [Array] The array from which values are extracted.
  696. # @return [(String, Array)] The resulting string, and an array of extracted values.
  697. def extract_values(arr)
  698. values = []
  699. return arr.map do |e|
  700. next e.gsub('{', '{{') if e.is_a?(String)
  701. values << e
  702. next "{#{values.count - 1}}"
  703. end.join, values
  704. end
  705. # Undoes \{#extract\_values} by transforming a string with escape sequences
  706. # into an array of strings and non-string values.
  707. #
  708. # @param str [String] The string with escape sequences.
  709. # @param values [Array] The array of values to inject.
  710. # @return [Array] The array of strings and values.
  711. def inject_values(str, values)
  712. return [str.gsub('{{', '{')] if values.empty?
  713. # Add an extra { so that we process the tail end of the string
  714. result = (str + '{{').scan(/(.*?)(?:(\{\{)|\{(\d+)\})/m).map do |(pre, esc, n)|
  715. [pre, esc ? '{' : '', n ? values[n.to_i] : '']
  716. end.flatten(1)
  717. result[-2] = '' # Get rid of the extra {
  718. merge_adjacent_strings(result).reject {|s| s == ''}
  719. end
  720. # Allows modifications to be performed on the string form
  721. # of an array containing both strings and non-strings.
  722. #
  723. # @param arr [Array] The array from which values are extracted.
  724. # @yield [str] A block in which string manipulation can be done to the array.
  725. # @yieldparam str [String] The string form of `arr`.
  726. # @yieldreturn [String] The modified string.
  727. # @return [Array] The modified, interpolated array.
  728. def with_extracted_values(arr)
  729. str, vals = extract_values(arr)
  730. str = yield str
  731. inject_values(str, vals)
  732. end
  733. ## Static Method Stuff
  734. # The context in which the ERB for \{#def\_static\_method} will be run.
  735. class StaticConditionalContext
  736. # @param set [#include?] The set of variables that are defined for this context.
  737. def initialize(set)
  738. @set = set
  739. end
  740. # Checks whether or not a variable is defined for this context.
  741. #
  742. # @param name [Symbol] The name of the variable
  743. # @return [Boolean]
  744. def method_missing(name, *args, &block)
  745. super unless args.empty? && block.nil?
  746. @set.include?(name)
  747. end
  748. end
  749. private
  750. # Calculates the memoization table for the Least Common Subsequence algorithm.
  751. # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
  752. def lcs_table(x, y)
  753. c = Array.new(x.size) {[]}
  754. x.size.times {|i| c[i][0] = 0}
  755. y.size.times {|j| c[0][j] = 0}
  756. (1...x.size).each do |i|
  757. (1...y.size).each do |j|
  758. c[i][j] =
  759. if yield x[i], y[j]
  760. c[i-1][j-1] + 1
  761. else
  762. [c[i][j-1], c[i-1][j]].max
  763. end
  764. end
  765. end
  766. return c
  767. end
  768. # Computes a single longest common subsequence for arrays x and y.
  769. # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
  770. def lcs_backtrace(c, x, y, i, j, &block)
  771. return [] if i == 0 || j == 0
  772. if v = yield(x[i], y[j])
  773. return lcs_backtrace(c, x, y, i-1, j-1, &block) << v
  774. end
  775. return lcs_backtrace(c, x, y, i, j-1, &block) if c[i][j-1] > c[i-1][j]
  776. return lcs_backtrace(c, x, y, i-1, j, &block)
  777. end
  778. end
  779. end
  780. require 'sass/util/multibyte_string_scanner'