haml 3.1.0.alpha.14 → 3.1.0.alpha.17

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of haml might be problematic. Click here for more details.

Files changed (222) hide show
  1. data/EDGE_GEM_VERSION +1 -1
  2. data/VERSION +1 -1
  3. data/lib/haml.rb +3 -2
  4. data/lib/haml/exec.rb +0 -226
  5. data/lib/sass.rb +8 -0
  6. data/lib/sass/plugin.rb +8 -0
  7. data/lib/sass/rails2_shim.rb +9 -0
  8. data/lib/sass/rails3_shim.rb +16 -0
  9. data/vendor/sass/CONTRIBUTING +3 -0
  10. data/vendor/sass/MIT-LICENSE +20 -0
  11. data/vendor/sass/README.md +201 -0
  12. data/vendor/sass/Rakefile +363 -0
  13. data/vendor/sass/TODO +39 -0
  14. data/vendor/sass/VERSION +1 -0
  15. data/vendor/sass/VERSION_NAME +1 -0
  16. data/vendor/sass/bin/css2sass +13 -0
  17. data/vendor/sass/bin/sass +8 -0
  18. data/vendor/sass/bin/sass-convert +7 -0
  19. data/vendor/sass/doc-src/FAQ.md +35 -0
  20. data/vendor/sass/doc-src/INDENTED_SYNTAX.md +210 -0
  21. data/vendor/sass/doc-src/SASS_CHANGELOG.md +1878 -0
  22. data/vendor/sass/doc-src/SASS_REFERENCE.md +1713 -0
  23. data/vendor/sass/doc-src/SCSS_FOR_SASS_USERS.md +155 -0
  24. data/vendor/sass/ext/extconf.rb +10 -0
  25. data/vendor/sass/extra/update_watch.rb +13 -0
  26. data/vendor/sass/init.rb +18 -0
  27. data/vendor/sass/lib/sass.rb +71 -0
  28. data/vendor/sass/lib/sass/cache_store.rb +208 -0
  29. data/vendor/sass/lib/sass/callbacks.rb +66 -0
  30. data/vendor/sass/lib/sass/css.rb +294 -0
  31. data/vendor/sass/lib/sass/engine.rb +792 -0
  32. data/vendor/sass/lib/sass/environment.rb +143 -0
  33. data/vendor/sass/lib/sass/error.rb +201 -0
  34. data/vendor/sass/lib/sass/exec.rb +619 -0
  35. data/vendor/sass/lib/sass/importers.rb +22 -0
  36. data/vendor/sass/lib/sass/importers/base.rb +138 -0
  37. data/vendor/sass/lib/sass/importers/filesystem.rb +121 -0
  38. data/vendor/sass/lib/sass/less.rb +363 -0
  39. data/vendor/sass/lib/sass/plugin.rb +126 -0
  40. data/vendor/sass/lib/sass/plugin/compiler.rb +346 -0
  41. data/vendor/sass/lib/sass/plugin/configuration.rb +123 -0
  42. data/vendor/sass/lib/sass/plugin/generic.rb +15 -0
  43. data/vendor/sass/lib/sass/plugin/merb.rb +48 -0
  44. data/vendor/sass/lib/sass/plugin/rack.rb +47 -0
  45. data/vendor/sass/lib/sass/plugin/rails.rb +41 -0
  46. data/vendor/sass/lib/sass/plugin/staleness_checker.rb +145 -0
  47. data/vendor/sass/lib/sass/railtie.rb +8 -0
  48. data/vendor/sass/lib/sass/repl.rb +58 -0
  49. data/vendor/sass/lib/sass/root.rb +7 -0
  50. data/vendor/sass/lib/sass/script.rb +63 -0
  51. data/vendor/sass/lib/sass/script/bool.rb +18 -0
  52. data/vendor/sass/lib/sass/script/color.rb +491 -0
  53. data/vendor/sass/lib/sass/script/css_lexer.rb +29 -0
  54. data/vendor/sass/lib/sass/script/css_parser.rb +31 -0
  55. data/vendor/sass/lib/sass/script/funcall.rb +76 -0
  56. data/vendor/sass/lib/sass/script/functions.rb +852 -0
  57. data/vendor/sass/lib/sass/script/interpolation.rb +70 -0
  58. data/vendor/sass/lib/sass/script/lexer.rb +337 -0
  59. data/vendor/sass/lib/sass/script/literal.rb +236 -0
  60. data/vendor/sass/lib/sass/script/node.rb +112 -0
  61. data/vendor/sass/lib/sass/script/number.rb +423 -0
  62. data/vendor/sass/lib/sass/script/operation.rb +90 -0
  63. data/vendor/sass/lib/sass/script/parser.rb +392 -0
  64. data/vendor/sass/lib/sass/script/string.rb +67 -0
  65. data/vendor/sass/lib/sass/script/string_interpolation.rb +93 -0
  66. data/vendor/sass/lib/sass/script/unary_operation.rb +57 -0
  67. data/vendor/sass/lib/sass/script/variable.rb +48 -0
  68. data/vendor/sass/lib/sass/scss.rb +17 -0
  69. data/vendor/sass/lib/sass/scss/css_parser.rb +51 -0
  70. data/vendor/sass/lib/sass/scss/parser.rb +838 -0
  71. data/vendor/sass/lib/sass/scss/rx.rb +126 -0
  72. data/vendor/sass/lib/sass/scss/sass_parser.rb +11 -0
  73. data/vendor/sass/lib/sass/scss/script_lexer.rb +15 -0
  74. data/vendor/sass/lib/sass/scss/script_parser.rb +25 -0
  75. data/vendor/sass/lib/sass/scss/static_parser.rb +40 -0
  76. data/vendor/sass/lib/sass/selector.rb +361 -0
  77. data/vendor/sass/lib/sass/selector/abstract_sequence.rb +62 -0
  78. data/vendor/sass/lib/sass/selector/comma_sequence.rb +82 -0
  79. data/vendor/sass/lib/sass/selector/sequence.rb +236 -0
  80. data/vendor/sass/lib/sass/selector/simple.rb +113 -0
  81. data/vendor/sass/lib/sass/selector/simple_sequence.rb +135 -0
  82. data/vendor/sass/lib/sass/shared.rb +78 -0
  83. data/vendor/sass/lib/sass/tree/comment_node.rb +128 -0
  84. data/vendor/sass/lib/sass/tree/debug_node.rb +36 -0
  85. data/vendor/sass/lib/sass/tree/directive_node.rb +75 -0
  86. data/vendor/sass/lib/sass/tree/extend_node.rb +65 -0
  87. data/vendor/sass/lib/sass/tree/for_node.rb +67 -0
  88. data/vendor/sass/lib/sass/tree/if_node.rb +81 -0
  89. data/vendor/sass/lib/sass/tree/import_node.rb +124 -0
  90. data/vendor/sass/lib/sass/tree/mixin_def_node.rb +60 -0
  91. data/vendor/sass/lib/sass/tree/mixin_node.rb +123 -0
  92. data/vendor/sass/lib/sass/tree/node.rb +490 -0
  93. data/vendor/sass/lib/sass/tree/prop_node.rb +220 -0
  94. data/vendor/sass/lib/sass/tree/root_node.rb +125 -0
  95. data/vendor/sass/lib/sass/tree/rule_node.rb +273 -0
  96. data/vendor/sass/lib/sass/tree/variable_node.rb +39 -0
  97. data/vendor/sass/lib/sass/tree/warn_node.rb +42 -0
  98. data/vendor/sass/lib/sass/tree/while_node.rb +48 -0
  99. data/vendor/sass/lib/sass/util.rb +700 -0
  100. data/vendor/sass/lib/sass/util/subset_map.rb +101 -0
  101. data/vendor/sass/lib/sass/version.rb +109 -0
  102. data/vendor/sass/rails/init.rb +1 -0
  103. data/vendor/sass/sass.gemspec +32 -0
  104. data/vendor/sass/test/sass/cache_test.rb +74 -0
  105. data/vendor/sass/test/sass/callbacks_test.rb +61 -0
  106. data/vendor/sass/test/sass/conversion_test.rb +1210 -0
  107. data/vendor/sass/test/sass/css2sass_test.rb +364 -0
  108. data/vendor/sass/test/sass/data/hsl-rgb.txt +319 -0
  109. data/vendor/sass/test/sass/engine_test.rb +2305 -0
  110. data/vendor/sass/test/sass/extend_test.rb +1348 -0
  111. data/vendor/sass/test/sass/functions_test.rb +565 -0
  112. data/vendor/sass/test/sass/importer_test.rb +104 -0
  113. data/vendor/sass/test/sass/less_conversion_test.rb +632 -0
  114. data/vendor/sass/test/sass/mock_importer.rb +49 -0
  115. data/vendor/sass/test/sass/more_results/more1.css +9 -0
  116. data/vendor/sass/test/sass/more_results/more1_with_line_comments.css +26 -0
  117. data/vendor/sass/test/sass/more_results/more_import.css +29 -0
  118. data/vendor/sass/test/sass/more_templates/_more_partial.sass +2 -0
  119. data/vendor/sass/test/sass/more_templates/more1.sass +23 -0
  120. data/vendor/sass/test/sass/more_templates/more_import.sass +11 -0
  121. data/vendor/sass/test/sass/plugin_test.rb +430 -0
  122. data/vendor/sass/test/sass/results/alt.css +4 -0
  123. data/vendor/sass/test/sass/results/basic.css +9 -0
  124. data/vendor/sass/test/sass/results/compact.css +5 -0
  125. data/vendor/sass/test/sass/results/complex.css +86 -0
  126. data/vendor/sass/test/sass/results/compressed.css +1 -0
  127. data/vendor/sass/test/sass/results/expanded.css +19 -0
  128. data/vendor/sass/test/sass/results/import.css +31 -0
  129. data/vendor/sass/test/sass/results/line_numbers.css +49 -0
  130. data/vendor/sass/test/sass/results/mixins.css +95 -0
  131. data/vendor/sass/test/sass/results/multiline.css +24 -0
  132. data/vendor/sass/test/sass/results/nested.css +22 -0
  133. data/vendor/sass/test/sass/results/options.css +1 -0
  134. data/vendor/sass/test/sass/results/parent_ref.css +13 -0
  135. data/vendor/sass/test/sass/results/script.css +16 -0
  136. data/vendor/sass/test/sass/results/scss_import.css +31 -0
  137. data/vendor/sass/test/sass/results/scss_importee.css +2 -0
  138. data/vendor/sass/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  139. data/vendor/sass/test/sass/results/subdir/subdir.css +3 -0
  140. data/vendor/sass/test/sass/results/units.css +11 -0
  141. data/vendor/sass/test/sass/results/warn.css +0 -0
  142. data/vendor/sass/test/sass/results/warn_imported.css +0 -0
  143. data/vendor/sass/test/sass/script_conversion_test.rb +254 -0
  144. data/vendor/sass/test/sass/script_test.rb +470 -0
  145. data/vendor/sass/test/sass/scss/css_test.rb +897 -0
  146. data/vendor/sass/test/sass/scss/rx_test.rb +156 -0
  147. data/vendor/sass/test/sass/scss/scss_test.rb +1088 -0
  148. data/vendor/sass/test/sass/scss/test_helper.rb +37 -0
  149. data/vendor/sass/test/sass/templates/_partial.sass +2 -0
  150. data/vendor/sass/test/sass/templates/alt.sass +16 -0
  151. data/vendor/sass/test/sass/templates/basic.sass +23 -0
  152. data/vendor/sass/test/sass/templates/bork1.sass +2 -0
  153. data/vendor/sass/test/sass/templates/bork2.sass +2 -0
  154. data/vendor/sass/test/sass/templates/bork3.sass +2 -0
  155. data/vendor/sass/test/sass/templates/bork4.sass +2 -0
  156. data/vendor/sass/test/sass/templates/compact.sass +17 -0
  157. data/vendor/sass/test/sass/templates/complex.sass +305 -0
  158. data/vendor/sass/test/sass/templates/compressed.sass +15 -0
  159. data/vendor/sass/test/sass/templates/expanded.sass +17 -0
  160. data/vendor/sass/test/sass/templates/import.sass +12 -0
  161. data/vendor/sass/test/sass/templates/importee.less +2 -0
  162. data/vendor/sass/test/sass/templates/importee.sass +19 -0
  163. data/vendor/sass/test/sass/templates/line_numbers.sass +13 -0
  164. data/vendor/sass/test/sass/templates/mixin_bork.sass +5 -0
  165. data/vendor/sass/test/sass/templates/mixins.sass +76 -0
  166. data/vendor/sass/test/sass/templates/multiline.sass +20 -0
  167. data/vendor/sass/test/sass/templates/nested.sass +25 -0
  168. data/vendor/sass/test/sass/templates/nested_bork1.sass +2 -0
  169. data/vendor/sass/test/sass/templates/nested_bork2.sass +2 -0
  170. data/vendor/sass/test/sass/templates/nested_bork3.sass +2 -0
  171. data/vendor/sass/test/sass/templates/nested_bork4.sass +2 -0
  172. data/vendor/sass/test/sass/templates/nested_mixin_bork.sass +6 -0
  173. data/vendor/sass/test/sass/templates/options.sass +2 -0
  174. data/vendor/sass/test/sass/templates/parent_ref.sass +25 -0
  175. data/vendor/sass/test/sass/templates/script.sass +101 -0
  176. data/vendor/sass/test/sass/templates/scss_import.scss +11 -0
  177. data/vendor/sass/test/sass/templates/scss_importee.scss +1 -0
  178. data/vendor/sass/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  179. data/vendor/sass/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  180. data/vendor/sass/test/sass/templates/subdir/subdir.sass +6 -0
  181. data/vendor/sass/test/sass/templates/units.sass +11 -0
  182. data/vendor/sass/test/sass/templates/warn.sass +3 -0
  183. data/vendor/sass/test/sass/templates/warn_imported.sass +4 -0
  184. data/vendor/sass/test/sass/test_helper.rb +8 -0
  185. data/vendor/sass/test/sass/util/subset_map_test.rb +91 -0
  186. data/vendor/sass/test/sass/util_test.rb +275 -0
  187. data/vendor/sass/test/test_helper.rb +64 -0
  188. data/vendor/sass/vendor/fssm/LICENSE +20 -0
  189. data/vendor/sass/vendor/fssm/README.markdown +55 -0
  190. data/vendor/sass/vendor/fssm/Rakefile +59 -0
  191. data/vendor/sass/vendor/fssm/VERSION.yml +5 -0
  192. data/vendor/sass/vendor/fssm/example.rb +9 -0
  193. data/vendor/sass/vendor/fssm/fssm.gemspec +77 -0
  194. data/vendor/sass/vendor/fssm/lib/fssm.rb +33 -0
  195. data/vendor/sass/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
  196. data/vendor/sass/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
  197. data/vendor/sass/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
  198. data/vendor/sass/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
  199. data/vendor/sass/vendor/fssm/lib/fssm/monitor.rb +26 -0
  200. data/vendor/sass/vendor/fssm/lib/fssm/path.rb +91 -0
  201. data/vendor/sass/vendor/fssm/lib/fssm/pathname.rb +502 -0
  202. data/vendor/sass/vendor/fssm/lib/fssm/state/directory.rb +57 -0
  203. data/vendor/sass/vendor/fssm/lib/fssm/state/file.rb +24 -0
  204. data/vendor/sass/vendor/fssm/lib/fssm/support.rb +63 -0
  205. data/vendor/sass/vendor/fssm/lib/fssm/tree.rb +176 -0
  206. data/vendor/sass/vendor/fssm/profile/prof-cache.rb +40 -0
  207. data/vendor/sass/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
  208. data/vendor/sass/vendor/fssm/profile/prof-pathname.rb +68 -0
  209. data/vendor/sass/vendor/fssm/profile/prof-plain-pathname.html +988 -0
  210. data/vendor/sass/vendor/fssm/profile/prof.html +2379 -0
  211. data/vendor/sass/vendor/fssm/spec/path_spec.rb +75 -0
  212. data/vendor/sass/vendor/fssm/spec/root/duck/quack.txt +0 -0
  213. data/vendor/sass/vendor/fssm/spec/root/file.css +0 -0
  214. data/vendor/sass/vendor/fssm/spec/root/file.rb +0 -0
  215. data/vendor/sass/vendor/fssm/spec/root/file.yml +0 -0
  216. data/vendor/sass/vendor/fssm/spec/root/moo/cow.txt +0 -0
  217. data/vendor/sass/vendor/fssm/spec/spec_helper.rb +14 -0
  218. data/vendor/sass/yard/callbacks.rb +29 -0
  219. data/vendor/sass/yard/default/fulldoc/html/css/common.sass +26 -0
  220. data/vendor/sass/yard/default/layout/html/footer.erb +12 -0
  221. data/vendor/sass/yard/inherited_hash.rb +41 -0
  222. metadata +219 -2
