sass 3.1.0.alpha.214 → 3.1.0.alpha.216

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/REVISION CHANGED
@@ -1 +1 @@
1
- e18890b4746317cad3919e6461554eb0415b12fc
1
+ d4f5f05b36ae3e07d9c417174bf655b8f62969d9
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.0.alpha.214
1
+ 3.1.0.alpha.216
@@ -12,3 +12,4 @@ end
12
12
  require 'sass/cache_stores/base'
13
13
  require 'sass/cache_stores/filesystem'
14
14
  require 'sass/cache_stores/memory'
15
+ require 'sass/cache_stores/chain'
@@ -48,7 +48,7 @@ module Sass
48
48
  # @param sha [String] The checksum for the contents that are being stored.
49
49
  # @param obj [Object] The object to cache.
50
50
  def store(key, sha, root)
51
- _store(key, Sass::VERSION, sha, Sass::Util.dump(root))
51
+ _store(key, Sass::VERSION, sha, Marshal.dump(root))
52
52
  end
53
53
 
54
54
  # Retrieve a {Sass::Tree::RootNode}.
@@ -58,7 +58,7 @@ module Sass
58
58
  # @return [Object] The cached object.
59
59
  def retrieve(key, sha)
60
60
  contents = _retrieve(key, Sass::VERSION, sha)
61
- Sass::Util.load(contents) if contents
61
+ Marshal.load(contents) if contents
62
62
  rescue EOFError, TypeError, ArgumentError => e
63
63
  Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
64
64
  end
@@ -0,0 +1,33 @@
1
+ module Sass
2
+ module CacheStores
3
+ # A meta-cache that chains multiple caches together.
4
+ # Specifically:
5
+ #
6
+ # * All `#store`s are passed to all caches.
7
+ # * `#retrieve`s are passed to each cache until one has a hit.
8
+ # * When one cache has a hit, the value is `#store`d in all earlier caches.
9
+ class Chain < Base
10
+ # Create a new cache chaining the given caches.
11
+ #
12
+ # @param caches [Array<Sass::CacheStores::Base>] The caches to chain.
13
+ def initialize(*caches)
14
+ @caches = caches
15
+ end
16
+
17
+ # @see Base#store
18
+ def store(key, sha, obj)
19
+ @caches.each {|c| c.store(key, sha, obj)}
20
+ end
21
+
22
+ # @see Base#retrieve
23
+ def retrieve(key, sha)
24
+ @caches.each_with_index do |c, i|
25
+ next unless obj = c.retrieve(key, sha)
26
+ @caches[0...i].each {|c| c.store(key, sha, obj)}
27
+ return obj
28
+ end
29
+ nil
30
+ end
31
+ end
32
+ end
33
+ end
@@ -24,22 +24,18 @@ module Sass
24
24
  @contents = {}
25
25
  end
26
26
 
27
- # @see Base#_retrieve
28
- def _retrieve(key, version, sha)
27
+ # @see Base#retrieve
28
+ def retrieve(key, sha)
29
29
  if @contents.has_key?(key)
30
- return unless @contents[key][:version] == version
31
30
  return unless @contents[key][:sha] == sha
32
- return @contents[key][:contents]
31
+ obj = @contents[key][:obj]
32
+ obj.respond_to?(:deep_copy) ? obj.deep_copy : obj.dup
33
33
  end
34
34
  end
35
-
36
- # @see Base#_store
37
- def _store(key, version, sha, contents)
38
- @contents[key] = {
39
- :version => version,
40
- :sha => sha,
41
- :contents => contents
42
- }
35
+
36
+ # @see Base#store
37
+ def store(key, sha, obj)
38
+ @contents[key] = {:sha => sha, :obj => obj}
43
39
  end
44
40
 
45
41
  # Destructively clear the cache.
data/lib/sass/engine.rb CHANGED
@@ -164,7 +164,8 @@ module Sass
164
164
  # Tracks the original filename of the top-level Sass file
