xass 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (252) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/Gemfile +9 -0
  4. data/Gemfile.lock +39 -0
  5. data/README.md +131 -193
  6. data/lib/initialize.rb +8 -0
  7. data/lib/xass.rb +67 -0
  8. data/spec/secrets.yml +4 -0
  9. data/spec/spec_helper.rb +8 -0
  10. data/spec/test_spec.rb +5 -0
  11. data/template/secrets.yml +2 -0
  12. data/xass.gemspec +15 -0
  13. metadata +53 -294
  14. data/.yardopts +0 -11
  15. data/CONTRIBUTING +0 -3
  16. data/MIT-LICENSE +0 -20
  17. data/Rakefile +0 -349
  18. data/VERSION +0 -1
  19. data/VERSION_NAME +0 -1
  20. data/bin/push +0 -13
  21. data/bin/sass +0 -13
  22. data/bin/sass-convert +0 -12
  23. data/bin/scss +0 -13
  24. data/extra/update_watch.rb +0 -13
  25. data/init.rb +0 -18
  26. data/lib/sass/cache_stores/base.rb +0 -88
  27. data/lib/sass/cache_stores/chain.rb +0 -33
  28. data/lib/sass/cache_stores/filesystem.rb +0 -64
  29. data/lib/sass/cache_stores/memory.rb +0 -47
  30. data/lib/sass/cache_stores/null.rb +0 -25
  31. data/lib/sass/cache_stores.rb +0 -15
  32. data/lib/sass/callbacks.rb +0 -66
  33. data/lib/sass/css.rb +0 -409
  34. data/lib/sass/engine.rb +0 -930
  35. data/lib/sass/environment.rb +0 -101
  36. data/lib/sass/error.rb +0 -201
  37. data/lib/sass/exec.rb +0 -707
  38. data/lib/sass/importers/base.rb +0 -139
  39. data/lib/sass/importers/filesystem.rb +0 -186
  40. data/lib/sass/importers.rb +0 -22
  41. data/lib/sass/logger/base.rb +0 -32
  42. data/lib/sass/logger/log_level.rb +0 -49
  43. data/lib/sass/logger.rb +0 -15
  44. data/lib/sass/media.rb +0 -213
  45. data/lib/sass/plugin/compiler.rb +0 -406
  46. data/lib/sass/plugin/configuration.rb +0 -123
  47. data/lib/sass/plugin/generic.rb +0 -15
  48. data/lib/sass/plugin/merb.rb +0 -48
  49. data/lib/sass/plugin/rack.rb +0 -60
  50. data/lib/sass/plugin/rails.rb +0 -47
  51. data/lib/sass/plugin/staleness_checker.rb +0 -199
  52. data/lib/sass/plugin.rb +0 -133
  53. data/lib/sass/railtie.rb +0 -10
  54. data/lib/sass/repl.rb +0 -57
  55. data/lib/sass/root.rb +0 -7
  56. data/lib/sass/script/arg_list.rb +0 -52
  57. data/lib/sass/script/bool.rb +0 -18
  58. data/lib/sass/script/color.rb +0 -606
  59. data/lib/sass/script/css_lexer.rb +0 -29
  60. data/lib/sass/script/css_parser.rb +0 -31
  61. data/lib/sass/script/funcall.rb +0 -245
  62. data/lib/sass/script/functions.rb +0 -1543
  63. data/lib/sass/script/interpolation.rb +0 -79
  64. data/lib/sass/script/lexer.rb +0 -345
  65. data/lib/sass/script/list.rb +0 -85
  66. data/lib/sass/script/literal.rb +0 -221
  67. data/lib/sass/script/node.rb +0 -99
  68. data/lib/sass/script/null.rb +0 -37
  69. data/lib/sass/script/number.rb +0 -453
  70. data/lib/sass/script/operation.rb +0 -110
  71. data/lib/sass/script/parser.rb +0 -502
  72. data/lib/sass/script/string.rb +0 -51
  73. data/lib/sass/script/string_interpolation.rb +0 -103
  74. data/lib/sass/script/unary_operation.rb +0 -69
  75. data/lib/sass/script/variable.rb +0 -58
  76. data/lib/sass/script.rb +0 -39
  77. data/lib/sass/scss/css_parser.rb +0 -36
  78. data/lib/sass/scss/parser.rb +0 -1180
  79. data/lib/sass/scss/rx.rb +0 -133
  80. data/lib/sass/scss/script_lexer.rb +0 -15
  81. data/lib/sass/scss/script_parser.rb +0 -25
  82. data/lib/sass/scss/static_parser.rb +0 -54
  83. data/lib/sass/scss.rb +0 -16
  84. data/lib/sass/selector/abstract_sequence.rb +0 -94
  85. data/lib/sass/selector/comma_sequence.rb +0 -92
  86. data/lib/sass/selector/sequence.rb +0 -507
  87. data/lib/sass/selector/simple.rb +0 -119
  88. data/lib/sass/selector/simple_sequence.rb +0 -215
  89. data/lib/sass/selector.rb +0 -452
  90. data/lib/sass/shared.rb +0 -76
  91. data/lib/sass/supports.rb +0 -229
  92. data/lib/sass/tree/charset_node.rb +0 -22
  93. data/lib/sass/tree/comment_node.rb +0 -82
  94. data/lib/sass/tree/content_node.rb +0 -9
  95. data/lib/sass/tree/css_import_node.rb +0 -60
  96. data/lib/sass/tree/debug_node.rb +0 -18
  97. data/lib/sass/tree/directive_node.rb +0 -42
  98. data/lib/sass/tree/each_node.rb +0 -24
  99. data/lib/sass/tree/extend_node.rb +0 -36
  100. data/lib/sass/tree/for_node.rb +0 -36
  101. data/lib/sass/tree/function_node.rb +0 -34
  102. data/lib/sass/tree/if_node.rb +0 -52
  103. data/lib/sass/tree/import_node.rb +0 -75
  104. data/lib/sass/tree/media_node.rb +0 -58
  105. data/lib/sass/tree/mixin_def_node.rb +0 -38
  106. data/lib/sass/tree/mixin_node.rb +0 -39
  107. data/lib/sass/tree/node.rb +0 -196
  108. data/lib/sass/tree/prop_node.rb +0 -152
  109. data/lib/sass/tree/return_node.rb +0 -18
  110. data/lib/sass/tree/root_node.rb +0 -78
  111. data/lib/sass/tree/rule_node.rb +0 -132
  112. data/lib/sass/tree/supports_node.rb +0 -51
  113. data/lib/sass/tree/trace_node.rb +0 -32
  114. data/lib/sass/tree/variable_node.rb +0 -30
  115. data/lib/sass/tree/visitors/base.rb +0 -75
  116. data/lib/sass/tree/visitors/check_nesting.rb +0 -147
  117. data/lib/sass/tree/visitors/convert.rb +0 -316
  118. data/lib/sass/tree/visitors/cssize.rb +0 -241
  119. data/lib/sass/tree/visitors/deep_copy.rb +0 -102
  120. data/lib/sass/tree/visitors/extend.rb +0 -68
  121. data/lib/sass/tree/visitors/perform.rb +0 -446
  122. data/lib/sass/tree/visitors/set_options.rb +0 -125
  123. data/lib/sass/tree/visitors/to_css.rb +0 -228
  124. data/lib/sass/tree/warn_node.rb +0 -18
  125. data/lib/sass/tree/while_node.rb +0 -18
  126. data/lib/sass/util/multibyte_string_scanner.rb +0 -155
  127. data/lib/sass/util/subset_map.rb +0 -109
  128. data/lib/sass/util/test.rb +0 -10
  129. data/lib/sass/util.rb +0 -948
  130. data/lib/sass/version.rb +0 -126
  131. data/lib/sass.rb +0 -95
  132. data/rails/init.rb +0 -1
  133. data/test/Gemfile +0 -3
  134. data/test/Gemfile.lock +0 -10
  135. data/test/sass/cache_test.rb +0 -89
  136. data/test/sass/callbacks_test.rb +0 -61
  137. data/test/sass/conversion_test.rb +0 -1760
  138. data/test/sass/css2sass_test.rb +0 -458
  139. data/test/sass/data/hsl-rgb.txt +0 -319
  140. data/test/sass/engine_test.rb +0 -3244
  141. data/test/sass/exec_test.rb +0 -86
  142. data/test/sass/extend_test.rb +0 -1482
  143. data/test/sass/fixtures/test_staleness_check_across_importers.css +0 -1
  144. data/test/sass/fixtures/test_staleness_check_across_importers.scss +0 -1
  145. data/test/sass/functions_test.rb +0 -1139
  146. data/test/sass/importer_test.rb +0 -192
  147. data/test/sass/logger_test.rb +0 -58
  148. data/test/sass/mock_importer.rb +0 -49
  149. data/test/sass/more_results/more1.css +0 -9
  150. data/test/sass/more_results/more1_with_line_comments.css +0 -26
  151. data/test/sass/more_results/more_import.css +0 -29
  152. data/test/sass/more_templates/_more_partial.sass +0 -2
  153. data/test/sass/more_templates/more1.sass +0 -23
  154. data/test/sass/more_templates/more_import.sass +0 -11
  155. data/test/sass/plugin_test.rb +0 -564
  156. data/test/sass/results/alt.css +0 -4
  157. data/test/sass/results/basic.css +0 -9
  158. data/test/sass/results/cached_import_option.css +0 -3
  159. data/test/sass/results/compact.css +0 -5
  160. data/test/sass/results/complex.css +0 -86
  161. data/test/sass/results/compressed.css +0 -1
  162. data/test/sass/results/expanded.css +0 -19
  163. data/test/sass/results/filename_fn.css +0 -3
  164. data/test/sass/results/if.css +0 -3
  165. data/test/sass/results/import.css +0 -31
  166. data/test/sass/results/import_charset.css +0 -5
  167. data/test/sass/results/import_charset_1_8.css +0 -5
  168. data/test/sass/results/import_charset_ibm866.css +0 -5
  169. data/test/sass/results/import_content.css +0 -1
  170. data/test/sass/results/line_numbers.css +0 -49
  171. data/test/sass/results/mixins.css +0 -95
  172. data/test/sass/results/multiline.css +0 -24
  173. data/test/sass/results/nested.css +0 -22
  174. data/test/sass/results/options.css +0 -1
  175. data/test/sass/results/parent_ref.css +0 -13
  176. data/test/sass/results/script.css +0 -16
  177. data/test/sass/results/scss_import.css +0 -31
  178. data/test/sass/results/scss_importee.css +0 -2
  179. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +0 -1
  180. data/test/sass/results/subdir/subdir.css +0 -3
  181. data/test/sass/results/units.css +0 -11
  182. data/test/sass/results/warn_imported.css +0 -0
  183. data/test/sass/script_conversion_test.rb +0 -299
  184. data/test/sass/script_test.rb +0 -622
  185. data/test/sass/scss/css_test.rb +0 -1100
  186. data/test/sass/scss/rx_test.rb +0 -156
  187. data/test/sass/scss/scss_test.rb +0 -2106
  188. data/test/sass/scss/test_helper.rb +0 -37
  189. data/test/sass/templates/_cached_import_option_partial.scss +0 -1
  190. data/test/sass/templates/_double_import_loop2.sass +0 -1
  191. data/test/sass/templates/_filename_fn_import.scss +0 -11
  192. data/test/sass/templates/_imported_charset_ibm866.sass +0 -4
  193. data/test/sass/templates/_imported_charset_utf8.sass +0 -4
  194. data/test/sass/templates/_imported_content.sass +0 -3
  195. data/test/sass/templates/_partial.sass +0 -2
  196. data/test/sass/templates/_same_name_different_partiality.scss +0 -1
  197. data/test/sass/templates/alt.sass +0 -16
  198. data/test/sass/templates/basic.sass +0 -23
  199. data/test/sass/templates/bork1.sass +0 -2
  200. data/test/sass/templates/bork2.sass +0 -2
  201. data/test/sass/templates/bork3.sass +0 -2
  202. data/test/sass/templates/bork4.sass +0 -2
  203. data/test/sass/templates/bork5.sass +0 -3
  204. data/test/sass/templates/cached_import_option.scss +0 -3
  205. data/test/sass/templates/compact.sass +0 -17
  206. data/test/sass/templates/complex.sass +0 -305
  207. data/test/sass/templates/compressed.sass +0 -15
  208. data/test/sass/templates/double_import_loop1.sass +0 -1
  209. data/test/sass/templates/expanded.sass +0 -17
  210. data/test/sass/templates/filename_fn.scss +0 -18
  211. data/test/sass/templates/if.sass +0 -11
  212. data/test/sass/templates/import.sass +0 -12
  213. data/test/sass/templates/import_charset.sass +0 -9
  214. data/test/sass/templates/import_charset_1_8.sass +0 -6
  215. data/test/sass/templates/import_charset_ibm866.sass +0 -11
  216. data/test/sass/templates/import_content.sass +0 -4
  217. data/test/sass/templates/importee.less +0 -2
  218. data/test/sass/templates/importee.sass +0 -19
  219. data/test/sass/templates/line_numbers.sass +0 -13
  220. data/test/sass/templates/mixin_bork.sass +0 -5
  221. data/test/sass/templates/mixins.sass +0 -76
  222. data/test/sass/templates/multiline.sass +0 -20
  223. data/test/sass/templates/nested.sass +0 -25
  224. data/test/sass/templates/nested_bork1.sass +0 -2
  225. data/test/sass/templates/nested_bork2.sass +0 -2
  226. data/test/sass/templates/nested_bork3.sass +0 -2
  227. data/test/sass/templates/nested_bork4.sass +0 -2
  228. data/test/sass/templates/nested_import.sass +0 -2
  229. data/test/sass/templates/nested_mixin_bork.sass +0 -6
  230. data/test/sass/templates/options.sass +0 -2
  231. data/test/sass/templates/parent_ref.sass +0 -25
  232. data/test/sass/templates/same_name_different_ext.sass +0 -2
  233. data/test/sass/templates/same_name_different_ext.scss +0 -1
  234. data/test/sass/templates/same_name_different_partiality.scss +0 -1
  235. data/test/sass/templates/script.sass +0 -101
  236. data/test/sass/templates/scss_import.scss +0 -11
  237. data/test/sass/templates/scss_importee.scss +0 -1
  238. data/test/sass/templates/single_import_loop.sass +0 -1
  239. data/test/sass/templates/subdir/import_up1.scss +0 -1
  240. data/test/sass/templates/subdir/import_up2.scss +0 -1
  241. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +0 -2
  242. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +0 -3
  243. data/test/sass/templates/subdir/subdir.sass +0 -6
  244. data/test/sass/templates/units.sass +0 -11
  245. data/test/sass/templates/warn.sass +0 -3
  246. data/test/sass/templates/warn_imported.sass +0 -4
  247. data/test/sass/test_helper.rb +0 -8
  248. data/test/sass/util/multibyte_string_scanner_test.rb +0 -147
  249. data/test/sass/util/subset_map_test.rb +0 -91
  250. data/test/sass/util_test.rb +0 -382
  251. data/test/test_helper.rb +0 -80
  252. /data/{test/sass/results/warn.css → template/files/.gitkeep} +0 -0
