sass 3.1.0.alpha.2

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.
Files changed (205) hide show
  1. data/.yardopts +11 -0
  2. data/CONTRIBUTING +3 -0
  3. data/EDGE_GEM_VERSION +1 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +201 -0
  6. data/REVISION +1 -0
  7. data/Rakefile +353 -0
  8. data/VERSION +1 -0
  9. data/VERSION_NAME +1 -0
  10. data/bin/css2sass +13 -0
  11. data/bin/sass +8 -0
  12. data/bin/sass-convert +7 -0
  13. data/extra/update_watch.rb +13 -0
  14. data/init.rb +18 -0
  15. data/lib/sass.rb +71 -0
  16. data/lib/sass/cache_store.rb +208 -0
  17. data/lib/sass/callbacks.rb +66 -0
  18. data/lib/sass/css.rb +294 -0
  19. data/lib/sass/engine.rb +792 -0
  20. data/lib/sass/environment.rb +143 -0
  21. data/lib/sass/error.rb +201 -0
  22. data/lib/sass/exec.rb +619 -0
  23. data/lib/sass/importers.rb +22 -0
  24. data/lib/sass/importers/base.rb +138 -0
  25. data/lib/sass/importers/filesystem.rb +121 -0
  26. data/lib/sass/less.rb +363 -0
  27. data/lib/sass/plugin.rb +126 -0
  28. data/lib/sass/plugin/compiler.rb +346 -0
  29. data/lib/sass/plugin/configuration.rb +123 -0
  30. data/lib/sass/plugin/generic.rb +15 -0
  31. data/lib/sass/plugin/merb.rb +48 -0
  32. data/lib/sass/plugin/rack.rb +47 -0
  33. data/lib/sass/plugin/rails.rb +41 -0
  34. data/lib/sass/plugin/staleness_checker.rb +145 -0
  35. data/lib/sass/railtie.rb +8 -0
  36. data/lib/sass/repl.rb +58 -0
  37. data/lib/sass/root.rb +7 -0
  38. data/lib/sass/script.rb +63 -0
  39. data/lib/sass/script/bool.rb +18 -0
  40. data/lib/sass/script/color.rb +490 -0
  41. data/lib/sass/script/css_lexer.rb +29 -0
  42. data/lib/sass/script/css_parser.rb +31 -0
  43. data/lib/sass/script/funcall.rb +78 -0
  44. data/lib/sass/script/functions.rb +852 -0
  45. data/lib/sass/script/interpolation.rb +70 -0
  46. data/lib/sass/script/lexer.rb +337 -0
  47. data/lib/sass/script/literal.rb +236 -0
  48. data/lib/sass/script/node.rb +101 -0
  49. data/lib/sass/script/number.rb +420 -0
  50. data/lib/sass/script/operation.rb +92 -0
  51. data/lib/sass/script/parser.rb +392 -0
  52. data/lib/sass/script/string.rb +67 -0
  53. data/lib/sass/script/string_interpolation.rb +93 -0
  54. data/lib/sass/script/unary_operation.rb +57 -0
  55. data/lib/sass/script/variable.rb +48 -0
  56. data/lib/sass/scss.rb +17 -0
  57. data/lib/sass/scss/css_parser.rb +51 -0
  58. data/lib/sass/scss/parser.rb +838 -0
  59. data/lib/sass/scss/rx.rb +126 -0
  60. data/lib/sass/scss/sass_parser.rb +11 -0
  61. data/lib/sass/scss/script_lexer.rb +15 -0
  62. data/lib/sass/scss/script_parser.rb +25 -0
  63. data/lib/sass/scss/static_parser.rb +40 -0
  64. data/lib/sass/selector.rb +361 -0
  65. data/lib/sass/selector/abstract_sequence.rb +62 -0
  66. data/lib/sass/selector/comma_sequence.rb +82 -0
  67. data/lib/sass/selector/sequence.rb +236 -0
  68. data/lib/sass/selector/simple.rb +113 -0
  69. data/lib/sass/selector/simple_sequence.rb +135 -0
  70. data/lib/sass/shared.rb +78 -0
  71. data/lib/sass/tree/comment_node.rb +128 -0
  72. data/lib/sass/tree/debug_node.rb +36 -0
  73. data/lib/sass/tree/directive_node.rb +75 -0
  74. data/lib/sass/tree/extend_node.rb +65 -0
  75. data/lib/sass/tree/for_node.rb +67 -0
  76. data/lib/sass/tree/if_node.rb +81 -0
  77. data/lib/sass/tree/import_node.rb +124 -0
  78. data/lib/sass/tree/mixin_def_node.rb +60 -0
  79. data/lib/sass/tree/mixin_node.rb +123 -0
  80. data/lib/sass/tree/node.rb +490 -0
  81. data/lib/sass/tree/prop_node.rb +220 -0
  82. data/lib/sass/tree/root_node.rb +125 -0
  83. data/lib/sass/tree/rule_node.rb +273 -0
  84. data/lib/sass/tree/variable_node.rb +39 -0
  85. data/lib/sass/tree/warn_node.rb +42 -0
  86. data/lib/sass/tree/while_node.rb +48 -0
  87. data/lib/sass/util.rb +687 -0
  88. data/lib/sass/util/subset_map.rb +101 -0
  89. data/lib/sass/version.rb +109 -0
  90. data/rails/init.rb +1 -0
  91. data/test/sass/cache_test.rb +74 -0
  92. data/test/sass/callbacks_test.rb +61 -0
  93. data/test/sass/conversion_test.rb +1210 -0
  94. data/test/sass/css2sass_test.rb +364 -0
  95. data/test/sass/data/hsl-rgb.txt +319 -0
  96. data/test/sass/engine_test.rb +2273 -0
  97. data/test/sass/extend_test.rb +1348 -0
  98. data/test/sass/functions_test.rb +565 -0
  99. data/test/sass/importer_test.rb +104 -0
  100. data/test/sass/less_conversion_test.rb +632 -0
  101. data/test/sass/mock_importer.rb +49 -0
  102. data/test/sass/more_results/more1.css +9 -0
  103. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  104. data/test/sass/more_results/more_import.css +29 -0
  105. data/test/sass/more_templates/_more_partial.sass +2 -0
  106. data/test/sass/more_templates/more1.sass +23 -0
  107. data/test/sass/more_templates/more_import.sass +11 -0
  108. data/test/sass/plugin_test.rb +430 -0
  109. data/test/sass/results/alt.css +4 -0
  110. data/test/sass/results/basic.css +9 -0
  111. data/test/sass/results/compact.css +5 -0
  112. data/test/sass/results/complex.css +86 -0
  113. data/test/sass/results/compressed.css +1 -0
  114. data/test/sass/results/expanded.css +19 -0
  115. data/test/sass/results/import.css +31 -0
  116. data/test/sass/results/line_numbers.css +49 -0
  117. data/test/sass/results/mixins.css +95 -0
  118. data/test/sass/results/multiline.css +24 -0
  119. data/test/sass/results/nested.css +22 -0
  120. data/test/sass/results/options.css +1 -0
  121. data/test/sass/results/parent_ref.css +13 -0
  122. data/test/sass/results/script.css +16 -0
  123. data/test/sass/results/scss_import.css +31 -0
  124. data/test/sass/results/scss_importee.css +2 -0
  125. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  126. data/test/sass/results/subdir/subdir.css +3 -0
  127. data/test/sass/results/units.css +11 -0
  128. data/test/sass/results/warn.css +0 -0
  129. data/test/sass/results/warn_imported.css +0 -0
  130. data/test/sass/script_conversion_test.rb +254 -0
  131. data/test/sass/script_test.rb +459 -0
  132. data/test/sass/scss/css_test.rb +897 -0
  133. data/test/sass/scss/rx_test.rb +156 -0
  134. data/test/sass/scss/scss_test.rb +1088 -0
  135. data/test/sass/scss/test_helper.rb +37 -0
  136. data/test/sass/templates/_partial.sass +2 -0
  137. data/test/sass/templates/alt.sass +16 -0
  138. data/test/sass/templates/basic.sass +23 -0
  139. data/test/sass/templates/bork1.sass +2 -0
  140. data/test/sass/templates/bork2.sass +2 -0
  141. data/test/sass/templates/bork3.sass +2 -0
  142. data/test/sass/templates/bork4.sass +2 -0
  143. data/test/sass/templates/compact.sass +17 -0
  144. data/test/sass/templates/complex.sass +305 -0
  145. data/test/sass/templates/compressed.sass +15 -0
  146. data/test/sass/templates/expanded.sass +17 -0
  147. data/test/sass/templates/import.sass +12 -0
  148. data/test/sass/templates/importee.less +2 -0
  149. data/test/sass/templates/importee.sass +19 -0
  150. data/test/sass/templates/line_numbers.sass +13 -0
  151. data/test/sass/templates/mixin_bork.sass +5 -0
  152. data/test/sass/templates/mixins.sass +76 -0
  153. data/test/sass/templates/multiline.sass +20 -0
  154. data/test/sass/templates/nested.sass +25 -0
  155. data/test/sass/templates/nested_bork1.sass +2 -0
  156. data/test/sass/templates/nested_bork2.sass +2 -0
  157. data/test/sass/templates/nested_bork3.sass +2 -0
  158. data/test/sass/templates/nested_bork4.sass +2 -0
  159. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  160. data/test/sass/templates/options.sass +2 -0
  161. data/test/sass/templates/parent_ref.sass +25 -0
  162. data/test/sass/templates/script.sass +101 -0
  163. data/test/sass/templates/scss_import.scss +11 -0
  164. data/test/sass/templates/scss_importee.scss +1 -0
  165. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  166. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  167. data/test/sass/templates/subdir/subdir.sass +6 -0
  168. data/test/sass/templates/units.sass +11 -0
  169. data/test/sass/templates/warn.sass +3 -0
  170. data/test/sass/templates/warn_imported.sass +4 -0
  171. data/test/sass/test_helper.rb +8 -0
  172. data/test/sass/util/subset_map_test.rb +91 -0
  173. data/test/sass/util_test.rb +275 -0
  174. data/test/test_helper.rb +64 -0
  175. data/vendor/fssm/LICENSE +20 -0
  176. data/vendor/fssm/README.markdown +55 -0
  177. data/vendor/fssm/Rakefile +59 -0
  178. data/vendor/fssm/VERSION.yml +5 -0
  179. data/vendor/fssm/example.rb +9 -0
  180. data/vendor/fssm/fssm.gemspec +77 -0
  181. data/vendor/fssm/lib/fssm.rb +33 -0
  182. data/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
  183. data/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
  184. data/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
  185. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
  186. data/vendor/fssm/lib/fssm/monitor.rb +26 -0
  187. data/vendor/fssm/lib/fssm/path.rb +91 -0
  188. data/vendor/fssm/lib/fssm/pathname.rb +502 -0
  189. data/vendor/fssm/lib/fssm/state/directory.rb +57 -0
  190. data/vendor/fssm/lib/fssm/state/file.rb +24 -0
  191. data/vendor/fssm/lib/fssm/support.rb +63 -0
  192. data/vendor/fssm/lib/fssm/tree.rb +176 -0
  193. data/vendor/fssm/profile/prof-cache.rb +40 -0
  194. data/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
  195. data/vendor/fssm/profile/prof-pathname.rb +68 -0
  196. data/vendor/fssm/profile/prof-plain-pathname.html +988 -0
  197. data/vendor/fssm/profile/prof.html +2379 -0
  198. data/vendor/fssm/spec/path_spec.rb +75 -0
  199. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  200. data/vendor/fssm/spec/root/file.css +0 -0
  201. data/vendor/fssm/spec/root/file.rb +0 -0
  202. data/vendor/fssm/spec/root/file.yml +0 -0
  203. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  204. data/vendor/fssm/spec/spec_helper.rb +14 -0
  205. metadata +297 -0
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 3.1.0.alpha.2
@@ -0,0 +1 @@
1
+ Bleeding Edge
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/sass'
4
+ require 'sass/exec'
5
+
6
+ warn <<END
7
+ DEPRECATION WARNING:
8
+ The css2sass tool is deprecated and will be removed in Sass 3.2.
9
+ Use the sass-convert tool instead.
10
+ END
11
+
12
+ opts = Sass::Exec::SassConvert.new(%w[--from css --to sass] + ARGV)
13
+ opts.parse!
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # The command line Sass parser.
3
+
4
+ require File.dirname(__FILE__) + '/../lib/sass'
5
+ require 'sass/exec'
6
+
7
+ opts = Sass::Exec::Sass.new(ARGV)
8
+ opts.parse!
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/sass'
4
+ require 'sass/exec'
5
+
6
+ opts = Sass::Exec::SassConvert.new(ARGV)
7
+ opts.parse!
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+ require 'json'
4
+ set :port, 3124
5
+ set :environment, :production
6
+ enable :lock
7
+ Dir.chdir(File.dirname(__FILE__) + "/..")
8
+
9
+ post "/" do
10
+ puts "Recieved payload!"
11
+ puts "Rev: #{`git name-rev HEAD`.strip}"
12
+ system %{rake handle_update --trace REF=#{JSON.parse(params["payload"])["ref"].inspect}}
13
+ end
data/init.rb ADDED
@@ -0,0 +1,18 @@
1
+ begin
2
+ require File.join(File.dirname(__FILE__), 'lib', 'sass') # From here
3
+ rescue LoadError
4
+ begin
5
+ require 'sass' # From gem
6
+ rescue LoadError => e
7
+ # gems:install may be run to install Haml with the skeleton plugin
8
+ # but not the gem itself installed.
9
+ # Don't die if this is the case.
10
+ raise e unless defined?(Rake) &&
11
+ (Rake.application.top_level_tasks.include?('gems') ||
12
+ Rake.application.top_level_tasks.include?('gems:install'))
13
+ end
14
+ end
15
+
16
+ # Load Sass.
17
+ # Sass may be undefined if we're running gems:install.
18
+ require 'sass/plugin' if defined?(Sass)
@@ -0,0 +1,71 @@
1
+ dir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
3
+
4
+ # This is necessary to set so that the Haml code that tries to load Sass
5
+ # knows that Sass is indeed loading,
6
+ # even if there's some crazy autoload stuff going on.
7
+ SASS_BEGUN_TO_LOAD = true unless defined?(SASS_BEGUN_TO_LOAD)
8
+
9
+ require 'sass/version'
10
+
11
+ # The module that contains everything Sass-related:
12
+ #
13
+ # * {Sass::Engine} is the class used to render Sass/SCSS within Ruby code.
14
+ # * {Sass::Plugin} is interfaces with web frameworks (Rails and Merb in particular).
15
+ # * {Sass::SyntaxError} is raised when Sass encounters an error.
16
+ # * {Sass::CSS} handles conversion of CSS to Sass.
17
+ #
18
+ # Also see the {file:SASS_REFERENCE.md full Sass reference}.
19
+ module Sass
20
+ # Compile a Sass or SCSS string to CSS.
21
+ # Defaults to SCSS.
22
+ #
23
+ # @param contents [String] The contents of the Sass file.
24
+ # @param options [{Symbol => Object}] An options hash;
25
+ # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
26
+ # @raise [Sass::SyntaxError] if there's an error in the document
27
+ # @raise [Encoding::UndefinedConversionError] if the source encoding
28
+ # cannot be converted to UTF-8
29
+ # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
30
+ def self.compile(contents, options = {})
31
+ options[:syntax] ||= :scss
32
+ Engine.new(contents, options).to_css
33
+ end
34
+
35
+ # Compile a file on disk to CSS.
36
+ #
37
+ # @param filename [String] The path to the Sass, SCSS, or CSS file on disk.
38
+ # @param options [{Symbol => Object}] An options hash;
39
+ # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
40
+ # @raise [Sass::SyntaxError] if there's an error in the document
41
+ # @raise [Encoding::UndefinedConversionError] if the source encoding
42
+ # cannot be converted to UTF-8
43
+ # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
44
+ #
45
+ # @overload compile_file(filename, options = {})
46
+ # @return [String] The compiled CSS.
47
+ #
48
+ # @overload compile_file(filename, css_filename, options = {})
49
+ # @param css_filename [String] The location to which to write the compiled CSS.
50
+ def self.compile_file(filename, *args)
51
+ options = args.last.is_a?(Hash) ? args.pop : {}
52
+ css_filename ||= args.shift
53
+ options[:css_filename] = css_filename
54
+ result = Sass::Engine.for_file(filename, options).render
55
+ if css_filename
56
+ open(css_filename,"w") {|css_file| css_file.write(result) }
57
+ nil
58
+ else
59
+ result
60
+ end
61
+ end
62
+ end
63
+
64
+ require 'sass/util'
65
+
66
+ dir = Sass::Util.scope("vendor/fssm/lib")
67
+ $LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
68
+
69
+ require 'sass/engine'
70
+ require 'sass/plugin' if defined?(Merb::Plugins)
71
+ require 'sass/railtie'
@@ -0,0 +1,208 @@
1
+ require 'stringio'
2
+
3
+ module Sass
4
+ # An abstract base class for backends for the Sass cache.
5
+ # Any key-value store can act as such a backend;
6
+ # it just needs to implement the
7
+ # \{#_store} and \{#_retrieve} methods.
8
+ #
9
+ # To use a cache store with Sass,
10
+ # use the {file:SASS_REFERENCE.md#cache_store-option `:cache_store` option}.
11
+ class CacheStore
12
+ # Store cached contents for later retrieval
13
+ # Must be implemented by all CacheStore subclasses
14
+ #
15
+ # Note: cache contents contain binary data.
16
+ #
17
+ # @param key [String] The key to store the contents under
18
+ # @param version [String] The current sass version.
19
+ # Cached contents must not be retrieved across different versions of sass.
20
+ # @param sha [String] The sha of the sass source.
21
+ # Cached contents must not be retrieved if the sha has changed.
22
+ # @param contents [String] The contents to store.
23
+ def _store(key, version, sha, contents)
24
+ raise "#{self.class} must implement #_store."
25
+ end
26
+
27
+ # Retrieved cached contents.
28
+ # Must be implemented by all subclasses.
29
+ #
30
+ # Note: if the key exists but the sha or version have changed,
31
+ # then the key may be deleted by the cache store, if it wants to do so.
32
+ #
33
+ # @param key [String] The key to retrieve
34
+ # @param version [String] The current sass version.
35
+ # Cached contents must not be retrieved across different versions of sass.
36
+ # @param sha [String] The sha of the sass source.
37
+ # Cached contents must not be retrieved if the sha has changed.
38
+ # @return [String] The contents that were previously stored.
39
+ # @return [NilClass] when the cache key is not found or the version or sha have changed.
40
+ def _retrieve(key, version, sha)
41
+ raise "#{self.class} must implement #_retrieve."
42
+ end
43
+
44
+ # Store a {Sass::Tree::RootNode}.
45
+ #
46
+ # @param key [String] The key to store it under.
47
+ # @param sha [String] The checksum for the contents that are being stored.
48
+ # @param obj [Object] The object to cache.
49
+ def store(key, sha, root)
50
+ _store(key, Sass::VERSION, sha, Sass::Util.dump(root))
51
+ end
52
+
53
+ # Retrieve a {Sass::Tree::RootNode}.
54
+ #
55
+ # @param key [String] The key the root element was stored under.
56
+ # @param sha [String] The checksum of the root element's content.
57
+ # @return [Object] The cached object.
58
+ def retrieve(key, sha)
59
+ contents = _retrieve(key, Sass::VERSION, sha)
60
+ Sass::Util.load(contents) if contents
61
+ rescue EOFError, TypeError, ArgumentError => e
62
+ raise
63
+ Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
64
+ end
65
+
66
+ # Return the key for the sass file.
67
+ #
68
+ # The `(sass_dirname, sass_basename)` pair
69
+ # should uniquely identify the Sass document,
70
+ # but otherwise there are no restrictions on their content.
71
+ #
72
+ # @param sass_dirname [String]
73
+ # The fully-expanded location of the Sass file.
74
+ # This corresponds to the directory name on a filesystem.
75
+ # @param sass_basename [String] The name of the Sass file that is being referenced.
76
+ # This corresponds to the basename on a filesystem.
77
+ def key(sass_dirname, sass_basename)
78
+ dir = Digest::SHA1.hexdigest(sass_dirname)
79
+ filename = "#{sass_basename}c"
80
+ "#{dir}/#{filename}"
81
+ end
82
+ end
83
+
84
+ # A backend for the Sass cache using the filesystem.
85
+ class FileCacheStore < CacheStore
86
+ # The directory where the cached files will be stored.
87
+ #
88
+ # @return [String]
89
+ attr_accessor :cache_location
90
+
91
+ # Create a new FileCacheStore.
92
+ #
93
+ # @param cache_location [String] see \{#cache\_location}
94
+ def initialize(cache_location)
95
+ @cache_location = cache_location
96
+ end
97
+
98
+ # @see {CacheStore#\_retrieve\_}
99
+ def _retrieve(key, version, sha)
100
+ return unless File.readable?(path_to(key))
101
+ contents = nil
102
+ File.open(path_to(key), "rb") do |f|
103
+ if f.readline("\n").strip == version && f.readline("\n").strip == sha
104
+ return f.read
105
+ end
106
+ end
107
+ File.unlink path_to(key)
108
+ nil
109
+ rescue EOFError, TypeError, ArgumentError => e
110
+ Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
111
+ end
112
+
113
+ # @see {CacheStore#\_store\_}
114
+ def _store(key, version, sha, contents)
115
+ return unless File.writable?(File.dirname(@cache_location))
116
+ return if File.exists?(@cache_location) && !File.writable?(@cache_location)
117
+ compiled_filename = path_to(key)
118
+ return if File.exists?(File.dirname(compiled_filename)) && !File.writable?(File.dirname(compiled_filename))
119
+ return if File.exists?(compiled_filename) && !File.writable?(compiled_filename)
120
+ FileUtils.mkdir_p(File.dirname(compiled_filename))
121
+ File.open(compiled_filename, "wb") do |f|
122
+ f.puts(version)
123
+ f.puts(sha)
124
+ f.write(contents)
125
+ end
126
+ end
127
+
128
+ private
129
+
130
+ # Returns the path to a file for the given key.
131
+ #
132
+ # @param key [String]
133
+ # @return [String] The path to the cache file.
134
+ def path_to(key)
135
+ File.join(cache_location, key)
136
+ end
137
+ end
138
+
139
+ # A backend for the Sass cache using in-process memory.
140
+ class InMemoryCacheStore < CacheStore
141
+ # Since the {InMemoryCacheStore} is stored in the Sass tree's options hash,
142
+ # when the options get serialized as part of serializing the tree,
143
+ # you get crazy exponential growth in the size of the cached objects
144
+ # unless you don't dump the cache.
145
+ #
146
+ # @private
147
+ def _dump(depth)
148
+ ""
149
+ end
150
+
151
+ # If we deserialize this class, just make a new empty one.
152
+ #
153
+ # @private
154
+ def self._load(repr)
155
+ InMemoryCacheStore.new
156
+ end
157
+
158
+ # Create a new, empty cache store.
159
+ def initialize
160
+ @contents = {}
161
+ end
162
+
163
+ # @see CacheStore#_retrieve
164
+ def _retrieve(key, version, sha)
165
+ if @contents.has_key?(key)
166
+ return unless @contents[key][:version] == version
167
+ return unless @contents[key][:sha] == sha
168
+ return @contents[key][:contents]
169
+ end
170
+ end
171
+
172
+ # @see CacheStore#_store
173
+ def _store(key, version, sha, contents)
174
+ @contents[key] = {
175
+ :version => version,
176
+ :sha => sha,
177
+ :contents => contents
178
+ }
179
+ end
180
+
181
+ # Destructively clear the cache.
182
+ def reset!
183
+ @contents = {}
184
+ end
185
+ end
186
+
187
+ # Doesn't store anything, but records what things it should have stored.
188
+ # This doesn't currently have any use except for testing and debugging.
189
+ #
190
+ # @private
191
+ class NullCacheStore < CacheStore
192
+ def initialize
193
+ @keys = {}
194
+ end
195
+
196
+ def _retrieve(key, version, sha)
197
+ nil
198
+ end
199
+
200
+ def _store(key, version, sha, contents)
201
+ @keys[key] = true
202
+ end
203
+
204
+ def was_set?(key)
205
+ @keys[key]
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,66 @@
1
+ module Sass
2
+ # A lightweight infrastructure for defining and running callbacks.
3
+ # Callbacks are defined using \{#define\_callback\} at the class level,
4
+ # and called using `run_#{name}` at the instance level.
5
+ #
6
+ # Clients can add callbacks by calling the generated `on_#{name}` method,
7
+ # and passing in a block that's run when the callback is activated.
8
+ #
9
+ # @example Define a callback
10
+ # class Munger
11
+ # extend Sass::Callbacks
12
+ # define_callback :string_munged
13
+ #
14
+ # def munge(str)
15
+ # res = str.gsub(/[a-z]/, '\1\1')
16
+ # run_string_munged str, res
17
+ # res
18
+ # end
19
+ # end
20
+ #
21
+ # @example Use a callback
22
+ # m = Munger.new
23
+ # m.on_string_munged {|str, res| puts "#{str} was munged into #{res}!"}
24
+ # m.munge "bar" #=> bar was munged into bbaarr!
25
+ module Callbacks
26
+ # Automatically includes {InstanceMethods}
27
+ # when something extends this module.
28
+ #
29
+ # @param base [Module]
30
+ def self.extended(base)
31
+ base.send(:include, InstanceMethods)
32
+ end
33
+ protected
34
+
35
+ module InstanceMethods
36
+ # Removes all callbacks registered against this object.
37
+ def clear_callbacks!
38
+ @_sass_callbacks = {}
39
+ end
40
+ end
41
+
42
+ # Define a callback with the given name.
43
+ # This will define an `on_#{name}` method
44
+ # that registers a block,
45
+ # and a `run_#{name}` method that runs that block
46
+ # (optionall with some arguments).
47
+ #
48
+ # @param name [Symbol] The name of the callback
49
+ # @return [void]
50
+ def define_callback(name)
51
+ class_eval <<RUBY
52
+ def on_#{name}(&block)
53
+ @_sass_callbacks ||= {}
54
+ (@_sass_callbacks[#{name.inspect}] ||= []) << block
55
+ end
56
+
57
+ def run_#{name}(*args)
58
+ return unless @_sass_callbacks
59
+ return unless @_sass_callbacks[#{name.inspect}]
60
+ @_sass_callbacks[#{name.inspect}].each {|c| c[*args]}
61
+ end
62
+ private :run_#{name}
63
+ RUBY
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,294 @@
1
+ require File.dirname(__FILE__) + '/../sass'
2
+ require 'sass/tree/node'
3
+ require 'sass/scss/css_parser'
4
+ require 'strscan'
5
+
6
+ module Sass
7
+ # This class converts CSS documents into Sass or SCSS templates.
8
+ # It works by parsing the CSS document into a {Sass::Tree} structure,
9
+ # and then applying various transformations to the structure
10
+ # to produce more concise and idiomatic Sass/SCSS.
11
+ #
12
+ # Example usage:
13
+ #
14
+ # Sass::CSS.new("p { color: blue }").render(:sass) #=> "p\n color: blue"
15
+ # Sass::CSS.new("p { color: blue }").render(:scss) #=> "p {\n color: blue; }"
16
+ class CSS
17
+ # @param template [String] The CSS stylesheet.
18
+ # This stylesheet can be encoded using any encoding
19
+ # that can be converted to Unicode.
20
+ # If the stylesheet contains an `@charset` declaration,
21
+ # that overrides the Ruby encoding
22
+ # (see {file:SASS_REFERENCE.md#encodings the encoding documentation})
23
+ # @option options :old [Boolean] (false)
24
+ # Whether or not to output old property syntax
25
+ # (`:color blue` as opposed to `color: blue`).
26
+ # This is only meaningful when generating Sass code,
27
+ # rather than SCSS.
28
+ def initialize(template, options = {})
29
+ if template.is_a? IO
30
+ template = template.read
31
+ end
32
+
33
+ @options = options.dup
34
+ # Backwards compatibility
35
+ @options[:old] = true if @options[:alternate] == false
36
+ @template = template
37
+ end
38
+
39
+ # Converts the CSS template into Sass or SCSS code.
40
+ #
41
+ # @param fmt [Symbol] `:sass` or `:scss`, designating the format to return.
42
+ # @return [String] The resulting Sass or SCSS code
43
+ # @raise [Sass::SyntaxError] if there's an error parsing the CSS template
44
+ def render(fmt = :sass)
45
+ check_encoding!
46
+ build_tree.send("to_#{fmt}", @options).strip + "\n"
47
+ rescue Sass::SyntaxError => err
48
+ err.modify_backtrace(:filename => @options[:filename] || '(css)')
49
+ raise err
50
+ end
51
+
52
+ # Returns the original encoding of the document,
53
+ # or `nil` under Ruby 1.8.
54
+ #
55
+ # @return [Encoding, nil]
56
+ # @raise [Encoding::UndefinedConversionError] if the source encoding
57
+ # cannot be converted to UTF-8
58
+ # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
59
+ def source_encoding
60
+ check_encoding!
61
+ @original_encoding
62
+ end
63
+
64
+ private
65
+
66
+ def check_encoding!
67
+ return if @checked_encoding
68
+ @checked_encoding = true
69
+ @template, @original_encoding = Sass::Util.check_sass_encoding(@template) do |msg, line|
70
+ raise Sass::SyntaxError.new(msg, :line => line)
71
+ end
72
+ end
73
+
74
+ # Parses the CSS template and applies various transformations
75
+ #
76
+ # @return [Tree::Node] The root node of the parsed tree
77
+ def build_tree
78
+ root = Sass::SCSS::CssParser.new(@template).parse
79
+ expand_commas root
80
+ parent_ref_rules root
81
+ remove_parent_refs root
82
+ flatten_rules root
83
+ fold_commas root
84
+ root
85
+ end
86
+
87
+ # Transform
88
+ #
89
+ # foo, bar, baz
90
+ # color: blue
91
+ #
92
+ # into
93
+ #
94
+ # foo
95
+ # color: blue
96
+ # bar
97
+ # color: blue
98
+ # baz
99
+ # color: blue
100
+ #
101
+ # @param root [Tree::Node] The parent node
102
+ def expand_commas(root)
103
+ root.children.map! do |child|
104
+ unless child.is_a?(Tree::RuleNode) && child.rule.first.include?(',')
105
+ expand_commas(child) if child.is_a?(Tree::DirectiveNode)
106
+ next child
107
+ end
108
+ child.rule.first.split(',').map do |rule|
109
+ node = Tree::RuleNode.new([rule.strip])
110
+ node.children = child.children
111
+ node
112
+ end
113
+ end
114
+ root.children.flatten!
115
+ end
116
+
117
+ # Make rules use parent refs so that
118
+ #
119
+ # foo
120
+ # color: green
121
+ # foo.bar
122
+ # color: blue
123
+ #
124
+ # becomes
125
+ #
126
+ # foo
127
+ # color: green
128
+ # &.bar
129
+ # color: blue
130
+ #
131
+ # This has the side effect of nesting rules,
132
+ # so that
133
+ #
134
+ # foo
135
+ # color: green
136
+ # foo bar
137
+ # color: red
138
+ # foo baz
139
+ # color: blue
140
+ #
141
+ # becomes
142
+ #
143
+ # foo
144
+ # color: green
145
+ # & bar
146
+ # color: red
147
+ # & baz
148
+ # color: blue
149
+ #
150
+ # @param root [Tree::Node] The parent node
151
+ def parent_ref_rules(root)
152
+ current_rule = nil
153
+ root.children.map! do |child|
154
+ unless child.is_a?(Tree::RuleNode)
155
+ parent_ref_rules(child) if child.is_a?(Tree::DirectiveNode)
156
+ next child
157
+ end
158
+
159
+ first, rest = child.rule.first.scan(/\A(&?(?: .|[^ ])[^.#: \[]*)([.#: \[].*)?\Z/m).first
160
+
161
+ if current_rule.nil? || current_rule.rule.first != first
162
+ current_rule = Tree::RuleNode.new([first])
163
+ end
164
+
165
+ if rest
166
+ child.rule = ["&" + rest]
167
+ current_rule << child
168
+ else
169
+ current_rule.children += child.children
170
+ end
171
+
172
+ current_rule
173
+ end
174
+ root.children.compact!
175
+ root.children.uniq!
176
+
177
+ root.children.each { |v| parent_ref_rules(v) }
178
+ end
179
+
180
+ # Remove useless parent refs so that
181
+ #
182
+ # foo
183
+ # & bar
184
+ # color: blue
185
+ #
186
+ # becomes
187
+ #
188
+ # foo
189
+ # bar
190
+ # color: blue
191
+ #
192
+ # @param root [Tree::Node] The parent node
193
+ def remove_parent_refs(root)
194
+ root.children.each do |child|
195
+ case child
196
+ when Tree::RuleNode
197
+ child.rule.first.gsub! /^& +/, ''
198
+ remove_parent_refs child
199
+ when Tree::DirectiveNode
200
+ remove_parent_refs child
201
+ end
202
+ end
203
+ end
204
+
205
+ # Flatten rules so that
206
+ #
207
+ # foo
208
+ # bar
209
+ # color: red
210
+ #
211
+ # becomes
212
+ #
213
+ # foo bar
214
+ # color: red
215
+ #
216
+ # and
217
+ #
218
+ # foo
219
+ # &.bar
220
+ # color: blue
221
+ #
222
+ # becomes
223
+ #
224
+ # foo.bar
225
+ # color: blue
226
+ #
227
+ # @param root [Tree::Node] The parent node
228
+ def flatten_rules(root)
229
+ root.children.each do |child|
230
+ case child
231
+ when Tree::RuleNode
232
+ flatten_rule(child)
233
+ when Tree::DirectiveNode
234
+ flatten_rules(child)
235
+ end
236
+ end
237
+ end
238
+
239
+ # Flattens a single rule
240
+ #
241
+ # @param rule [Tree::RuleNode] The candidate for flattening
242
+ # @see #flatten_rules
243
+ def flatten_rule(rule)
244
+ while rule.children.size == 1 && rule.children.first.is_a?(Tree::RuleNode)
245
+ child = rule.children.first
246
+
247
+ if child.rule.first[0] == ?&
248
+ rule.rule = [child.rule.first.gsub(/^&/, rule.rule.first)]
249
+ else
250
+ rule.rule = ["#{rule.rule.first} #{child.rule.first}"]
251
+ end
252
+
253
+ rule.children = child.children
254
+ end
255
+
256
+ flatten_rules(rule)
257
+ end
258
+
259
+ # Transform
260
+ #
261
+ # foo
262
+ # bar
263
+ # color: blue
264
+ # baz
265
+ # color: blue
266
+ #
267
+ # into
268
+ #
269
+ # foo
270
+ # bar, baz
271
+ # color: blue
272
+ #
273
+ # @param rule [Tree::RuleNode] The candidate for flattening
274
+ def fold_commas(root)
275
+ prev_rule = nil
276
+ root.children.map! do |child|
277
+ unless child.is_a?(Tree::RuleNode)
278
+ fold_commas(child) if child.is_a?(Tree::DirectiveNode)
279
+ next child
280
+ end
281
+
282
+ if prev_rule && prev_rule.children == child.children
283
+ prev_rule.rule.first << ", #{child.rule.first}"
284
+ next nil
285
+ end
286
+
287
+ fold_commas(child)
288
+ prev_rule = child
289
+ child
290
+ end
291
+ root.children.compact!
292
+ end
293
+ end
294
+ end