165
165
  options[:original_filename] = options[:original_filename] || options[:filename]
166
166
 
167
- options[:cache_store] ||= Sass::CacheStores::Filesystem.new(options[:cache_location])
167
+ options[:cache_store] ||= Sass::CacheStores::Chain.new(
168
+ Sass::CacheStores::Memory.new, Sass::CacheStores::Filesystem.new(options[:cache_location]))
168
169
  # Support both, because the docs said one and the other actually worked
169
170
  # for quite a long time.
170
171
  options[:line_comments] ||= options[:line_numbers]
@@ -333,7 +334,15 @@ module Sass
333
334
  end
334
335
 
335
336
  root.options = @options
336
- @options[:cache_store].store(key, sha, root) if @options[:cache] && key && sha
337
+ if @options[:cache] && key && sha
338
+ begin
339
+ old_options = root.options
340
+ root.options = {:importer => root.options[:importer]}
341
+ @options[:cache_store].store(key, sha, root)
342
+ ensure
343
+ root.options = old_options
344
+ end
345
+ end
337
346
  root
338
347
  rescue SyntaxError => e
339
348
  e.modify_backtrace(:filename => @options[:filename], :line => @line)
@@ -75,15 +75,15 @@ module Sass
75
75
  # @raise [Sass::SyntaxError] if the function call raises an ArgumentError
76
76
  def _perform(environment)
77
77
  args = @args.map {|a| a.perform(environment)}
78
- keywords = Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
79
78
  if fn = environment.function(@name)
79
+ keywords = Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
80
80
  return perform_sass_fn(fn, args, keywords)
81
81
  end
82
82
 
83
83
  ruby_name = @name.tr('-', '_')
84
- args = construct_ruby_args(ruby_name, args, keywords)
84
+ args = construct_keyword_args(ruby_name, args, environment) unless @keywords.empty?
85
85
 
86
- unless Sass::Util.has?(:public_instance_method, Functions, ruby_name) && ruby_name !~ /^__/
86
+ unless Functions.callable?(ruby_name)
87
87
  opts(to_literal(args))
88
88
  else
89
89
  opts(Functions::EvaluationContext.new(environment.options).send(ruby_name, *args))
@@ -102,7 +102,9 @@ module Sass
102
102
 
103
103
  private
104
104
 
105
- def construct_ruby_args(name, args, keywords)
105
+ def construct_keyword_args(name, args, environment)
106
+ keywords = Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
107
+
106
108
  unless signature = Functions.signature(name.to_sym, args.size, keywords.size)
107
109
  return args if keywords.empty?
108
110
  raise Sass::SyntaxError.new("Function #{name} doesn't support keyword arguments")
@@ -93,12 +93,12 @@ module Sass::Script
93
93
  # \{#adjust adjust-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\]}
94
94
  # : Increase or decrease any of the components of a color.
95
95
  #
96
+ # \{#scale_color scale-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\]}
97
+ # : Fluidly scale one or more components of a color.
98
+ #
96
99
  # \{#change_color change-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\]}
97
100
  # : Changes one or more properties of a color.
98
101
  #
99
- # \{#scale_color scale-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\]}
100
- # : Scales one or more properties of a color by a percentage value.
101
- #
102
102
  # ## String Functions
103
103
  #
104
104
  # \{#unquote unquote($string)}
@@ -295,6 +295,8 @@ module Sass::Script
295
295
  # That means that all instance methods of {EvaluationContext}
296
296
  # are available to use in functions.
297
297
  class EvaluationContext
298
+ include Functions
299
+
298
300
  # The options hash for the {Sass::Engine} that is processing the function call
299
301
  #
300
302
  # @return [{Symbol => Object}]
@@ -303,10 +305,6 @@ module Sass::Script
303
305
  # @param options [{Symbol => Object}] See \{#options}
304
306
  def initialize(options)
305
307
  @options = options