data/lib/sass/util.rb DELETED
@@ -1,948 +0,0 @@
1
- require 'erb'
2
- require 'set'
3
- require 'enumerator'
4
- require 'stringio'
5
- require 'rbconfig'
6
- require 'thread'
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
- # The Ruby engine we're running under. Defaults to `"ruby"`
21
- # if the top-level constant is undefined.
22
- # @api public
23
- RUBY_ENGINE = defined?(::RUBY_ENGINE) ? ::RUBY_ENGINE : "ruby"
24
-
25
- # Returns the path of a file relative to the Sass root directory.
26
- #
27
- # @param file [String] The filename relative to the Sass root
28
- # @return [String] The filename relative to the the working directory
29
- def scope(file)
30
- File.join(Sass::ROOT_DIR, file)
31
- end
32
-
33
- # Converts an array of `[key, value]` pairs to a hash.
34
- #
35
- # @example
36
- # to_hash([[:foo, "bar"], [:baz, "bang"]])
37
- # #=> {:foo => "bar", :baz => "bang"}
38
- # @param arr [Array<(Object, Object)>] An array of pairs
39
- # @return [Hash] A hash
40
- def to_hash(arr)
41
- Hash[arr.compact]
42
- end
43
-
44
- # Maps the keys in a hash according to a block.
45
- #
46
- # @example
47
- # map_keys({:foo => "bar", :baz => "bang"}) {|k| k.to_s}
48
- # #=> {"foo" => "bar", "baz" => "bang"}
49
- # @param hash [Hash] The hash to map
50
- # @yield [key] A block in which the keys are transformed
51
- # @yieldparam key [Object] The key that should be mapped
52
- # @yieldreturn [Object] The new value for the key
53
- # @return [Hash] The mapped hash
54
- # @see #map_vals
55
- # @see #map_hash
56
- def map_keys(hash)
57
- to_hash(hash.map {|k, v| [yield(k), v]})
58
- end
59
-
60
- # Maps the values in a hash according to a block.
61
- #
62
- # @example
63
- # map_values({:foo => "bar", :baz => "bang"}) {|v| v.to_sym}
64
- # #=> {:foo => :bar, :baz => :bang}
65
- # @param hash [Hash] The hash to map
66
- # @yield [value] A block in which the values are transformed
67
- # @yieldparam value [Object] The value that should be mapped
68
- # @yieldreturn [Object] The new value for the value
69
- # @return [Hash] The mapped hash
70
- # @see #map_keys
71
- # @see #map_hash
72
- def map_vals(hash)
73
- to_hash(hash.map {|k, v| [k, yield(v)]})
74
- end
75
-
76
- # Maps the key-value pairs of a hash according to a block.
77
- #
78
- # @example
79
- # map_hash({:foo => "bar", :baz => "bang"}) {|k, v| [k.to_s, v.to_sym]}
80
- # #=> {"foo" => :bar, "baz" => :bang}
81
- # @param hash [Hash] The hash to map
82
- # @yield [key, value] A block in which the key-value pairs are transformed
83
- # @yieldparam [key] The hash key
84
- # @yieldparam [value] The hash value
85
- # @yieldreturn [(Object, Object)] The new value for the `[key, value]` pair
86
- # @return [Hash] The mapped hash
87
- # @see #map_keys
88
- # @see #map_vals
89
- def map_hash(hash)
90
- # Using &block here completely hoses performance on 1.8.
91
- to_hash(hash.map {|k, v| yield k, v})
92
- end
93
-
94
- # Computes the powerset of the given array.
95
- # This is the set of all subsets of the array.
96
- #
97
- # @example
98
- # powerset([1, 2, 3]) #=>
99
- # Set[Set[], Set[1], Set[2], Set[3], Set[1, 2], Set[2, 3], Set[1, 3], Set[1, 2, 3]]
100
- # @param arr [Enumerable]
101
- # @return [Set<Set>] The subsets of `arr`
102
- def powerset(arr)
103
- arr.inject([Set.new].to_set) do |powerset, el|
104
- new_powerset = Set.new
105
- powerset.each do |subset|
106
- new_powerset << subset
107
- new_powerset << subset + [el]
108
- end
109
- new_powerset
110
- end
111
- end
112
-
113
- # Restricts a number to falling within a given range.
114
- # Returns the number if it falls within the range,
115
- # or the closest value in the range if it doesn't.
116
- #
117
- # @param value [Numeric]
118
- # @param range [Range<Numeric>]
119
- # @return [Numeric]
120
- def restrict(value, range)
121
- [[value, range.first].max, range.last].min
122
- end
123
-
124
- # Concatenates all strings that are adjacent in an array,
125
- # while leaving other elements as they are.
126
- #
127
- # @example
128
- # merge_adjacent_strings([1, "foo", "bar", 2, "baz"])
129
- # #=> [1, "foobar", 2, "baz"]
130
- # @param arr [Array]
131
- # @return [Array] The enumerable with strings merged
132
- def merge_adjacent_strings(arr)
133
- # Optimize for the common case of one element
134
- return arr if arr.size < 2
135
- arr.inject([]) do |a, e|
136
- if e.is_a?(String)
137
- if a.last.is_a?(String)
138
- a.last << e
139
- else
140
- a << e.dup
141
- end
142
- else
143
- a << e
144
- end
145
- a
146
- end
147
- end
148
-
149
- # Intersperses a value in an enumerable, as would be done with `Array#join`
150
- # but without concatenating the array together afterwards.
151
- #
152
- # @param enum [Enumerable]
153
- # @param val
154
- # @return [Array]
155
- def intersperse(enum, val)
156
- enum.inject([]) {|a, e| a << e << val}[0...-1]
157
- end
158
-
159
- # Substitutes a sub-array of one array with another sub-array.
160
- #
161
- # @param ary [Array] The array in which to make the substitution
162
- # @param from [Array] The sequence of elements to replace with `to`
163
- # @param to [Array] The sequence of elements to replace `from` with
164
- def substitute(ary, from, to)
165
- res = ary.dup
166
- i = 0
167
- while i < res.size
168
- if res[i...i+from.size] == from
169
- res[i...i+from.size] = to
170
- end
171
- i += 1
172
- end
173
- res
174
- end
175
-
176
- # Destructively strips whitespace from the beginning and end
177
- # of the first and last elements, respectively,
178
- # in the array (if those elements are strings).
179
- #
180
- # @param arr [Array]
181
- # @return [Array] `arr`
182
- def strip_string_array(arr)
183
- arr.first.lstrip! if arr.first.is_a?(String)
184
- arr.last.rstrip! if arr.last.is_a?(String)
185
- arr
186
- end
187
-
188
- # Return an array of all possible paths through the given arrays.
189
- #
190
- # @param arrs [Array<Array>]
191
- # @return [Array<Arrays>]
192
- #
193
- # @example
194
- # paths([[1, 2], [3, 4], [5]]) #=>
195
- # # [[1, 3, 5],
196
- # # [2, 3, 5],
197
- # # [1, 4, 5],
198
- # # [2, 4, 5]]
199
- def paths(arrs)
200
- arrs.inject([[]]) do |paths, arr|
201
- flatten(arr.map {|e| paths.map {|path| path + [e]}}, 1)
202
- end
203
- end
204
-
205
- # Computes a single longest common subsequence for `x` and `y`.
206
- # If there are more than one longest common subsequences,
207
- # the one returned is that which starts first in `x`.
208
- #
209
- # @param x [Array]
210
- # @param y [Array]
211
- # @yield [a, b] An optional block to use in place of a check for equality
212
- # between elements of `x` and `y`.
213
- # @yieldreturn [Object, nil] If the two values register as equal,
214
- # this will return the value to use in the LCS array.
215
- # @return [Array] The LCS
216
- def lcs(x, y, &block)
217
- x = [nil, *x]
218
- y = [nil, *y]
219
- block ||= proc {|a, b| a == b && a}
220
- lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
221
- end
222
-
223
- # Converts a Hash to an Array. This is usually identical to `Hash#to_a`,
224
- # with the following exceptions:
225
- #
226
- # * In Ruby 1.8, `Hash#to_a` is not deterministically ordered, but this is.
227
- # * In Ruby 1.9 when running tests, this is ordered in the same way it would
228
- # be under Ruby 1.8 (sorted key order rather than insertion order).
229
- #
230
- # @param hash [Hash]
231
- # @return [Array]
232
- def hash_to_a(hash)
233
- return hash.to_a unless ruby1_8? || defined?(Test::Unit)
234
- return hash.sort_by {|k, v| k}
235
- end
236
-
237
- # Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed
238
- # order. Unlike [#hash_to_a], the resulting order isn't sorted key order;
239
- # instead, it's the same order as `#group_by` has under Ruby 1.9 (key
240
- # appearance order).
241
- #
242
- # @param enum [Enumerable]
243
- # @return [Array<[Object, Array]>] An array of pairs.
244
- def group_by_to_a(enum, &block)
245
- return enum.group_by(&block).to_a unless ruby1_8?
246
- order = {}
247
- arr = []
248
- enum.group_by do |e|
249
- res = block[e]
250
- unless order.include?(res)
251
- order[res] = order.size
252
- end
253
- res
254
- end.each do |key, vals|
255
- arr[order[key]] = [key, vals]
256
- end
257
- arr
258
- end
259
-
260
- # Returns a sub-array of `minuend` containing only elements that are also in
261
- # `subtrahend`. Ensures that the return value has the same order as
262
- # `minuend`, even on Rubinius where that's not guaranteed by {Array#-}.
263
- #
264
- # @param minuend [Array]
265
- # @param subtrahend [Array]
266
- # @return [Array]
267
- def array_minus(minuend, subtrahend)
268
- return minuend - subtrahend unless rbx?
269
- set = Set.new(minuend) - subtrahend
270
- minuend.select {|e| set.include?(e)}
271
- end
272
-
273
- # Returns a string description of the character that caused an
274
- # `Encoding::UndefinedConversionError`.
275
- #
276
- # @param [Encoding::UndefinedConversionError]
277
- # @return [String]
278
- def undefined_conversion_error_char(e)
279
- # Rubinius (as of 2.0.0.rc1) pre-quotes the error character.
280
- return e.error_char if rbx?
281
- # JRuby (as of 1.7.2) doesn't have an error_char field on
282
- # Encoding::UndefinedConversionError.
283
- return e.error_char.dump unless jruby?
284
- e.message[/^"[^"]+"/] #"
285
- end
286
-
287
- # Asserts that `value` falls within `range` (inclusive), leaving
288
- # room for slight floating-point errors.
289
- #
290
- # @param name [String] The name of the value. Used in the error message.
291
- # @param range [Range] The allowed range of values.
292
- # @param value [Numeric, Sass::Script::Number] The value to check.
293
- # @param unit [String] The unit of the value. Used in error reporting.
294
- # @return [Numeric] `value` adjusted to fall within range, if it
295
- # was outside by a floating-point margin.
296
- def check_range(name, range, value, unit='')
297
- grace = (-0.00001..0.00001)
298
- str = value.to_s
299
- value = value.value if value.is_a?(Sass::Script::Number)
300
- return value if range.include?(value)
301
- return range.first if grace.include?(value - range.first)
302
- return range.last if grace.include?(value - range.last)
303
- raise ArgumentError.new(
304
- "#{name} #{str} must be between #{range.first}#{unit} and #{range.last}#{unit}")
305
- end
306
-
307
- # Returns whether or not `seq1` is a subsequence of `seq2`. That is, whether
308
- # or not `seq2` contains every element in `seq1` in the same order (and
309
- # possibly more elements besides).
310
- #
311
- # @param seq1 [Array]
312
- # @param seq2 [Array]
313
- # @return [Boolean]
314
- def subsequence?(seq1, seq2)
315
- i = j = 0
316
- loop do
317
- return true if i == seq1.size
318
- return false if j == seq2.size
319
- i += 1 if seq1[i] == seq2[j]
320
- j += 1
321
- end
322
- end
323
-
324
- # Returns information about the caller of the previous method.
325
- #
326
- # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
327
- # @return [[String, Fixnum, (String, nil)]] An array containing the filename, line, and method name of the caller.
328
- # The method name may be nil
329
- def caller_info(entry = nil)
330
- # JRuby evaluates `caller` incorrectly when it's in an actual default argument.
331
- entry ||= caller[1]
332
- info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
333
- info[1] = info[1].to_i
334
- # This is added by Rubinius to designate a block, but we don't care about it.
335
- info[2].sub!(/ \{\}\Z/, '') if info[2]
336
- info
337
- end
338
-
339
- # Returns whether one version string represents a more recent version than another.
340
- #
341
- # @param v1 [String] A version string.
342
- # @param v2 [String] Another version string.
343
- # @return [Boolean]
344
- def version_gt(v1, v2)
345
- # Construct an array to make sure the shorter version is padded with nil
346
- Array.new([v1.length, v2.length].max).zip(v1.split("."), v2.split(".")) do |_, p1, p2|
347
- p1 ||= "0"
348
- p2 ||= "0"
349
- release1 = p1 =~ /^[0-9]+$/
350
- release2 = p2 =~ /^[0-9]+$/
351
- if release1 && release2
352
- # Integer comparison if both are full releases
353
- p1, p2 = p1.to_i, p2.to_i
354
- next if p1 == p2
355
- return p1 > p2
356
- elsif !release1 && !release2
357
- # String comparison if both are prereleases
358
- next if p1 == p2
359
- return p1 > p2
360
- else
361
- # If only one is a release, that one is newer
362
- return release1
363
- end
364
- end
365
- end
366
-
367
- # Returns whether one version string represents the same or a more
368
- # recent version than another.
369
- #
370
- # @param v1 [String] A version string.
371
- # @param v2 [String] Another version string.
372
- # @return [Boolean]
373
- def version_geq(v1, v2)
374
- version_gt(v1, v2) || !version_gt(v2, v1)
375
- end
376
-
377
- # Throws a NotImplementedError for an abstract method.
378
- #
379
- # @param obj [Object] `self`
380
- # @raise [NotImplementedError]
381
- def abstract(obj)
382
- raise NotImplementedError.new("#{obj.class} must implement ##{caller_info[2]}")
383
- end
384
-
385
- # Silence all output to STDERR within a block.
386
- #
387
- # @yield A block in which no output will be printed to STDERR
388
- def silence_warnings
389
- the_real_stderr, $stderr = $stderr, StringIO.new
390
- yield
391
- ensure
392
- $stderr = the_real_stderr
393
- end
394
-
395
- @@silence_warnings = false
396
- # Silences all Sass warnings within a block.
397
- #
398
- # @yield A block in which no Sass warnings will be printed
399
- def silence_sass_warnings
400
- old_level, Sass.logger.log_level = Sass.logger.log_level, :error
401
- yield
402
- ensure
403
- Sass.logger.log_level = old_level
404
- end
405
-
406
- # The same as `Kernel#warn`, but is silenced by \{#silence\_sass\_warnings}.
407
- #
408
- # @param msg [String]
409
- def sass_warn(msg)
410
- msg = msg + "\n" unless ruby1?
411
- Sass.logger.warn(msg)
412
- end
413
-
414
- ## Cross Rails Version Compatibility
415
-
416
- # Returns the root of the Rails application,
417
- # if this is running in a Rails context.
418
- # Returns `nil` if no such root is defined.
419
- #
420
- # @return [String, nil]
421
- def rails_root
422
- if defined?(::Rails.root)
423
- return ::Rails.root.to_s if ::Rails.root
424
- raise "ERROR: Rails.root is nil!"
425
- end
426
- return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
427
- return nil
428
- end
429
-
430
- # Returns the environment of the Rails application,
431
- # if this is running in a Rails context.
432
- # Returns `nil` if no such environment is defined.
433
- #
434
- # @return [String, nil]
435
- def rails_env
436
- return ::Rails.env.to_s if defined?(::Rails.env)
437
- return RAILS_ENV.to_s if defined?(RAILS_ENV)
438
- return nil
439
- end
440
-
441
- # Returns whether this environment is using ActionPack
442
- # version 3.0.0 or greater.
443
- #
444
- # @return [Boolean]
445
- def ap_geq_3?
446
- ap_geq?("3.0.0.beta1")
447
- end
448
-
449
- # Returns whether this environment is using ActionPack
450
- # of a version greater than or equal to that specified.
451
- #
452
- # @param version [String] The string version number to check against.
453
- # Should be greater than or equal to Rails 3,
454
- # because otherwise ActionPack::VERSION isn't autoloaded
455
- # @return [Boolean]
456
- def ap_geq?(version)
457
- # The ActionPack module is always loaded automatically in Rails >= 3
458
- return false unless defined?(ActionPack) && defined?(ActionPack::VERSION) &&
459
- defined?(ActionPack::VERSION::STRING)
460
-
461
- version_geq(ActionPack::VERSION::STRING, version)
462
- end
463
-
464
- # Returns an ActionView::Template* class.
465
- # In pre-3.0 versions of Rails, most of these classes
466
- # were of the form `ActionView::TemplateFoo`,
467
- # while afterwards they were of the form `ActionView;:Template::Foo`.
468
- #
469
- # @param name [#to_s] The name of the class to get.
470
- # For example, `:Error` will return `ActionView::TemplateError`
471
- # or `ActionView::Template::Error`.
472
- def av_template_class(name)
473
- return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
474
- return ActionView::Template.const_get(name.to_s)
475
- end
476
-
477
- ## Cross-OS Compatibility
478
-
479
- # Whether or not this is running on Windows.
480
- #
481
- # @return [Boolean]
482
- def windows?
483
- RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i
484
- end
485
-
486
- # Whether or not this is running on IronRuby.
487
- #
488
- # @return [Boolean]
489
- def ironruby?
490
- RUBY_ENGINE == "ironruby"
491
- end
492
-
493
- # Whether or not this is running on Rubinius.
494
- #
495
- # @return [Boolean]
496
- def rbx?
497
- RUBY_ENGINE == "rbx"
498
- end
499
-
500
- # Whether or not this is running on JRuby.
501
- #
502
- # @return [Boolean]
503
- def jruby?
504
- RUBY_PLATFORM =~ /java/
505
- end
506
-
507
- # Returns an array of ints representing the JRuby version number.
508
- #
509
- # @return [Array<Fixnum>]
510
- def jruby_version
511
- $jruby_version ||= ::JRUBY_VERSION.split(".").map {|s| s.to_i}
512
- end
513
-
514
- # Like `Dir.glob`, but works with backslash-separated paths on Windows.
515
- #
516
- # @param path [String]
517
- def glob(path, &block)
518
- path = path.gsub('\\', '/') if windows?
519
- Dir.glob(path, &block)
520
- end
521
-
522
- # Prepare a value for a destructuring assignment (e.g. `a, b =
523
- # val`). This works around a performance bug when using
524
- # ActiveSupport, and only needs to be called when `val` is likely
525
- # to be `nil` reasonably often.
526
- #
527
- # See [this bug report](http://redmine.ruby-lang.org/issues/4917).
528
- #
529
- # @param val [Object]
530
- # @return [Object]
531
- def destructure(val)
532
- val || []
533
- end
534
-
535
- ## Cross-Ruby-Version Compatibility
536
-
537
- # Whether or not this is running under a Ruby version under 2.0.
538
- #
539
- # @return [Boolean]
540
- def ruby1?
541
- Sass::Util::RUBY_VERSION[0] <= 1
542
- end
543
-
544
- # Whether or not this is running under Ruby 1.8 or lower.
545
- #
546
- # Note that IronRuby counts as Ruby 1.8,
547
- # because it doesn't support the Ruby 1.9 encoding API.
548
- #
549
- # @return [Boolean]
550
- def ruby1_8?
551
- # IronRuby says its version is 1.9, but doesn't support any of the encoding APIs.
552
- # We have to fall back to 1.8 behavior.
553
- ironruby? || (Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9)
554
- end
555
-
556
- # Whether or not this is running under Ruby 1.8.6 or lower.
557
- # Note that lower versions are not officially supported.
558
- #
559
- # @return [Boolean]
560
- def ruby1_8_6?
561
- ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
562
- end
563
-
564
- # Wehter or not this is running under JRuby 1.6 or lower.
565
- def jruby1_6?
566
- jruby? && jruby_version[0] == 1 && jruby_version[1] < 7
567
- end
568
-
569
- # Whether or not this is running under MacRuby.
570
- #
571
- # @return [Boolean]
572
- def macruby?
573
- RUBY_ENGINE == 'macruby'
574
- end
575
-
576
- # Checks that the encoding of a string is valid in Ruby 1.9
577
- # and cleans up potential encoding gotchas like the UTF-8 BOM.
578
- # If it's not, yields an error string describing the invalid character
579
- # and the line on which it occurrs.
580
- #
581
- # @param str [String] The string of which to check the encoding
582
- # @yield [msg] A block in which an encoding error can be raised.
583
- # Only yields if there is an encoding error
584
- # @yieldparam msg [String] The error message to be raised
585
- # @return [String] `str`, potentially with encoding gotchas like BOMs removed
586
- def check_encoding(str)
587
- if ruby1_8?
588
- return str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM
589
- elsif str.valid_encoding?
590
- # Get rid of the Unicode BOM if possible
591
- if str.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?$/
592
- return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '')
593
- else
594
- return str
595
- end
596
- end
597
-
598
- encoding = str.encoding
599
- newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding("binary"))
600
- str.force_encoding("binary").split(newlines).each_with_index do |line, i|
601
- begin
602
- line.encode(encoding)
603
- rescue Encoding::UndefinedConversionError => e
604
- yield <<MSG.rstrip, i + 1
605
- Invalid #{encoding.name} character #{undefined_conversion_error_char(e)}
606
- MSG
607
- end
608
- end
609
- return str
610
- end
611
-
612
- # Like {\#check\_encoding}, but also checks for a `@charset` declaration
613
- # at the beginning of the file and uses that encoding if it exists.
614
- #
615
- # The Sass encoding rules are simple.
616
- # If a `@charset` declaration exists,
617
- # we assume that that's the original encoding of the document.
618
- # Otherwise, we use whatever encoding Ruby has.
619
- # Then we convert that to UTF-8 to process internally.
620
- # The UTF-8 end result is what's returned by this method.
621
- #
622
- # @param str [String] The string of which to check the encoding
623
- # @yield [msg] A block in which an encoding error can be raised.
624
- # Only yields if there is an encoding error
625
- # @yieldparam msg [String] The error message to be raised
626
- # @return [(String, Encoding)] The original string encoded as UTF-8,
627
- # and the source encoding of the string (or `nil` under Ruby 1.8)
628
- # @raise [Encoding::UndefinedConversionError] if the source encoding
629
- # cannot be converted to UTF-8
630
- # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
631
- def check_sass_encoding(str, &block)
632
- return check_encoding(str, &block), nil if ruby1_8?
633
- # We allow any printable ASCII characters but double quotes in the charset decl
634
- bin = str.dup.force_encoding("BINARY")
635
- encoding = Sass::Util::ENCODINGS_TO_CHECK.find do |enc|
636
- re = Sass::Util::CHARSET_REGEXPS[enc]
637
- re && bin =~ re
638
- end
639
- charset, bom = $1, $2
640
- if charset
641
- charset = charset.force_encoding(encoding).encode("UTF-8")
642
- if endianness = encoding[/[BL]E$/]
643
- begin
644
- Encoding.find(charset + endianness)
645
- charset << endianness
646
- rescue ArgumentError # Encoding charset + endianness doesn't exist
647
- end
648
- end
649
- str.force_encoding(charset)
650
- elsif bom
651
- str.force_encoding(encoding)
652
- end
653
-
654
- str = check_encoding(str, &block)
655
- return str.encode("UTF-8"), str.encoding
656
- end
657
-
658
- unless ruby1_8?
659
- # @private
660
- def _enc(string, encoding)
661
- string.encode(encoding).force_encoding("BINARY")
662
- end
663
-
664
- # We could automatically add in any non-ASCII-compatible encodings here,
665
- # but there's not really a good way to do that
666
- # without manually checking that each encoding
667
- # encodes all ASCII characters properly,
668
- # which takes long enough to affect the startup time of the CLI.
669
- ENCODINGS_TO_CHECK = %w[UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE]
670
-
671
- CHARSET_REGEXPS = Hash.new do |h, e|
672
- h[e] =
673
- begin
674
- # /\A(?:\uFEFF)?@charset "(.*?)"|\A(\uFEFF)/
675
- Regexp.new(/\A(?:#{_enc("\uFEFF", e)})?#{
676
- _enc('@charset "', e)}(.*?)#{_enc('"', e)}|\A(#{
677
- _enc("\uFEFF", e)})/)
678
- rescue Encoding::ConverterNotFoundError => _
679
- nil # JRuby on Java 5 doesn't support UTF-32
680
- rescue
681
- # /\A@charset "(.*?)"/
682
- Regexp.new(/\A#{_enc('@charset "', e)}(.*?)#{_enc('"', e)}/)
683
- end
684
- end
685
- end
686
-
687
- # Checks to see if a class has a given method.
688
- # For example:
689
- #
690
- # Sass::Util.has?(:public_instance_method, String, :gsub) #=> true
691
- #
692
- # Method collections like `Class#instance_methods`
693
- # return strings in Ruby 1.8 and symbols in Ruby 1.9 and on,
694
- # so this handles checking for them in a compatible way.
695
- #
696
- # @param attr [#to_s] The (singular) name of the method-collection method
697
- # (e.g. `:instance_methods`, `:private_methods`)
698
- # @param klass [Module] The class to check the methods of which to check
699
- # @param method [String, Symbol] The name of the method do check for
700
- # @return [Boolean] Whether or not the given collection has the given method
701
- def has?(attr, klass, method)
702
- klass.send("#{attr}s").include?(ruby1_8? ? method.to_s : method.to_sym)
703
- end
704
-
705
- # A version of `Enumerable#enum_with_index` that works in Ruby 1.8 and 1.9.
706
- #
707
- # @param enum [Enumerable] The enumerable to get the enumerator for
708
- # @return [Enumerator] The with-index enumerator
709
- def enum_with_index(enum)
710
- ruby1_8? ? enum.enum_with_index : enum.each_with_index
711
- end
712
-
713
- # A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.
714
- #
715
- # @param enum [Enumerable] The enumerable to get the enumerator for
716
- # @param n [Fixnum] The size of each cons
717
- # @return [Enumerator] The consed enumerator
718
- def enum_cons(enum, n)
719
- ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
720
- end
721
-
722
- # A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.
723
- #
724
- # @param enum [Enumerable] The enumerable to get the enumerator for
725
- # @param n [Fixnum] The size of each slice
726
- # @return [Enumerator] The consed enumerator
727
- def enum_slice(enum, n)
728
- ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
729
- end
730
-
731
- # Destructively removes all elements from an array that match a block, and
732
- # returns the removed elements.
733
- #
734
- # @param array [Array] The array from which to remove elements.
735
- # @yield [el] Called for each element.
736
- # @yieldparam el [*] The element to test.
737
- # @yieldreturn [Boolean] Whether or not to extract the element.
738
- # @return [Array] The extracted elements.
739
- def extract!(array)
740
- out = []
741
- array.reject! do |e|
742
- next false unless yield e
743
- out << e
744
- true
745
- end
746
- out
747
- end
748
-
749
- # Returns the ASCII code of the given character.
750
- #
751
- # @param c [String] All characters but the first are ignored.
752
- # @return [Fixnum] The ASCII code of `c`.
753
- def ord(c)
754
- ruby1_8? ? c[0] : c.ord
755
- end
756
-
757
- # Flattens the first `n` nested arrays in a cross-version manner.
758
- #
759
- # @param arr [Array] The array to flatten
760
- # @param n [Fixnum] The number of levels to flatten
761
- # @return [Array] The flattened array
762
- def flatten(arr, n)
763
- return arr.flatten(n) unless ruby1_8_6?
764
- return arr if n == 0
765
- arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
766
- end
767
-
768
- # Returns the hash code for a set in a cross-version manner.
769
- # Aggravatingly, this is order-dependent in Ruby 1.8.6.
770
- #
771
- # @param set [Set]
772
- # @return [Fixnum] The order-independent hashcode of `set`
773
- def set_hash(set)
774
- return set.hash unless ruby1_8_6?
775
- set.map {|e| e.hash}.uniq.sort.hash
776
- end
777
-
778
- # Tests the hash-equality of two sets in a cross-version manner.
779
- # Aggravatingly, this is order-dependent in Ruby 1.8.6.
780
- #
781
- # @param set1 [Set]
782
- # @param set2 [Set]
783
- # @return [Boolean] Whether or not the sets are hashcode equal
784
- def set_eql?(set1, set2)
785
- return set1.eql?(set2) unless ruby1_8_6?
786
- set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
787
- end
788
-
789
- # Like `Object#inspect`, but preserves non-ASCII characters rather than escaping them under Ruby 1.9.2.
790
- # This is necessary so that the precompiled Haml template can be `#encode`d into `@options[:encoding]`
791
- # before being evaluated.
792
- #
793
- # @param obj {Object}
794
- # @return {String}
795
- def inspect_obj(obj)
796
- return obj.inspect unless version_geq(::RUBY_VERSION, "1.9.2")
797
- return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
798
- return obj.inspect unless obj.is_a?(String)
799
- '"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
800
- end
801
-
802
- # Extracts the non-string vlaues from an array containing both strings and non-strings.
803
- # These values are replaced with escape sequences.
804
- # This can be undone using \{#inject\_values}.
805
- #
806
- # This is useful e.g. when we want to do string manipulation
807
- # on an interpolated string.
808
- #
809
- # The precise format of the resulting string is not guaranteed.
810
- # However, it is guaranteed that newlines and whitespace won't be affected.
811
- #
812
- # @param arr [Array] The array from which values are extracted.
813
- # @return [(String, Array)] The resulting string, and an array of extracted values.
814
- def extract_values(arr)
815
- values = []
816
- return arr.map do |e|
817
- next e.gsub('{', '{{') if e.is_a?(String)
818
- values << e
819
- next "{#{values.count - 1}}"
820
- end.join, values
821
- end
822
-
823
- # Undoes \{#extract\_values} by transforming a string with escape sequences
824
- # into an array of strings and non-string values.
825
- #
826
- # @param str [String] The string with escape sequences.
827
- # @param values [Array] The array of values to inject.
828
- # @return [Array] The array of strings and values.
829
- def inject_values(str, values)
830
- return [str.gsub('{{', '{')] if values.empty?
831
- # Add an extra { so that we process the tail end of the string
832
- result = (str + '{{').scan(/(.*?)(?:(\{\{)|\{(\d+)\})/m).map do |(pre, esc, n)|
833
- [pre, esc ? '{' : '', n ? values[n.to_i] : '']
834
- end.flatten(1)
835
- result[-2] = '' # Get rid of the extra {
836
- merge_adjacent_strings(result).reject {|s| s == ''}
837
- end
838
-
839
- # Allows modifications to be performed on the string form
840
- # of an array containing both strings and non-strings.
841
- #
842
- # @param arr [Array] The array from which values are extracted.
843
- # @yield [str] A block in which string manipulation can be done to the array.
844
- # @yieldparam str [String] The string form of `arr`.
845
- # @yieldreturn [String] The modified string.
846
- # @return [Array] The modified, interpolated array.
847
- def with_extracted_values(arr)
848
- str, vals = extract_values(arr)
849
- str = yield str
850
- inject_values(str, vals)
851
- end
852
-
853
- ## Static Method Stuff
854
-
855
- # The context in which the ERB for \{#def\_static\_method} will be run.
856
- class StaticConditionalContext
857
- # @param set [#include?] The set of variables that are defined for this context.
858
- def initialize(set)
859
- @set = set
860
- end
861
-
862
- # Checks whether or not a variable is defined for this context.
863
- #
864
- # @param name [Symbol] The name of the variable
865
- # @return [Boolean]
866
- def method_missing(name, *args, &block)
867
- super unless args.empty? && block.nil?
868
- @set.include?(name)
869
- end
870
- end
871
-
872
- # @private
873
- ATOMIC_WRITE_MUTEX = Mutex.new
874
-
875
-
876
- # This creates a temp file and yields it for writing. When the
877
- # write is complete, the file is moved into the desired location.
878
- # The atomicity of this operation is provided by the filesystem's
879
- # rename operation.
880
- #
881
- # @param filename [String] The file to write to.
882
- # @param perms [Integer] The permissions used for creating this file.
883
- # Will be masked by the process umask. Defaults to readable/writeable
884
- # by all users however the umask usually changes this to only be writable
885
- # by the process's user.
886
- # @yieldparam tmpfile [Tempfile] The temp file that can be written to.
887
- # @return The value returned by the block.
888
- def atomic_create_and_write_file(filename, perms = 0666)
889
- require 'tempfile'
890
- tmpfile = Tempfile.new(File.basename(filename), File.dirname(filename))
891
- tmpfile.binmode if tmpfile.respond_to?(:binmode)
892
- result = yield tmpfile
893
- tmpfile.flush # ensure all writes are flushed to the OS
894
- begin
895
- tmpfile.fsync # ensure all buffered data in the OS is sync'd to disk.
896
- rescue NotImplementedError
897
- # Not all OSes support fsync
898
- end
899
- tmpfile.close # Windows cannot rename an open file.
900
- # Make file readable and writeable to all but respect umask (usually 022).
901
- File.chmod(perms & ~File.umask, tmpfile.path)
902
- ATOMIC_WRITE_MUTEX.synchronize do
903
- File.rename tmpfile.path, filename
904
- end
905
- result
906
- ensure
907
- # close and remove the tempfile if it still exists,
908
- # presumably due to an error during write
909
- tmpfile.close if tmpfile
910
- tmpfile.unlink if tmpfile
911
- end
912
-
913
- private
914
-
915
- # Calculates the memoization table for the Least Common Subsequence algorithm.
916
- # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
917
- def lcs_table(x, y)
918
- c = Array.new(x.size) {[]}
919
- x.size.times {|i| c[i][0] = 0}
920
- y.size.times {|j| c[0][j] = 0}
921
- (1...x.size).each do |i|
922
- (1...y.size).each do |j|
923
- c[i][j] =
924
- if yield x[i], y[j]
925
- c[i-1][j-1] + 1
926
- else
927
- [c[i][j-1], c[i-1][j]].max
928
- end
929
- end
930
- end
931
- return c
932
- end
933
-
934
- # Computes a single longest common subsequence for arrays x and y.
935
- # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
936
- def lcs_backtrace(c, x, y, i, j, &block)
937
- return [] if i == 0 || j == 0
938
- if v = yield(x[i], y[j])
939
- return lcs_backtrace(c, x, y, i-1, j-1, &block) << v
940
- end
941
-
942
- return lcs_backtrace(c, x, y, i, j-1, &block) if c[i][j-1] > c[i-1][j]
943
- return lcs_backtrace(c, x, y, i-1, j, &block)
944
- end
945
- end
946
- end
947
-
948
- require 'sass/util/multibyte_string_scanner'