sass 3.1.0.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
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