306
-
307
- # We need to include this individually in each instance
308
- # because of an icky Ruby restriction
309
- class << self; include Sass::Script::Functions; end
310
308
  end
311
309
 
312
310
  # Asserts that the type of a given SassScript value
@@ -330,8 +328,32 @@ module Sass::Script
330
328
  end
331
329
  end
332
330
 
333
- instance_methods.each { |m| undef_method m unless m.to_s =~ /^__/ }
331
+ class << self
332
+ FUNCTIONS = Set.new
334
333
 
334
+ # Returns whether user function with a given name exists.
335
+ #
336
+ # @param function_name [String]
337
+ # @return [Boolean]
338
+ def callable?(function_name)
339
+ FUNCTIONS.include?(function_name)
340
+ end
341
+
342
+ private
343
+ def include(*args)
344
+ r = super
345
+ update_callable_functions
346
+ # We have to re-include ourselves into EvaluationContext to work around
347
+ # an icky Ruby restriction.
348
+ EvaluationContext.send :include, self
349
+ r
350
+ end
351
+
352
+ def update_callable_functions
353
+ FUNCTIONS.clear
354
+ public_instance_methods.each {|function_name| FUNCTIONS << function_name.to_s}
355
+ end
356
+ end
335
357
 
336
358
  # Creates a {Color} object from red, green, and blue values.
337
359
  #
@@ -1325,5 +1347,15 @@ module Sass::Script
1325
1347
  color.with(attr => Sass::Util.restrict(
1326
1348
  color.send(attr).send(op, amount.value), range))
1327
1349
  end
1350
+
1351
+ class << self
1352
+ private
1353
+ def method_added(name)
1354
+ update_callable_functions
1355
+ super
1356
+ end
1357
+ end
1358
+
1359
+ update_callable_functions # generate the initial set
1328
1360
  end
1329
1361
  end
@@ -103,9 +103,7 @@ module Sass
103
103
  # @see Simple#to_a
104
104
  def to_a
105
105
  ary = @members.map {|seq_or_op| seq_or_op.is_a?(SimpleSequence) ? seq_or_op.to_a : seq_or_op}
106
- ary = Sass::Util.intersperse(ary, " ")
107
- ary = Sass::Util.substitute(ary, [" ", "\n", " "], ["\n"])
108
- ary.flatten.compact
106
+ Sass::Util.intersperse(ary, " ").flatten.compact
109
107
  end
110
108
 
111
109
  # Returns a string representation of the sequence.
@@ -41,23 +41,22 @@ module Sass::Tree
41
41
  self.else.options = options if self.else
42
42
  end
43
43
 
44
- # @see Node#_around_dump
45
- def _around_dump
46
- old_else = @else
47
- old_last_else = @last_else
48
- @else = Sass::Util.dump(@else)
49
- @last_else = (self == @last_else ? nil : Sass::Util.dump(@last_else))
50
- super
51
- ensure
52
- @else = old_else
53
- @last_else = old_last_else
44
+ def _dump(f)
45
+ Marshal.dump([self.expr, self.else])
54
46
  end
55
47
 
56
- # @see Node#_after_load
57
- def _after_load
58
- super
59
- @else = Sass::Util.load(@else)
60
- @last_else = (@last_else ? Sass::Util.load(@last_else) : self)
48
+ def self._load(data)
49
+ expr, else_ = Marshal.load(data)
50
+ node = IfNode.new(expr)
51
+ node.else = else_
52
+ node
53
+ end
54
+
55
+ # @see Node#deep_copy
56
+ def deep_copy
57
+ node = super
58
+ node.else = self.else.deep_copy if self.else
59
+ node
61
60
  end
62
61
  end
63
62
  end
@@ -180,29 +180,14 @@ module Sass
180
180
  Sass::Tree::Visitors::Convert.visit(self, options, :scss)
181
181
  end
182
182
 