@@ -0,0 +1,39 @@
1
+ module Sass
2
+ module Tree
3
+ # A dynamic node representing a variable definition.
4
+ #
5
+ # @see Sass::Tree
6
+ class VariableNode < Node
7
+ # @param name [String] The name of the variable
8
+ # @param expr [Script::Node] The parse tree for the initial variable value
9
+ # @param guarded [Boolean] Whether this is a guarded variable assignment (`||=`)
10
+ def initialize(name, expr, guarded)
11
+ @name = name
12
+ @expr = expr
13
+ @guarded = guarded
14
+ super()
15
+ end
16
+
17
+ protected
18
+
19
+ # @see Node#to_src
20
+ def to_src(tabs, opts, fmt)
21
+ "#{' ' * tabs}$#{dasherize(@name, opts)}: #{@expr.to_sass(opts)}#{' !default' if @guarded}#{semi fmt}\n"
22
+ end
23
+
24
+ # Loads the new variable value into the environment.
25
+ #
26
+ # @param environment [Sass::Environment] The lexical environment containing
27
+ # variable and mixin values
28
+ def _perform(environment)
29
+ return [] if @guarded && !environment.var(@name).nil?
30
+ val = @expr.perform(environment)
31
+ if @expr.context == :equals && val.is_a?(Sass::Script::String)
32
+ val = Sass::Script::String.new(val.value)
33
+ end
34
+ environment.set_var(@name, val)
35
+ []
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,42 @@
1
+ module Sass
2
+ module Tree
3
+ # A dynamic node representing a Sass `@warn` statement.
4
+ #
5
+ # @see Sass::Tree
6
+ class WarnNode < Node
7
+ # @param expr [Script::Node] The expression to print
8
+ def initialize(expr)
9
+ @expr = expr
10
+ super()
11
+ end
12
+
13
+ protected
14
+
15
+ # @see Node#to_src
16
+ def to_src(tabs, opts, fmt)
17
+ "#{' ' * tabs}@warn #{@expr.to_sass(opts)}#{semi fmt}\n"
18
+ end
19
+
20
+ # Prints the expression to STDERR with a stylesheet trace.
21
+ #
22
+ # @param environment [Sass::Environment] The lexical environment containing
23
+ # variable and mixin values
24
+ def _perform(environment)
25
+ environment.push_frame(:filename => filename, :line => line)
26
+ res = @expr.perform(environment)
27
+ res = res.value if res.is_a?(Sass::Script::String)
28
+ msg = "WARNING: #{res}\n"
29
+ environment.stack.reverse.each_with_index do |entry, i|
30
+ msg << " #{i == 0 ? "on" : "from"} line #{entry[:line]}" <<
31
+ " of #{entry[:filename] || "an unknown file"}"
32
+ msg << ", in `#{entry[:mixin]}'" if entry[:mixin]
33
+ msg << "\n"
34
+ end
35
+ Sass::Util.sass_warn msg
36
+ []
37
+ ensure
38
+ environment.pop_frame
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,48 @@
1
+ require 'sass/tree/node'
2
+
3
+ module Sass::Tree
4
+ # A dynamic node representing a Sass `@while` loop.
5
+ #
6
+ # @see Sass::Tree
7
+ class WhileNode < Node
8
+ # @param expr [Script::Node] The parse tree for the continue expression
9
+ def initialize(expr)
10
+ @expr = expr
11
+ super()
12
+ end
13
+
14
+ protected
15
+
16
+ # @see Node#to_src
17
+ def to_src(tabs, opts, fmt)
18
+ "#{' ' * tabs}@while #{@expr.to_sass(opts)}" + children_to_src(tabs, opts, fmt)
19
+ end
20
+
21
+ # Runs the child nodes until the continue expression becomes false.
22
+ #
23
+ # @param environment [Sass::Environment] The lexical environment containing
24
+ # variable and mixin values
25
+ # @return [Array<Tree::Node>] The resulting static nodes
26
+ # @see Sass::Tree
27
+ def _perform(environment)
28
+ children = []
29
+ new_environment = Sass::Environment.new(environment)
30
+ while @expr.perform(environment).to_bool
31
+ children += perform_children(new_environment)
32
+ end
33
+ children
34
+ end
35
+
36
+ # Returns an error message if the given child node is invalid,
37
+ # and false otherwise.
38
+ #
39
+ # {ExtendNode}s are valid within {WhileNode}s.
40
+ #
41
+ # @param child [Tree::Node] A potential child node.
42
+ # @return [Boolean, String] Whether or not the child node is valid,
43
+ # as well as the error message to display if it is invalid
44
+ def invalid_child?(child)
45
+ super unless child.is_a?(ExtendNode)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,700 @@
1
+ require 'erb'
2
+ require 'set'
3
+ require 'enumerator'
4
+ require 'stringio'
5
+ require 'strscan'
6
+ require 'rbconfig'
7
+
8
+ require 'sass/root'
9
+ require 'sass/util/subset_map'
10
+
11
+ module Sass
12
+ # A module containing various useful functions.
13
+ module Util
14
+ extend self
15
+
16
+ # An array of ints representing the Ruby version number.
17
+ # @api public
18
+ RUBY_VERSION = ::RUBY_VERSION.split(".").map {|s| s.to_i}
19
+
20
+ # Returns the path of a file relative to the Sass root directory.
21
+ #
22
+ # @param file [String] The filename relative to the Sass root
23
+ # @return [String] The filename relative to the the working directory
24
+ def scope(file)
25
+ File.join(Sass::ROOT_DIR, file)
26
+ end
27
+
28
+ # Converts an array of `[key, value]` pairs to a hash.
29
+ #
30
+ # @example
31
+ # to_hash([[:foo, "bar"], [:baz, "bang"]])
32
+ # #=> {:foo => "bar", :baz => "bang"}
33
+ # @param arr [Array<(Object, Object)>] An array of pairs
34
+ # @return [Hash] A hash
35
+ def to_hash(arr)
36
+ Hash[arr.compact]
37
+ end
38
+
39
+ # Maps the keys in a hash according to a block.
40
+ #
41
+ # @example
42
+ # map_keys({:foo => "bar", :baz => "bang"}) {|k| k.to_s}
43
+ # #=> {"foo" => "bar", "baz" => "bang"}
44
+ # @param hash [Hash] The hash to map
45
+ # @yield [key] A block in which the keys are transformed
46
+ # @yieldparam key [Object] The key that should be mapped
47
+ # @yieldreturn [Object] The new value for the key
48
+ # @return [Hash] The mapped hash
49
+ # @see #map_vals
50
+ # @see #map_hash
51
+ def map_keys(hash)
52
+ to_hash(hash.map {|k, v| [yield(k), v]})
53
+ end
54
+
55
+ # Maps the values in a hash according to a block.
56
+ #
57
+ # @example
58
+ # map_values({:foo => "bar", :baz => "bang"}) {|v| v.to_sym}
59
+ # #=> {:foo => :bar, :baz => :bang}
60
+ # @param hash [Hash] The hash to map
61
+ # @yield [value] A block in which the values are transformed
62
+ # @yieldparam value [Object] The value that should be mapped
63
+ # @yieldreturn [Object] The new value for the value
64
+ # @return [Hash] The mapped hash
65
+ # @see #map_keys
66
+ # @see #map_hash
67
+ def map_vals(hash)
68
+ to_hash(hash.map {|k, v| [k, yield(v)]})
69
+ end
70
+
71
+ # Maps the key-value pairs of a hash according to a block.
72
+ #
73
+ # @example
74
+ # map_hash({:foo => "bar", :baz => "bang"}) {|k, v| [k.to_s, v.to_sym]}
75
+ # #=> {"foo" => :bar, "baz" => :bang}
76
+ # @param hash [Hash] The hash to map
77
+ # @yield [key, value] A block in which the key-value pairs are transformed
78
+ # @yieldparam [key] The hash key
79
+ # @yieldparam [value] The hash value
80
+ # @yieldreturn [(Object, Object)] The new value for the `[key, value]` pair
81
+ # @return [Hash] The mapped hash
82
+ # @see #map_keys
83
+ # @see #map_vals
84
+ def map_hash(hash, &block)
85
+ to_hash(hash.map(&block))
86
+ end
87
+
88
+ # Computes the powerset of the given array.
89
+ # This is the set of all subsets of the array.
90
+ #
91
+ # @example
92
+ # powerset([1, 2, 3]) #=>
93
+ # Set[Set[], Set[1], Set[2], Set[3], Set[1, 2], Set[2, 3], Set[1, 3], Set[1, 2, 3]]
94
+ # @param arr [Enumerable]
95
+ # @return [Set<Set>] The subsets of `arr`
96
+ def powerset(arr)
97
+ arr.inject([Set.new].to_set) do |powerset, el|
98
+ new_powerset = Set.new
99
+ powerset.each do |subset|
100
+ new_powerset << subset
101
+ new_powerset << subset + [el]
102
+ end
103
+ new_powerset
104
+ end
105
+ end
106
+
107
+ # Restricts a number to falling within a given range.
108
+ # Returns the number if it falls within the range,
109
+ # or the closest value in the range if it doesn't.
110
+ #
111
+ # @param value [Numeric]
112
+ # @param range [Range<Numeric>]
113
+ # @return [Numeric]
114
+ def restrict(value, range)
115
+ [[value, range.first].max, range.last].min
116
+ end
117
+
118
+ # Concatenates all strings that are adjacent in an array,
119
+ # while leaving other elements as they are.
120
+ #
121
+ # @example
122
+ # merge_adjacent_strings([1, "foo", "bar", 2, "baz"])
123
+ # #=> [1, "foobar", 2, "baz"]
124
+ # @param enum [Enumerable]
125
+ # @return [Array] The enumerable with strings merged
126
+ def merge_adjacent_strings(enum)
127
+ enum.inject([]) do |a, e|
128
+ if e.is_a?(String)
129
+ if a.last.is_a?(String)
130
+ a.last << e
131
+ else
132
+ a << e.dup
133
+ end
134
+ else
135
+ a << e
136
+ end
137
+ a
138
+ end
139
+ end
140
+
141
+ # Intersperses a value in an enumerable, as would be done with `Array#join`
142
+ # but without concatenating the array together afterwards.
143
+ #
144
+ # @param enum [Enumerable]
145
+ # @param val
146
+ # @return [Array]
147
+ def intersperse(enum, val)
148
+ enum.inject([]) {|a, e| a << e << val}[0...-1]
149
+ end
150
+
151
+ # Substitutes a sub-array of one array with another sub-array.
152
+ #
153
+ # @param ary [Array] The array in which to make the substitution
154
+ # @param from [Array] The sequence of elements to replace with `to`
155
+ # @param to [Array] The sequence of elements to replace `from` with
156
+ def substitute(ary, from, to)
157
+ res = ary.dup
158
+ i = 0
159
+ while i < res.size
160
+ if res[i...i+from.size] == from
161
+ res[i...i+from.size] = to
162
+ end
163
+ i += 1
164
+ end
165
+ res
166
+ end
167
+
168
+ # Destructively strips whitespace from the beginning and end
169
+ # of the first and last elements, respectively,
170
+ # in the array (if those elements are strings).
171
+ #
172
+ # @param arr [Array]
173
+ # @return [Array] `arr`
174
+ def strip_string_array(arr)
175
+ arr.first.lstrip! if arr.first.is_a?(String)
176
+ arr.last.rstrip! if arr.last.is_a?(String)
177
+ arr
178
+ end
179
+
180
+ # Return an array of all possible paths through the given arrays.
181
+ #
182
+ # @param arrs [Array<Array>]
183
+ # @return [Array<Arrays>]
184
+ #
185
+ # @example
186
+ # paths([[1, 2], [3, 4], [5]]) #=>
187
+ # # [[1, 3, 5],
188
+ # # [2, 3, 5],
189
+ # # [1, 4, 5],
190
+ # # [2, 4, 5]]
191
+ def paths(arrs)
192
+ arrs.inject([[]]) do |paths, arr|
193
+ flatten(arr.map {|e| paths.map {|path| path + [e]}}, 1)
194
+ end
195
+ end
196
+
197
+ # Computes a single longest common subsequence for `x` and `y`.
198
+ # If there are more than one longest common subsequences,
199
+ # the one returned is that which starts first in `x`.
200
+ #
201
+ # @param x [Array]
202
+ # @param y [Array]
203
+ # @yield [a, b] An optional block to use in place of a check for equality
204
+ # between elements of `x` and `y`.
205
+ # @yieldreturn [Object, nil] If the two values register as equal,
206
+ # this will return the value to use in the LCS array.
207
+ # @return [Array] The LCS
208
+ def lcs(x, y, &block)
209
+ x = [nil, *x]
210
+ y = [nil, *y]
211
+ block ||= proc {|a, b| a == b && a}
212
+ lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
213
+ end
214
+
215
+ # Returns information about the caller of the previous method.
216
+ #
217
+ # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
218
+ # @return [[String, Fixnum, (String, nil)]] An array containing the filename, line, and method name of the caller.
219
+ # The method name may be nil
220
+ def caller_info(entry = caller[1])
221
+ info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
222
+ info[1] = info[1].to_i
223
+ # This is added by Rubinius to designate a block, but we don't care about it.
224
+ info[2].sub!(/ \{\}\Z/, '') if info[2]
225
+ info
226
+ end
227
+
228
+ # Returns whether one version string represents a more recent version than another.
229
+ #
230
+ # @param v1 [String] A version string.
231
+ # @param v2 [String] Another version string.
232
+ # @return [Boolean]
233
+ def version_gt(v1, v2)
234
+ # Construct an array to make sure the shorter version is padded with nil
235
+ Array.new([v1.length, v2.length].max).zip(v1.split("."), v2.split(".")) do |_, p1, p2|
236
+ p1 ||= "0"
237
+ p2 ||= "0"
238
+ release1 = p1 =~ /^[0-9]+$/
239
+ release2 = p2 =~ /^[0-9]+$/
240
+ if release1 && release2
241
+ # Integer comparison if both are full releases
242
+ p1, p2 = p1.to_i, p2.to_i
243
+ next if p1 == p2
244
+ return p1 > p2
245
+ elsif !release1 && !release2
246
+ # String comparison if both are prereleases
247
+ next if p1 == p2
248
+ return p1 > p2
249
+ else
250
+ # If only one is a release, that one is newer
251
+ return release1
252
+ end
253
+ end
254
+ end
255
+
256
+ # Returns whether one version string represents the same or a more
257
+ # recent version than another.
258
+ #
259
+ # @param v1 [String] A version string.
260
+ # @param v2 [String] Another version string.
261
+ # @return [Boolean]
262
+ def version_geq(v1, v2)
263
+ version_gt(v1, v2) || !version_gt(v2, v1)
264
+ end
265
+
266
+ # A wrapper for `Marshal.dump` that calls `#_before_dump` on the object
267
+ # before dumping it, `#_after_dump` afterwards.
268
+ # It also calls `#_around_dump` and passes it a block in which the object is dumped.
269
+ #
270
+ # If any of these methods are undefined, they are not called.
271
+ #
272
+ # This will recursively call itself on members of arrays and hashes,
273
+ # but not of user-defined objects.
274
+ # This means that user-defined objects that need their members' `#_before_dump` etc. methods called
275
+ # must call `Haml::Util.dump` and `Haml::Util.load` manually on those members.
276
+ #
277
+ # @param obj [Object] The object to dump.
278
+ # @return [String] The dumped data.
279
+ def dump(obj)
280
+ obj._before_dump if obj.respond_to?(:_before_dump)
281
+ return convert_and_dump(obj) unless obj.respond_to?(:_around_dump)
282
+ res = nil
283
+ obj._around_dump {res = convert_and_dump(obj)}
284
+ res
285
+ ensure
286
+ obj._after_dump if obj.respond_to?(:_after_dump)
287
+ end
288
+
289
+ # A wrapper for `Marshal.load` that calls `#_after_load` on the object
290
+ # after loading it, if it's defined.
291
+ #
292
+ # @param data [String] The data to load.
293
+ # @return [Object] The loaded object.
294
+ def load(data)
295
+ obj = Marshal.load(data)
296
+
297
+ if obj.is_a?(Array)
298
+ obj = obj.map {|e| Sass::Util.load(e)}
299
+ elsif obj.is_a?(Hash)
300
+ obj = map_hash(obj) {|k, v| [Sass::Util.load(k), Sass::Util.load(v)]}
301
+ end
302
+
303
+ obj._after_load if obj.respond_to?(:_after_load)
304
+ obj
305
+ end
306
+
307
+ # Throws a NotImplementedError for an abstract method.
308
+ #
309
+ # @param obj [Object] `self`
310
+ # @raise [NotImplementedError]
311
+ def abstract(obj)
312
+ raise NotImplementedError.new("#{obj.class} must implement ##{caller_info[2]}")
313
+ end
314
+
315
+ # Silence all output to STDERR within a block.
316
+ #
317
+ # @yield A block in which no output will be printed to STDERR
318
+ def silence_warnings
319
+ the_real_stderr, $stderr = $stderr, StringIO.new
320
+ yield
321
+ ensure
322
+ $stderr = the_real_stderr
323
+ end
324
+
325
+ @@silence_warnings = false
326
+ # Silences all Sass warnings within a block.
327
+ #
328
+ # @yield A block in which no Sass warnings will be printed
329
+ def silence_sass_warnings
330
+ old_silence_warnings = @@silence_warnings
331
+ @@silence_warnings = true
332
+ yield
333
+ ensure
334
+ @@silence_warnings = old_silence_warnings
335
+ end
336
+
337
+ # The same as `Kernel#warn`, but is silenced by \{#silence\_sass\_warnings}.
338
+ #
339
+ # @param msg [String]
340
+ def sass_warn(msg)
341
+ return if @@silence_warnings
342
+ warn(msg)
343
+ end
344
+
345
+ ## Cross Rails Version Compatibility
346
+
347
+ # Returns the root of the Rails application,
348
+ # if this is running in a Rails context.
349
+ # Returns `nil` if no such root is defined.
350
+ #
351
+ # @return [String, nil]
352
+ def rails_root
353
+ if defined?(::Rails.root)
354
+ return ::Rails.root.to_s if ::Rails.root
355
+ raise "ERROR: Rails.root is nil!"
356
+ end
357
+ return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
358
+ return nil
359
+ end
360
+
361
+ # Returns the environment of the Rails application,
362
+ # if this is running in a Rails context.
363
+ # Returns `nil` if no such environment is defined.
364
+ #
365
+ # @return [String, nil]
366
+ def rails_env
367
+ return ::Rails.env.to_s if defined?(::Rails.env)
368
+ return RAILS_ENV.to_s if defined?(RAILS_ENV)
369
+ return nil
370
+ end
371
+
372
+ # Returns whether this environment is using ActionPack
373
+ # version 3.0.0 or greater.
374
+ #
375
+ # @return [Boolean]
376
+ def ap_geq_3?
377
+ ap_geq?("3.0.0.beta1")
378
+ end
379
+
380
+ # Returns whether this environment is using ActionPack
381
+ # of a version greater than or equal to that specified.
382
+ #
383
+ # @param version [String] The string version number to check against.
384
+ # Should be greater than or equal to Rails 3,
385
+ # because otherwise ActionPack::VERSION isn't autoloaded
386
+ # @return [Boolean]
387
+ def ap_geq?(version)
388
+ # The ActionPack module is always loaded automatically in Rails >= 3
389
+ return false unless defined?(ActionPack) && defined?(ActionPack::VERSION) &&
390
+ defined?(ActionPack::VERSION::STRING)
391
+
392
+ version_geq(ActionPack::VERSION::STRING, version)
393
+ end
394
+
395
+ # Returns an ActionView::Template* class.
396
+ # In pre-3.0 versions of Rails, most of these classes
397
+ # were of the form `ActionView::TemplateFoo`,
398
+ # while afterwards they were of the form `ActionView;:Template::Foo`.
399
+ #
400
+ # @param name [#to_s] The name of the class to get.
401
+ # For example, `:Error` will return `ActionView::TemplateError`
402
+ # or `ActionView::Template::Error`.
403
+ def av_template_class(name)
404
+ return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
405
+ return ActionView::Template.const_get(name.to_s)
406
+ end
407
+
408
+ ## Cross-OS Compatibility
409
+
410
+ # Whether or not this is running on Windows.
411
+ #
412
+ # @return [Boolean]
413
+ def windows?
414
+ RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i
415
+ end
416
+
417
+ ## Cross-Ruby-Version Compatibility
418
+
419
+ # Whether or not this is running under Ruby 1.8 or lower.
420
+ #
421
+ # @return [Boolean]
422
+ def ruby1_8?
423
+ Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9
424
+ end
425
+
426
+ # Whether or not this is running under Ruby 1.8.6 or lower.
427
+ # Note that lower versions are not officially supported.
428
+ #
429
+ # @return [Boolean]
430
+ def ruby1_8_6?
431
+ ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
432
+ end
433
+
434
+ # Checks that the encoding of a string is valid in Ruby 1.9
435
+ # and cleans up potential encoding gotchas like the UTF-8 BOM.
436
+ # If it's not, yields an error string describing the invalid character
437
+ # and the line on which it occurrs.
438
+ #
439
+ # @param str [String] The string of which to check the encoding
440
+ # @yield [msg] A block in which an encoding error can be raised.
441
+ # Only yields if there is an encoding error
442
+ # @yieldparam msg [String] The error message to be raised
443
+ # @return [String] `str`, potentially with encoding gotchas like BOMs removed
444
+ def check_encoding(str)
445
+ if ruby1_8?
446
+ return str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM
447
+ elsif str.valid_encoding?
448
+ # Get rid of the Unicode BOM if possible
449
+ if str.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?$/
450
+ return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '')
451
+ else
452
+ return str
453
+ end
454
+ end
455
+
456
+ encoding = str.encoding
457
+ newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding("binary"))
458
+ str.force_encoding("binary").split(newlines).each_with_index do |line, i|
459
+ begin
460
+ line.encode(encoding)
461
+ rescue Encoding::UndefinedConversionError => e
462
+ yield <<MSG.rstrip, i + 1
463
+ Invalid #{encoding.name} character #{e.error_char.dump}
464
+ MSG
465
+ end
466
+ end
467
+ return str
468
+ end
469
+
470
+ # Like {\#check\_encoding}, but also checks for a `@charset` declaration
471
+ # at the beginning of the file and uses that encoding if it exists.
472
+ #
473
+ # The Sass encoding rules are simple.
474
+ # If a `@charset` declaration exists,
475
+ # we assume that that's the original encoding of the document.
476
+ # Otherwise, we use whatever encoding Ruby has.
477
+ # Then we convert that to UTF-8 to process internally.
478
+ # The UTF-8 end result is what's returned by this method.
479
+ #
480
+ # @param str [String] The string of which to check the encoding
481
+ # @yield [msg] A block in which an encoding error can be raised.
482
+ # Only yields if there is an encoding error
483
+ # @yieldparam msg [String] The error message to be raised
484
+ # @return [(String, Encoding)] The original string encoded as UTF-8,
485
+ # and the source encoding of the string (or `nil` under Ruby 1.8)
486
+ # @raise [Encoding::UndefinedConversionError] if the source encoding
487
+ # cannot be converted to UTF-8
488
+ # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
489
+ def check_sass_encoding(str, &block)
490
+ return check_encoding(str, &block), nil if ruby1_8?
491
+ # We allow any printable ASCII characters but double quotes in the charset decl
492
+ bin = str.dup.force_encoding("BINARY")
493
+ encoding = Sass::Util::ENCODINGS_TO_CHECK.find do |enc|
494
+ bin =~ Sass::Util::CHARSET_REGEXPS[enc]
495
+ end
496
+ charset, bom = $1, $2
497
+ if charset
498
+ charset = charset.force_encoding(encoding).encode("UTF-8")
499
+ if endianness = encoding[/[BL]E$/]
500
+ begin
501
+ Encoding.find(charset + endianness)
502
+ charset << endianness
503
+ rescue ArgumentError # Encoding charset + endianness doesn't exist
504
+ end
505
+ end
506
+ str.force_encoding(charset)
507
+ elsif bom
508
+ str.force_encoding(encoding)
509
+ end
510
+
511
+ str = check_encoding(str, &block)
512
+ return str.encode("UTF-8"), str.encoding
513
+ end
514
+
515
+ unless ruby1_8?
516
+ # @private
517
+ def _enc(string, encoding)
518
+ string.encode(encoding).force_encoding("BINARY")
519
+ end
520
+
521
+ # We could automatically add in any non-ASCII-compatible encodings here,
522
+ # but there's not really a good way to do that
523
+ # without manually checking that each encoding
524
+ # encodes all ASCII characters properly,
525
+ # which takes long enough to affect the startup time of the CLI.
526
+ ENCODINGS_TO_CHECK = %w[UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE]
527
+
528
+ CHARSET_REGEXPS = Hash.new do |h, e|
529
+ h[e] =
530
+ begin
531
+ # /\A(?:\uFEFF)?@charset "(.*?)"|\A(\uFEFF)/
532
+ Regexp.new(/\A(?:#{_enc("\uFEFF", e)})?#{
533
+ _enc('@charset "', e)}(.*?)#{_enc('"', e)}|\A(#{
534
+ _enc("\uFEFF", e)})/)
535
+ rescue
536
+ # /\A@charset "(.*?)"/
537
+ Regexp.new(/\A#{_enc('@charset "', e)}(.*?)#{_enc('"', e)}/)
538
+ end
539
+ end
540
+ end
541
+
542
+ # Checks to see if a class has a given method.
543
+ # For example:
544
+ #
545
+ # Sass::Util.has?(:public_instance_method, String, :gsub) #=> true
546
+ #
547
+ # Method collections like `Class#instance_methods`
548
+ # return strings in Ruby 1.8 and symbols in Ruby 1.9 and on,
549
+ # so this handles checking for them in a compatible way.
550
+ #
551
+ # @param attr [#to_s] The (singular) name of the method-collection method
552
+ # (e.g. `:instance_methods`, `:private_methods`)
553
+ # @param klass [Module] The class to check the methods of which to check
554
+ # @param method [String, Symbol] The name of the method do check for
555
+ # @return [Boolean] Whether or not the given collection has the given method
556
+ def has?(attr, klass, method)
557
+ klass.send("#{attr}s").include?(ruby1_8? ? method.to_s : method.to_sym)
558
+ end
559
+
560
+ # A version of `Enumerable#enum_with_index` that works in Ruby 1.8 and 1.9.
561
+ #
562
+ # @param enum [Enumerable] The enumerable to get the enumerator for
563
+ # @return [Enumerator] The with-index enumerator
564
+ def enum_with_index(enum)
565
+ ruby1_8? ? enum.enum_with_index : enum.each_with_index
566
+ end
567
+
568
+ # A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.
569
+ #
570
+ # @param enum [Enumerable] The enumerable to get the enumerator for
571
+ # @param n [Fixnum] The size of each cons
572
+ # @return [Enumerator] The consed enumerator
573
+ def enum_cons(enum, n)
574
+ ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
575
+ end
576
+
577
+ # A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.
578
+ #
579
+ # @param enum [Enumerable] The enumerable to get the enumerator for
580
+ # @param n [Fixnum] The size of each slice
581
+ # @return [Enumerator] The consed enumerator
582
+ def enum_slice(enum, n)
583
+ ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
584
+ end
585
+
586
+ # Returns the ASCII code of the given character.
587
+ #
588
+ # @param c [String] All characters but the first are ignored.
589
+ # @return [Fixnum] The ASCII code of `c`.
590
+ def ord(c)
591
+ ruby1_8? ? c[0] : c.ord
592
+ end
593
+
594
+ # Flattens the first `n` nested arrays in a cross-version manner.
595
+ #
596
+ # @param arr [Array] The array to flatten
597
+ # @param n [Fixnum] The number of levels to flatten
598
+ # @return [Array] The flattened array
599
+ def flatten(arr, n)
600
+ return arr.flatten(n) unless ruby1_8_6?
601
+ return arr if n == 0
602
+ arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
603
+ end
604
+
605
+ # Returns the hash code for a set in a cross-version manner.
606
+ # Aggravatingly, this is order-dependent in Ruby 1.8.6.
607
+ #
608
+ # @param set [Set]
609
+ # @return [Fixnum] The order-independent hashcode of `set`
610
+ def set_hash(set)
611
+ return set.hash unless ruby1_8_6?
612
+ set.map {|e| e.hash}.uniq.sort.hash
613
+ end
614
+
615
+ # Tests the hash-equality of two sets in a cross-version manner.
616
+ # Aggravatingly, this is order-dependent in Ruby 1.8.6.
617
+ #
618
+ # @param set1 [Set]
619
+ # @param set2 [Set]
620
+ # @return [Boolean] Whether or not the sets are hashcode equal
621
+ def set_eql?(set1, set2)
622
+ return set1.eql?(set2) unless ruby1_8_6?
623
+ set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
624
+ end
625
+
626
+ # Like `Object#inspect`, but preserves non-ASCII characters rather than escaping them under Ruby 1.9.2.
627
+ # This is necessary so that the precompiled Haml template can be `#encode`d into `@options[:encoding]`
628
+ # before being evaluated.
629
+ #
630
+ # @param obj {Object}
631
+ # @return {String}
632
+ def inspect(obj)
633
+ return obj.inspect unless version_geq(::RUBY_VERSION, "1.9.2")
634
+ return ':' + inspect(obj.to_s) if obj.is_a?(Symbol)
635
+ return obj.inspect unless obj.is_a?(String)
636
+ '"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
637
+ end
638
+
639
+ ## Static Method Stuff
640
+
641
+ # The context in which the ERB for \{#def\_static\_method} will be run.
642
+ class StaticConditionalContext
643
+ # @param set [#include?] The set of variables that are defined for this context.
644
+ def initialize(set)
645
+ @set = set
646
+ end
647
+
648
+ # Checks whether or not a variable is defined for this context.
649
+ #
650
+ # @param name [Symbol] The name of the variable
651
+ # @return [Boolean]
652
+ def method_missing(name, *args, &block)
653
+ super unless args.empty? && block.nil?
654
+ @set.include?(name)
655
+ end
656
+ end
657
+
658
+ private
659
+
660
+ # Calculates the memoization table for the Least Common Subsequence algorithm.
661
+ # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
662
+ def lcs_table(x, y)
663
+ c = Array.new(x.size) {[]}
664
+ x.size.times {|i| c[i][0] = 0}
665
+ y.size.times {|j| c[0][j] = 0}
666
+ (1...x.size).each do |i|
667
+ (1...y.size).each do |j|
668
+ c[i][j] =
669
+ if yield x[i], y[j]
670
+ c[i-1][j-1] + 1
671
+ else
672
+ [c[i][j-1], c[i-1][j]].max
673
+ end
674
+ end
675
+ end
676
+ return c
677
+ end
678
+
679
+ # Computes a single longest common subsequence for arrays x and y.
680
+ # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
681
+ def lcs_backtrace(c, x, y, i, j, &block)
682
+ return [] if i == 0 || j == 0
683
+ if v = yield(x[i], y[j])
684
+ return lcs_backtrace(c, x, y, i-1, j-1, &block) << v
685
+ end
686
+
687
+ return lcs_backtrace(c, x, y, i, j-1, &block) if c[i][j-1] > c[i-1][j]
688
+ return lcs_backtrace(c, x, y, i-1, j, &block)
689
+ end
690
+
691
+ def convert_and_dump(obj)
692
+ if obj.is_a?(Array)
693
+ obj = obj.map {|e| dump(e)}
694
+ elsif obj.is_a?(Hash)
695
+ obj = map_hash(obj) {|k, v| [dump(k), dump(v)]}
696
+ end
697
+ Marshal.dump(obj)
698
+ end
699
+ end
700
+ end