util.rb 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  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. # Asserts that `value` falls within `range` (inclusive), leaving
  218. # room for slight floating-point errors.
  219. #
  220. # @param name [String] The name of the value. Used in the error message.
  221. # @param range [Range] The allowed range of values.
  222. # @param value [Numeric, Sass::Script::Number] The value to check.
  223. # @param unit [String] The unit of the value. Used in error reporting.
  224. # @return [Numeric] `value` adjusted to fall within range, if it
  225. # was outside by a floating-point margin.
  226. def check_range(name, range, value, unit='')
  227. grace = (-0.00001..0.00001)
  228. str = value.to_s
  229. value = value.value if value.is_a?(Sass::Script::Number)
  230. return value if range.include?(value)
  231. return range.first if grace.include?(value - range.first)
  232. return range.last if grace.include?(value - range.last)
  233. raise ArgumentError.new(
  234. "#{name} #{str} must be between #{range.first}#{unit} and #{range.last}#{unit}")
  235. end
  236. # Returns whether or not `seq1` is a subsequence of `seq2`. That is, whether
  237. # or not `seq2` contains every element in `seq1` in the same order (and
  238. # possibly more elements besides).
  239. #
  240. # @param seq1 [Array]
  241. # @param seq2 [Array]
  242. # @return [Boolean]
  243. def subsequence?(seq1, seq2)
  244. i = j = 0
  245. loop do
  246. return true if i == seq1.size
  247. return false if j == seq2.size
  248. i += 1 if seq1[i] == seq2[j]
  249. j += 1
  250. end
  251. end
  252. # Returns information about the caller of the previous method.
  253. #
  254. # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
  255. # @return [[String, Fixnum, (String, nil)]] An array containing the filename, line, and method name of the caller.
  256. # The method name may be nil
  257. def caller_info(entry = caller[1])
  258. info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
  259. info[1] = info[1].to_i
  260. # This is added by Rubinius to designate a block, but we don't care about it.
  261. info[2].sub!(/ \{\}\Z/, '') if info[2]
  262. info
  263. end
  264. # Returns whether one version string represents a more recent version than another.
  265. #
  266. # @param v1 [String] A version string.
  267. # @param v2 [String] Another version string.
  268. # @return [Boolean]
  269. def version_gt(v1, v2)
  270. # Construct an array to make sure the shorter version is padded with nil
  271. Array.new([v1.length, v2.length].max).zip(v1.split("."), v2.split(".")) do |_, p1, p2|
  272. p1 ||= "0"
  273. p2 ||= "0"
  274. release1 = p1 =~ /^[0-9]+$/
  275. release2 = p2 =~ /^[0-9]+$/
  276. if release1 && release2
  277. # Integer comparison if both are full releases
  278. p1, p2 = p1.to_i, p2.to_i
  279. next if p1 == p2
  280. return p1 > p2
  281. elsif !release1 && !release2
  282. # String comparison if both are prereleases
  283. next if p1 == p2
  284. return p1 > p2
  285. else
  286. # If only one is a release, that one is newer
  287. return release1
  288. end
  289. end
  290. end
  291. # Returns whether one version string represents the same or a more
  292. # recent version than another.
  293. #
  294. # @param v1 [String] A version string.
  295. # @param v2 [String] Another version string.
  296. # @return [Boolean]
  297. def version_geq(v1, v2)
  298. version_gt(v1, v2) || !version_gt(v2, v1)
  299. end
  300. # Throws a NotImplementedError for an abstract method.
  301. #
  302. # @param obj [Object] `self`
  303. # @raise [NotImplementedError]
  304. def abstract(obj)
  305. raise NotImplementedError.new("#{obj.class} must implement ##{caller_info[2]}")
  306. end
  307. # Silence all output to STDERR within a block.
  308. #
  309. # @yield A block in which no output will be printed to STDERR
  310. def silence_warnings
  311. the_real_stderr, $stderr = $stderr, StringIO.new
  312. yield
  313. ensure
  314. $stderr = the_real_stderr
  315. end
  316. @@silence_warnings = false
  317. # Silences all Sass warnings within a block.
  318. #
  319. # @yield A block in which no Sass warnings will be printed
  320. def silence_sass_warnings
  321. old_level, Sass.logger.log_level = Sass.logger.log_level, :error
  322. yield
  323. ensure
  324. Sass.logger.log_level = old_level
  325. end
  326. # The same as `Kernel#warn`, but is silenced by \{#silence\_sass\_warnings}.
  327. #
  328. # @param msg [String]
  329. def sass_warn(msg)
  330. Sass.logger.warn(msg)
  331. end
  332. ## Cross Rails Version Compatibility
  333. # Returns the root of the Rails application,
  334. # if this is running in a Rails context.
  335. # Returns `nil` if no such root is defined.
  336. #
  337. # @return [String, nil]
  338. def rails_root
  339. if defined?(::Rails.root)
  340. return ::Rails.root.to_s if ::Rails.root
  341. raise "ERROR: Rails.root is nil!"
  342. end
  343. return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
  344. return nil
  345. end
  346. # Returns the environment of the Rails application,
  347. # if this is running in a Rails context.
  348. # Returns `nil` if no such environment is defined.
  349. #
  350. # @return [String, nil]
  351. def rails_env
  352. return ::Rails.env.to_s if defined?(::Rails.env)
  353. return RAILS_ENV.to_s if defined?(RAILS_ENV)
  354. return nil
  355. end
  356. # Returns whether this environment is using ActionPack
  357. # version 3.0.0 or greater.
  358. #
  359. # @return [Boolean]
  360. def ap_geq_3?
  361. ap_geq?("3.0.0.beta1")
  362. end
  363. # Returns whether this environment is using ActionPack
  364. # of a version greater than or equal to that specified.
  365. #
  366. # @param version [String] The string version number to check against.
  367. # Should be greater than or equal to Rails 3,
  368. # because otherwise ActionPack::VERSION isn't autoloaded
  369. # @return [Boolean]
  370. def ap_geq?(version)
  371. # The ActionPack module is always loaded automatically in Rails >= 3
  372. return false unless defined?(ActionPack) && defined?(ActionPack::VERSION) &&
  373. defined?(ActionPack::VERSION::STRING)
  374. version_geq(ActionPack::VERSION::STRING, version)
  375. end
  376. # Returns an ActionView::Template* class.
  377. # In pre-3.0 versions of Rails, most of these classes
  378. # were of the form `ActionView::TemplateFoo`,
  379. # while afterwards they were of the form `ActionView;:Template::Foo`.
  380. #
  381. # @param name [#to_s] The name of the class to get.
  382. # For example, `:Error` will return `ActionView::TemplateError`
  383. # or `ActionView::Template::Error`.
  384. def av_template_class(name)
  385. return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
  386. return ActionView::Template.const_get(name.to_s)
  387. end
  388. ## Cross-OS Compatibility
  389. # Whether or not this is running on Windows.
  390. #
  391. # @return [Boolean]
  392. def windows?
  393. RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i
  394. end
  395. # Whether or not this is running on IronRuby.
  396. #
  397. # @return [Boolean]
  398. def ironruby?
  399. RUBY_ENGINE == "ironruby"
  400. end
  401. # Like `Dir.glob`, but works with backslash-separated paths on Windows.
  402. #
  403. # @param path [String]
  404. def glob(path, &block)
  405. path = path.gsub('\\', '/') if windows?
  406. Dir.glob(path, &block)
  407. end
  408. ## Cross-Ruby-Version Compatibility
  409. # Whether or not this is running under Ruby 1.8 or lower.
  410. #
  411. # Note that IronRuby counts as Ruby 1.8,
  412. # because it doesn't support the Ruby 1.9 encoding API.
  413. #
  414. # @return [Boolean]
  415. def ruby1_8?
  416. # IronRuby says its version is 1.9, but doesn't support any of the encoding APIs.
  417. # We have to fall back to 1.8 behavior.
  418. ironruby? || (Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9)
  419. end
  420. # Whether or not this is running under Ruby 1.8.6 or lower.
  421. # Note that lower versions are not officially supported.
  422. #
  423. # @return [Boolean]
  424. def ruby1_8_6?
  425. ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
  426. end
  427. # Whether or not this is running under MacRuby.
  428. #
  429. # @return [Boolean]
  430. def macruby?
  431. RUBY_ENGINE == 'macruby'
  432. end
  433. # Checks that the encoding of a string is valid in Ruby 1.9
  434. # and cleans up potential encoding gotchas like the UTF-8 BOM.
  435. # If it's not, yields an error string describing the invalid character
  436. # and the line on which it occurrs.
  437. #
  438. # @param str [String] The string of which to check the encoding
  439. # @yield [msg] A block in which an encoding error can be raised.
  440. # Only yields if there is an encoding error
  441. # @yieldparam msg [String] The error message to be raised
  442. # @return [String] `str`, potentially with encoding gotchas like BOMs removed
  443. def check_encoding(str)
  444. if ruby1_8?
  445. return str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM
  446. elsif str.valid_encoding?
  447. # Get rid of the Unicode BOM if possible
  448. if str.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?$/
  449. return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '')
  450. else
  451. return str
  452. end
  453. end
  454. encoding = str.encoding
  455. newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding("binary"))
  456. str.force_encoding("binary").split(newlines).each_with_index do |line, i|
  457. begin
  458. line.encode(encoding)
  459. rescue Encoding::UndefinedConversionError => e
  460. yield <<MSG.rstrip, i + 1
  461. Invalid #{encoding.name} character #{e.error_char.dump}
  462. MSG
  463. end
  464. end
  465. return str
  466. end
  467. # Like {\#check\_encoding}, but also checks for a `@charset` declaration
  468. # at the beginning of the file and uses that encoding if it exists.
  469. #
  470. # The Sass encoding rules are simple.
  471. # If a `@charset` declaration exists,
  472. # we assume that that's the original encoding of the document.
  473. # Otherwise, we use whatever encoding Ruby has.
  474. # Then we convert that to UTF-8 to process internally.
  475. # The UTF-8 end result is what's returned by this method.
  476. #
  477. # @param str [String] The string of which to check the encoding
  478. # @yield [msg] A block in which an encoding error can be raised.
  479. # Only yields if there is an encoding error
  480. # @yieldparam msg [String] The error message to be raised
  481. # @return [(String, Encoding)] The original string encoded as UTF-8,
  482. # and the source encoding of the string (or `nil` under Ruby 1.8)
  483. # @raise [Encoding::UndefinedConversionError] if the source encoding
  484. # cannot be converted to UTF-8
  485. # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
  486. def check_sass_encoding(str, &block)
  487. return check_encoding(str, &block), nil if ruby1_8?
  488. # We allow any printable ASCII characters but double quotes in the charset decl
  489. bin = str.dup.force_encoding("BINARY")
  490. encoding = Sass::Util::ENCODINGS_TO_CHECK.find do |enc|
  491. re = Sass::Util::CHARSET_REGEXPS[enc]
  492. re && bin =~ re
  493. end
  494. charset, bom = $1, $2
  495. if charset
  496. charset = charset.force_encoding(encoding).encode("UTF-8")
  497. if endianness = encoding[/[BL]E$/]
  498. begin
  499. Encoding.find(charset + endianness)
  500. charset << endianness
  501. rescue ArgumentError # Encoding charset + endianness doesn't exist
  502. end
  503. end
  504. str.force_encoding(charset)
  505. elsif bom
  506. str.force_encoding(encoding)
  507. end
  508. str = check_encoding(str, &block)
  509. return str.encode("UTF-8"), str.encoding
  510. end
  511. unless ruby1_8?
  512. # @private
  513. def _enc(string, encoding)
  514. string.encode(encoding).force_encoding("BINARY")
  515. end
  516. # We could automatically add in any non-ASCII-compatible encodings here,
  517. # but there's not really a good way to do that
  518. # without manually checking that each encoding
  519. # encodes all ASCII characters properly,
  520. # which takes long enough to affect the startup time of the CLI.
  521. ENCODINGS_TO_CHECK = %w[UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE]
  522. CHARSET_REGEXPS = Hash.new do |h, e|
  523. h[e] =
  524. begin
  525. # /\A(?:\uFEFF)?@charset "(.*?)"|\A(\uFEFF)/
  526. Regexp.new(/\A(?:#{_enc("\uFEFF", e)})?#{
  527. _enc('@charset "', e)}(.*?)#{_enc('"', e)}|\A(#{
  528. _enc("\uFEFF", e)})/)
  529. rescue Encoding::ConverterNotFoundError => _
  530. nil # JRuby on Java 5 doesn't support UTF-32
  531. rescue
  532. # /\A@charset "(.*?)"/
  533. Regexp.new(/\A#{_enc('@charset "', e)}(.*?)#{_enc('"', e)}/)
  534. end
  535. end
  536. end
  537. # Checks to see if a class has a given method.
  538. # For example:
  539. #
  540. # Sass::Util.has?(:public_instance_method, String, :gsub) #=> true
  541. #
  542. # Method collections like `Class#instance_methods`
  543. # return strings in Ruby 1.8 and symbols in Ruby 1.9 and on,
  544. # so this handles checking for them in a compatible way.
  545. #
  546. # @param attr [#to_s] The (singular) name of the method-collection method
  547. # (e.g. `:instance_methods`, `:private_methods`)
  548. # @param klass [Module] The class to check the methods of which to check
  549. # @param method [String, Symbol] The name of the method do check for
  550. # @return [Boolean] Whether or not the given collection has the given method
  551. def has?(attr, klass, method)
  552. klass.send("#{attr}s").include?(ruby1_8? ? method.to_s : method.to_sym)
  553. end
  554. # A version of `Enumerable#enum_with_index` that works in Ruby 1.8 and 1.9.
  555. #
  556. # @param enum [Enumerable] The enumerable to get the enumerator for
  557. # @return [Enumerator] The with-index enumerator
  558. def enum_with_index(enum)
  559. ruby1_8? ? enum.enum_with_index : enum.each_with_index
  560. end
  561. # A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.
  562. #
  563. # @param enum [Enumerable] The enumerable to get the enumerator for
  564. # @param n [Fixnum] The size of each cons
  565. # @return [Enumerator] The consed enumerator
  566. def enum_cons(enum, n)
  567. ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
  568. end
  569. # A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.
  570. #
  571. # @param enum [Enumerable] The enumerable to get the enumerator for
  572. # @param n [Fixnum] The size of each slice
  573. # @return [Enumerator] The consed enumerator
  574. def enum_slice(enum, n)
  575. ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
  576. end
  577. # Destructively removes all elements from an array that match a block, and
  578. # returns the removed elements.
  579. #
  580. # @param array [Array] The array from which to remove elements.
  581. # @yield [el] Called for each element.
  582. # @yieldparam el [*] The element to test.
  583. # @yieldreturn [Boolean] Whether or not to extract the element.
  584. # @return [Array] The extracted elements.
  585. def extract!(array)
  586. out = []
  587. array.reject! do |e|
  588. next false unless yield e
  589. out << e
  590. true
  591. end
  592. out
  593. end
  594. # Returns the ASCII code of the given character.
  595. #
  596. # @param c [String] All characters but the first are ignored.
  597. # @return [Fixnum] The ASCII code of `c`.
  598. def ord(c)
  599. ruby1_8? ? c[0] : c.ord
  600. end
  601. # Flattens the first `n` nested arrays in a cross-version manner.
  602. #
  603. # @param arr [Array] The array to flatten
  604. # @param n [Fixnum] The number of levels to flatten
  605. # @return [Array] The flattened array
  606. def flatten(arr, n)
  607. return arr.flatten(n) unless ruby1_8_6?
  608. return arr if n == 0
  609. arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
  610. end
  611. # Returns the hash code for a set in a cross-version manner.
  612. # Aggravatingly, this is order-dependent in Ruby 1.8.6.
  613. #
  614. # @param set [Set]
  615. # @return [Fixnum] The order-independent hashcode of `set`
  616. def set_hash(set)
  617. return set.hash unless ruby1_8_6?
  618. set.map {|e| e.hash}.uniq.sort.hash
  619. end
  620. # Tests the hash-equality of two sets in a cross-version manner.
  621. # Aggravatingly, this is order-dependent in Ruby 1.8.6.
  622. #
  623. # @param set1 [Set]
  624. # @param set2 [Set]
  625. # @return [Boolean] Whether or not the sets are hashcode equal
  626. def set_eql?(set1, set2)
  627. return set1.eql?(set2) unless ruby1_8_6?
  628. set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
  629. end
  630. # Like `Object#inspect`, but preserves non-ASCII characters rather than escaping them under Ruby 1.9.2.
  631. # This is necessary so that the precompiled Haml template can be `#encode`d into `@options[:encoding]`
  632. # before being evaluated.
  633. #
  634. # @param obj {Object}
  635. # @return {String}
  636. def inspect_obj(obj)
  637. return obj.inspect unless version_geq(::RUBY_VERSION, "1.9.2")
  638. return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
  639. return obj.inspect unless obj.is_a?(String)
  640. '"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
  641. end
  642. # Extracts the non-string vlaues from an array containing both strings and non-strings.
  643. # These values are replaced with escape sequences.
  644. # This can be undone using \{#inject\_values}.
  645. #
  646. # This is useful e.g. when we want to do string manipulation
  647. # on an interpolated string.
  648. #
  649. # The precise format of the resulting string is not guaranteed.
  650. # However, it is guaranteed that newlines and whitespace won't be affected.
  651. #
  652. # @param arr [Array] The array from which values are extracted.
  653. # @return [(String, Array)] The resulting string, and an array of extracted values.
  654. def extract_values(arr)
  655. values = []
  656. return arr.map do |e|
  657. next e.gsub('{', '{{') if e.is_a?(String)
  658. values << e
  659. next "{#{values.count - 1}}"
  660. end.join, values
  661. end
  662. # Undoes \{#extract\_values} by transforming a string with escape sequences
  663. # into an array of strings and non-string values.
  664. #
  665. # @param str [String] The string with escape sequences.
  666. # @param values [Array] The array of values to inject.
  667. # @return [Array] The array of strings and values.
  668. def inject_values(str, values)
  669. return [str.gsub('{{', '{')] if values.empty?
  670. # Add an extra { so that we process the tail end of the string
  671. result = (str + '{{').scan(/(.*?)(?:(\{\{)|\{(\d+)\})/m).map do |(pre, esc, n)|
  672. [pre, esc ? '{' : '', n ? values[n.to_i] : '']
  673. end.flatten(1)
  674. result[-2] = '' # Get rid of the extra {
  675. merge_adjacent_strings(result).reject {|s| s == ''}
  676. end
  677. # Allows modifications to be performed on the string form
  678. # of an array containing both strings and non-strings.
  679. #
  680. # @param arr [Array] The array from which values are extracted.
  681. # @yield [str] A block in which string manipulation can be done to the array.
  682. # @yieldparam str [String] The string form of `arr`.
  683. # @yieldreturn [String] The modified string.
  684. # @return [Array] The modified, interpolated array.
  685. def with_extracted_values(arr)
  686. str, vals = extract_values(arr)
  687. str = yield str
  688. inject_values(str, vals)
  689. end
  690. ## Static Method Stuff
  691. # The context in which the ERB for \{#def\_static\_method} will be run.
  692. class StaticConditionalContext
  693. # @param set [#include?] The set of variables that are defined for this context.
  694. def initialize(set)
  695. @set = set
  696. end
  697. # Checks whether or not a variable is defined for this context.
  698. #
  699. # @param name [Symbol] The name of the variable
  700. # @return [Boolean]
  701. def method_missing(name, *args, &block)
  702. super unless args.empty? && block.nil?
  703. @set.include?(name)
  704. end
  705. end
  706. private
  707. # Calculates the memoization table for the Least Common Subsequence algorithm.
  708. # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
  709. def lcs_table(x, y)
  710. c = Array.new(x.size) {[]}
  711. x.size.times {|i| c[i][0] = 0}
  712. y.size.times {|j| c[0][j] = 0}
  713. (1...x.size).each do |i|
  714. (1...y.size).each do |j|
  715. c[i][j] =
  716. if yield x[i], y[j]
  717. c[i-1][j-1] + 1
  718. else
  719. [c[i][j-1], c[i-1][j]].max
  720. end
  721. end
  722. end
  723. return c
  724. end
  725. # Computes a single longest common subsequence for arrays x and y.
  726. # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
  727. def lcs_backtrace(c, x, y, i, j, &block)
  728. return [] if i == 0 || j == 0
  729. if v = yield(x[i], y[j])
  730. return lcs_backtrace(c, x, y, i-1, j-1, &block) << v
  731. end
  732. return lcs_backtrace(c, x, y, i, j-1, &block) if c[i][j-1] > c[i-1][j]
  733. return lcs_backtrace(c, x, y, i-1, j, &block)
  734. end
  735. end
  736. end
  737. require 'sass/util/multibyte_string_scanner'