183
- # Names of options that are saved when the node is serialized and cached.
184
- SAVED_OPTIONS = [:importer]
185
-
186
- # Ensures that only {SAVED_OPTIONS} get saved.
187
- def _around_dump
188
- old_options = @options
189
- old_children = @children
190
- @options = {}
191
- SAVED_OPTIONS.each do |opt|
192
- @options[opt] = old_options[opt]
193
- end
194
- @options = Sass::Util.dump(@options)
195
- @children = Sass::Util.dump(@children)
196
- yield
197
- ensure
198
- @options = old_options
199
- @children = old_children
200
- end
201
-
202
- # Ensures that only {SAVED_OPTIONS} get saved.
203
- def _after_load
204
- @options = Sass::Util.load(@options)
205
- @children = Sass::Util.load(@children)
183
+ # Return a deep clone of this node.
184
+ # The child nodes are cloned, but options are not.
185
+ #
186
+ # @return [Node]
187
+ def deep_copy
188
+ node = dup
189
+ node.children = children.map {|c| c.deep_copy}
190
+ node
206
191
  end
207
192
 
208
193
  protected
@@ -56,9 +56,22 @@ module Sass::Tree
56
56
  merged = Sass::Util.merge_adjacent_strings(rule)
57
57
  @rule = Sass::Util.strip_string_array(merged)
58
58
  @tabs = 0
59
+ try_to_parse_non_interpolated_rules
59
60
  super()
60
61
  end
61
62
 
63
+ # If we've precached the parsed selector, set the line on it, too.
64
+ def line=(line)
65
+ @parsed_rules.line = line if @parsed_rules
66
+ super
67
+ end
68
+
69
+ # If we've precached the parsed selector, set the filename on it, too.
70
+ def filename=(filename)
71
+ @parsed_rules.filename = filename if @parsed_rules
72
+ super
73
+ end
74
+
62
75
  # Compares the contents of two rules.
63
76
  #
64
77
  # @param other [Object] The object to compare with
@@ -74,6 +87,7 @@ module Sass::Tree
74
87
  def add_rules(node)
75
88
  @rule = Sass::Util.strip_string_array(
76
89
  Sass::Util.merge_adjacent_strings(@rule + ["\n"] + node.rule))
90
+ try_to_parse_non_interpolated_rules
77
91
  end
78
92
 
79
93
  # @return [Boolean] Whether or not this rule is continued on the next line
@@ -100,5 +114,16 @@ module Sass::Tree
100
114
  {:filename => filename && ("file://" + URI.escape(File.expand_path(filename))),
101
115
  :line => self.line}
102
116
  end
117
+
118
+ private
119
+
120
+ def try_to_parse_non_interpolated_rules
121
+ if @rule.all? {|t| t.kind_of?(String)}
122
+ # We don't use real filename/line info because we don't have it yet.
123
+ # When we get it, we'll set it on the parsed rules if possible.
124
+ parser = Sass::SCSS::StaticParser.new(@rule.join.strip, 1)
125
+ @parsed_rules = parser.parse_selector('') rescue nil
126
+ end
127
+ end
103
128
  end
104
129
  end
@@ -228,7 +228,7 @@ END
228
228
  # and then parses the result into a {Sass::Selector::CommaSequence}.
229
229
  def visit_rule(node)
230
230
  parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.line)
231
- node.parsed_rules = parser.parse_selector(node.filename)
231
+ node.parsed_rules ||= parser.parse_selector(node.filename)
232
232
  yield
233
233
  end
234
234
 
@@ -128,11 +128,15 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
128
128
  rule_indent = ' ' * @tabs
129
129
  per_rule_indent, total_indent = [:nested, :expanded].include?(node.style) ? [rule_indent, ''] : ['', rule_indent]
130
130
 
131
- total_rule = total_indent + node.resolved_rules.members.
132
- map {|seq| seq.to_a.join.gsub(/([^,])\n/m, node.style == :compressed ? '\1 ' : "\\1\n")}.
133
- join(rule_separator).split("\n").map do |line|
134
- per_rule_indent + line.strip
135
- end.join(line_separator)
131
+ joined_rules = node.resolved_rules.members.map do |seq|
132
+ rule_part = seq.to_a.join
133
+ rule_part.gsub!(/\s*([^,])\s*\n\s*/m, '\1 ') if node.style == :compressed
134
+ rule_part
135
+ end.join(rule_separator)
136
+
137
+ joined_rules.sub!(/\A\s*/, per_rule_indent)
138
+ joined_rules.gsub!(/\s*\n\s*/, "#{line_separator}#{per_rule_indent}")
139
+ total_rule = total_indent << joined_rules
136
140
 
137
141
  to_return = ''
138
142
  old_spaces = ' ' * @tabs
data/lib/sass/util.rb CHANGED
@@ -270,47 +270,6 @@ module Sass
270
270
  version_gt(v1, v2) || !version_gt(v2, v1)
271
271
  end
272
272
 
273
- # A wrapper for `Marshal.dump` that calls `#_before_dump` on the object
274
- # before dumping it, `#_after_dump` afterwards.
275
- # It also calls `#_around_dump` and passes it a block in which the object is dumped.
276
- #
277
- # If any of these methods are undefined, they are not called.
278
- #
279
- # This will recursively call itself on members of arrays and hashes,
280
- # but not of user-defined objects.
281
- # This means that user-defined objects that need their members' `#_before_dump` etc. methods called
282
- # must call `Haml::Util.dump` and `Haml::Util.load` manually on those members.
283
- #
284
- # @param obj [Object] The object to dump.
285
- # @return [String] The dumped data.
286
- def dump(obj)
287
- obj._before_dump if obj.respond_to?(:_before_dump)
288
- return convert_and_dump(obj) unless obj.respond_to?(:_around_dump)
289
- res = nil
290
- obj._around_dump {res = convert_and_dump(obj)}
291
- res
292
- ensure
293
- obj._after_dump if obj.respond_to?(:_after_dump)
294
- end
295
-
296
- # A wrapper for `Marshal.load` that calls `#_after_load` on the object
297
- # after loading it, if it's defined.
298
- #
299
- # @param data [String] The data to load.
300
- # @return [Object] The loaded object.
301
- def load(data)
302
- obj = Marshal.load(data)
303
-
304
- if obj.is_a?(Array)
305
- obj = obj.map {|e| Sass::Util.load(e)}
306
- elsif obj.is_a?(Hash)
307
- obj = map_hash(obj) {|k, v| [Sass::Util.load(k), Sass::Util.load(v)]}
308
- end
309
-
310
- obj._after_load if obj.respond_to?(:_after_load)
311
- obj
312
- end
313
-
314
273
  # Throws a NotImplementedError for an abstract method.
315
274
  #
316
275
  # @param obj [Object] `self`
@@ -706,14 +665,5 @@ MSG
706
665
  return lcs_backtrace(c, x, y, i, j-1, &block) if c[i][j-1] > c[i-1][j]
707
666
  return lcs_backtrace(c, x, y, i-1, j, &block)
708
667
  end
709
-
710
- def convert_and_dump(obj)
711
- if obj.is_a?(Array)
712
- obj = obj.map {|e| dump(e)}
713
- elsif obj.is_a?(Hash)
714
- obj = map_hash(obj) {|k, v| [dump(k), dump(v)]}
715
- end
716
- Marshal.dump(obj)
717
- end
718
668
  end
719
669
  end
@@ -5,8 +5,6 @@ require File.dirname(__FILE__) + '/test_helper'
5
5
  class ImporterTest < Test::Unit::TestCase
6
6
 
7
7
  class FruitImporter < Sass::Importers::Base
8
- attr_reader :cached
9
-
10
8
  def find(name, context = nil)
11
9
  if name =~ %r{fruits/(\w+)(\.s[ac]ss)?}
12
10
  fruit = $1
@@ -31,13 +29,6 @@ class ImporterTest < Test::Unit::TestCase
31
29
  def key(name, context)
32
30
  [self.class.name, name]
33
31
  end
34
-
35
- def _around_dump
36
- @cached = true
37
- yield
38
- ensure
39
- @cached = false
40
- end
41
32
  end
42
33
 
43
34
  # This class proves that you can override the extension scheme for importers
@@ -88,17 +79,4 @@ CSS
88
79
  ensure
89
80
  FileUtils.rm_rf(absolutize("tmp"))
90
81
  end
91
-
92
- def test_caching_importer
93
- source = "p\n foo: bar"
94
- importer = FruitImporter.new
95
- filename = filename_for_test
96
- engine = Sass::Engine.new(source, :filename => filename, :importer => importer)
97
- engine.to_tree # Trigger caching
98
-
99
- sha = Digest::SHA1.hexdigest(source)
100
- cache = engine.options[:cache_store]
101
- cached_tree = cache.retrieve(cache.key(*importer.key(filename, engine.options)), sha)
102
- assert cached_tree.options[:importer].cached, "Importer's _around_dump method should have been called"
103
- end
104
82
  end
@@ -198,6 +198,13 @@ SASS
198
198
  assert_equal "public_instance_methods()", resolve("public_instance_methods()")
199
199
  end
200
200
 
201
+ def test_adding_functions_directly_to_functions_module
202
+ assert !Functions.callable?('nonexistant')
203
+ Functions.class_eval { def nonexistant; end }
204
+ assert Functions.callable?('nonexistant')
205
+ Functions.send :remove_method, :nonexistant
206
+ end
207
+
201
208
  def test_default_functions
202
209
  assert_equal "url(12)", resolve("url(12)")
203
210
  assert_equal 'blam("foo")', resolve('blam("foo")')
@@ -5,19 +5,6 @@ require 'pathname'
5
5
  class UtilTest < Test::Unit::TestCase
6
6
  include Sass::Util
7
7
 
8
- class Dumpable
9
- attr_reader :arr
10
- def initialize; @arr = []; end
11
- def _before_dump; @arr << :before; end
12
- def _after_dump; @arr << :after; end
13
- def _around_dump
14
- @arr << :around_before
15
- yield
16
- @arr << :around_after
17
- end
18
- def _after_load; @arr << :loaded; end
19
- end
20
-
21
8
  def test_scope
22
9
  assert(File.exist?(scope("Rakefile")))
23
10
  end
@@ -253,14 +240,6 @@ class UtilTest < Test::Unit::TestCase
253
240
  assert(!version_gt(v2, v1), "Expected #{v2} = #{v1}")
254
241
  end
255
242
 
256
- def test_dump_and_load
257
- obj = Dumpable.new
258
- data = dump(obj)
259
- assert_equal([:before, :around_before, :around_after, :after], obj.arr)
260
- obj2 = load(data)
261
- assert_equal([:before, :around_before, :loaded], obj2.arr)
262
- end
263
-
264
243
  class FooBar
265
244
  def foo
266
245
  Sass::Util.abstract(self)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sass
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0.alpha.214
4
+ version: 3.1.0.alpha.216
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Weizenbaum
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2010-12-31 00:00:00 -05:00
14
+ date: 2011-01-13 00:00:00 -05:00
15
15
  default_executable:
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
@@ -60,6 +60,7 @@ files:
60
60
  - lib/sass/cache_stores.rb
61
61
  - lib/sass/cache_stores/active_support.rb
62
62
  - lib/sass/cache_stores/base.rb
63
+ - lib/sass/cache_stores/chain.rb
63
64
  - lib/sass/cache_stores/filesystem.rb
64
65
  - lib/sass/cache_stores/memory.rb
65
66
  - lib/sass/cache_stores/null.rb