haml 2.0.10 → 2.2.0

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

Potentially problematic release.


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

Files changed (107) hide show
  1. data/.yardopts +5 -0
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +347 -0
  4. data/Rakefile +124 -19
  5. data/VERSION +1 -1
  6. data/VERSION_NAME +1 -0
  7. data/extra/haml-mode.el +397 -78
  8. data/extra/sass-mode.el +148 -36
  9. data/extra/update_watch.rb +13 -0
  10. data/lib/haml.rb +15 -993
  11. data/lib/haml/buffer.rb +131 -84
  12. data/lib/haml/engine.rb +129 -97
  13. data/lib/haml/error.rb +7 -7
  14. data/lib/haml/exec.rb +127 -42
  15. data/lib/haml/filters.rb +107 -42
  16. data/lib/haml/helpers.rb +210 -156
  17. data/lib/haml/helpers/action_view_extensions.rb +34 -39
  18. data/lib/haml/helpers/action_view_mods.rb +132 -139
  19. data/lib/haml/html.rb +77 -65
  20. data/lib/haml/precompiler.rb +404 -213
  21. data/lib/haml/shared.rb +78 -0
  22. data/lib/haml/template.rb +14 -14
  23. data/lib/haml/template/patch.rb +2 -2
  24. data/lib/haml/template/plugin.rb +2 -3
  25. data/lib/haml/util.rb +211 -6
  26. data/lib/haml/version.rb +30 -13
  27. data/lib/sass.rb +7 -856
  28. data/lib/sass/css.rb +169 -161
  29. data/lib/sass/engine.rb +344 -328
  30. data/lib/sass/environment.rb +79 -0
  31. data/lib/sass/error.rb +33 -11
  32. data/lib/sass/files.rb +139 -0
  33. data/lib/sass/plugin.rb +160 -117
  34. data/lib/sass/plugin/merb.rb +7 -6
  35. data/lib/sass/plugin/rails.rb +5 -6
  36. data/lib/sass/repl.rb +58 -0
  37. data/lib/sass/script.rb +59 -0
  38. data/lib/sass/script/bool.rb +17 -0
  39. data/lib/sass/script/color.rb +183 -0
  40. data/lib/sass/script/funcall.rb +50 -0
  41. data/lib/sass/script/functions.rb +198 -0
  42. data/lib/sass/script/lexer.rb +178 -0
  43. data/lib/sass/script/literal.rb +177 -0
  44. data/lib/sass/script/node.rb +14 -0
  45. data/lib/sass/script/number.rb +381 -0
  46. data/lib/sass/script/operation.rb +45 -0
  47. data/lib/sass/script/parser.rb +172 -0
  48. data/lib/sass/script/string.rb +12 -0
  49. data/lib/sass/script/unary_operation.rb +34 -0
  50. data/lib/sass/script/variable.rb +31 -0
  51. data/lib/sass/tree/comment_node.rb +73 -10
  52. data/lib/sass/tree/debug_node.rb +30 -0
  53. data/lib/sass/tree/directive_node.rb +42 -17
  54. data/lib/sass/tree/file_node.rb +41 -0
  55. data/lib/sass/tree/for_node.rb +48 -0
  56. data/lib/sass/tree/if_node.rb +54 -0
  57. data/lib/sass/tree/mixin_def_node.rb +29 -0
  58. data/lib/sass/tree/mixin_node.rb +48 -0
  59. data/lib/sass/tree/node.rb +214 -11
  60. data/lib/sass/tree/prop_node.rb +109 -0
  61. data/lib/sass/tree/rule_node.rb +178 -51
  62. data/lib/sass/tree/variable_node.rb +34 -0
  63. data/lib/sass/tree/while_node.rb +31 -0
  64. data/test/haml/engine_test.rb +331 -36
  65. data/test/haml/helper_test.rb +12 -1
  66. data/test/haml/results/content_for_layout.xhtml +0 -3
  67. data/test/haml/results/filters.xhtml +2 -0
  68. data/test/haml/results/list.xhtml +1 -1
  69. data/test/haml/template_test.rb +7 -2
  70. data/test/haml/templates/content_for_layout.haml +0 -2
  71. data/test/haml/templates/list.haml +1 -1
  72. data/test/haml/util_test.rb +92 -0
  73. data/test/sass/css2sass_test.rb +69 -24
  74. data/test/sass/engine_test.rb +586 -64
  75. data/test/sass/functions_test.rb +125 -0
  76. data/test/sass/more_results/more1.css +9 -0
  77. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  78. data/test/sass/more_results/more_import.css +29 -0
  79. data/test/sass/more_templates/_more_partial.sass +2 -0
  80. data/test/sass/more_templates/more1.sass +23 -0
  81. data/test/sass/more_templates/more_import.sass +11 -0
  82. data/test/sass/plugin_test.rb +81 -28
  83. data/test/sass/results/line_numbers.css +49 -0
  84. data/test/sass/results/{constants.css → script.css} +4 -4
  85. data/test/sass/results/subdir/subdir.css +2 -0
  86. data/test/sass/results/units.css +11 -0
  87. data/test/sass/script_test.rb +258 -0
  88. data/test/sass/templates/import.sass +1 -1
  89. data/test/sass/templates/importee.sass +7 -2
  90. data/test/sass/templates/line_numbers.sass +13 -0
  91. data/test/sass/templates/{constants.sass → script.sass} +11 -10
  92. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  93. data/test/sass/templates/subdir/subdir.sass +2 -2
  94. data/test/sass/templates/units.sass +11 -0
  95. data/test/test_helper.rb +14 -0
  96. metadata +77 -19
  97. data/FAQ +0 -138
  98. data/README.rdoc +0 -319
  99. data/lib/sass/constant.rb +0 -216
  100. data/lib/sass/constant/color.rb +0 -101
  101. data/lib/sass/constant/literal.rb +0 -54
  102. data/lib/sass/constant/nil.rb +0 -9
  103. data/lib/sass/constant/number.rb +0 -87
  104. data/lib/sass/constant/operation.rb +0 -30
  105. data/lib/sass/constant/string.rb +0 -22
  106. data/lib/sass/tree/attr_node.rb +0 -57
  107. data/lib/sass/tree/value_node.rb +0 -20
@@ -0,0 +1,79 @@
1
+ module Sass
2
+ # The lexical environment for SassScript.
3
+ # This keeps track of variable and mixin definitions.
4
+ #
5
+ # A new environment is created for each level of Sass nesting.
6
+ # This allows variables to be lexically scoped.
7
+ # The new environment refers to the environment in the upper scope,
8
+ # so it has access to variables defined in enclosing scopes,
9
+ # but new variables are defined locally.
10
+ #
11
+ # Environment also keeps track of the {Engine} options
12
+ # so that they can be made available to {Sass::Script::Functions}.
13
+ class Environment
14
+ # The enclosing environment,
15
+ # or nil if this is the global environment.
16
+ #
17
+ # @return [Environment]
18
+ attr_reader :parent
19
+ attr_writer :options
20
+
21
+ # @param parent [Environment] See \{#parent}
22
+ def initialize(parent = nil)
23
+ @vars = {}
24
+ @mixins = {}
25
+ @parent = parent
26
+
27
+ set_var("important", Script::String.new("!important")) unless @parent
28
+ end
29
+
30
+ # The options hash.
31
+ # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
32
+ #
33
+ # @return [Hash<Symbol, Object>]
34
+ def options
35
+ @options || (parent && parent.options) || {}
36
+ end
37
+
38
+ class << self
39
+ private
40
+
41
+ # Note: when updating this,
42
+ # update haml/yard/inherited_hash.rb as well.
43
+ def inherited_hash(name)
44
+ class_eval <<RUBY, __FILE__, __LINE__ + 1
45
+ def #{name}(name)
46
+ @#{name}s[name] || @parent && @parent.#{name}(name)
47
+ end
48
+
49
+ def set_#{name}(name, value)
50
+ @#{name}s[name] = value unless try_set_#{name}(name, value)
51
+ end
52
+
53
+ def try_set_#{name}(name, value)
54
+ if @#{name}s.include?(name)
55
+ @#{name}s[name] = value
56
+ true
57
+ elsif @parent
58
+ @parent.try_set_#{name}(name, value)
59
+ else
60
+ false
61
+ end
62
+ end
63
+ protected :try_set_#{name}
64
+
65
+ def set_local_#{name}(name, value)
66
+ @#{name}s[name] = value
67
+ end
68
+ RUBY
69
+ end
70
+ end
71
+
72
+ # variable
73
+ # Script::Literal
74
+ inherited_hash :var
75
+ # mixin
76
+ # Engine::Mixin
77
+ inherited_hash :mixin
78
+ end
79
+ end
@@ -1,35 +1,57 @@
1
1
  module Sass
2
- # Sass::SyntaxError encapsulates information about the exception,
3
- # such as the line of the Sass template it was raised on
2
+ # An exception class that keeps track of
3
+ # the line of the Sass template it was raised on
4
4
  # and the Sass file that was being parsed (if applicable).
5
- # It also provides a handy way to rescue only exceptions raised
6
- # because of a faulty template.
5
+ #
6
+ # All Sass errors are raised as {Sass::SyntaxError}s.
7
7
  class SyntaxError < StandardError
8
- # The line of the Sass template on which the exception was thrown.
8
+ # The line of the Sass template on which the error occurred.
9
+ #
10
+ # @return [Fixnum]
9
11
  attr_accessor :sass_line
10
12
 
11
13
  # The name of the file that was being parsed when the exception was raised.
12
- # This will be nil unless Sass is being used as an ActionView plugin.
14
+ # This could be `nil` if no filename is available.
15
+ #
16
+ # @return [String]
13
17
  attr_reader :sass_filename
14
18
 
15
- # Creates a new SyntaxError.
16
- # +lineno+ should be the line of the Sass template on which the error occurred.
19
+ # @param msg [String] The error message
20
+ # @param lineno [Fixnum] See \{#sass\_line}
17
21
  def initialize(msg, lineno = nil)
18
22
  @message = msg
19
23
  @sass_line = lineno
20
24
  end
21
25
 
26
+ # Add information about the filename and line on which the error was raised,
27
+ # and re-raises the exception.
28
+ #
29
+ # @param filename [String] See \{#sass\_filename}
30
+ # @param line [Fixnum] See \{#sass\_line}
31
+ # @raise [Sass::SyntaxError] self
32
+ def add_metadata(filename, line)
33
+ self.sass_line ||= line
34
+ add_backtrace_entry(filename) unless sass_filename
35
+ raise self
36
+ end
37
+
22
38
  # Adds a properly formatted entry to the exception's backtrace.
23
- # +filename+ should be the file in which the error occurred,
24
- # if applicable (defaults to "(sass)").
39
+ #
40
+ # @param filename [String] The file in which the error occurred,
41
+ # if applicable (defaults to "(sass)")
25
42
  def add_backtrace_entry(filename) # :nodoc:
26
43
  @sass_filename ||= filename
27
44
  self.backtrace ||= []
28
45
  self.backtrace.unshift "#{@sass_filename || '(sass)'}:#{@sass_line}"
29
46
  end
30
47
 
31
- def to_s # :nodoc:
48
+ # @return [String] The error message
49
+ def to_s
32
50
  @message
33
51
  end
34
52
  end
53
+
54
+ # The class for Sass errors that are raised due to invalid unit conversions
55
+ # in SassScript.
56
+ class UnitConversionError < SyntaxError; end
35
57
  end
@@ -0,0 +1,139 @@
1
+ require 'digest/sha1'
2
+ require 'pathname'
3
+
4
+ module Sass
5
+ # This module contains various bits of functionality
6
+ # related to finding and caching Sass files.
7
+ module Files
8
+ extend self
9
+
10
+ # Returns the {Sass::Tree} for the given file,
11
+ # reading it from the Sass cache if possible.
12
+ #
13
+ # @param filename [String] The path to the Sass file
14
+ # @param options [Hash<Symbol, Object>] The options hash.
15
+ # Only the {file:SASS_REFERENCE.md#cache-option `:cache_location`} option is used
16
+ # @raise [Sass::SyntaxError] if there's an error in the document
17
+ def tree_for(filename, options)
18
+ options = Sass::Engine::DEFAULT_OPTIONS.merge(options)
19
+ text = File.read(filename)
20
+
21
+ if options[:cache]
22
+ compiled_filename = sassc_filename(filename, options)
23
+ sha = Digest::SHA1.hexdigest(text)
24
+
25
+ if root = try_to_read_sassc(filename, compiled_filename, sha)
26
+ root.options = options.merge(:filename => filename)
27
+ return root
28
+ end
29
+ end
30
+
31
+ engine = Sass::Engine.new(text, options.merge(:filename => filename))
32
+
33
+ begin
34
+ root = engine.to_tree
35
+ rescue Sass::SyntaxError => err
36
+ err.add_backtrace_entry(filename)
37
+ raise err
38
+ end
39
+
40
+ try_to_write_sassc(root, compiled_filename, sha, options) if options[:cache]
41
+
42
+ root
43
+ end
44
+
45
+ # Find the full filename of a Sass or CSS file to import.
46
+ # This follows Sass's import rules:
47
+ # if the filename given ends in `".sass"` or `".css"`,
48
+ # it will try to find that type of file;
49
+ # otherwise, it will try to find the corresponding Sass file
50
+ # and fall back on CSS if it's not available.
51
+ #
52
+ # Any Sass filename returned will correspond to
53
+ # an actual Sass file on the filesystem.
54
+ # CSS filenames, however, may not;
55
+ # they're expected to be put through directly to the stylesheet
56
+ # as CSS `@import` statements.
57
+ #
58
+ # @param filename [String] The filename to search for
59
+ # @param load_paths [Array<String>] The set of filesystem paths
60
+ # to search for Sass files.
61
+ # @return [String] The filename of the imported file.
62
+ # This is an absolute path if the file is a `".sass"` file.
63
+ # @raise [Sass::SyntaxError] if `filename` ends in ``".sass"``
64
+ # and no corresponding Sass file could be found.
65
+ def find_file_to_import(filename, load_paths)
66
+ was_sass = false
67
+ original_filename = filename
68
+
69
+ if filename[-5..-1] == ".sass"
70
+ filename = filename[0...-5]
71
+ was_sass = true
72
+ elsif filename[-4..-1] == ".css"
73
+ return filename
74
+ end
75
+
76
+ new_filename = find_full_path("#{filename}.sass", load_paths)
77
+
78
+ return new_filename if new_filename
79
+ return filename + '.css' unless was_sass
80
+ raise SyntaxError.new("File to import not found or unreadable: #{original_filename}.", @line)
81
+ end
82
+
83
+ private
84
+
85
+ def sassc_filename(filename, options)
86
+ File.join(options[:cache_location],
87
+ Digest::SHA1.hexdigest(File.dirname(File.expand_path(filename))),
88
+ File.basename(filename) + 'c')
89
+ end
90
+
91
+ def try_to_read_sassc(filename, compiled_filename, sha)
92
+ return unless File.readable?(compiled_filename)
93
+
94
+ File.open(compiled_filename, "rb") do |f|
95
+ return unless f.readline("\n").strip == Sass::VERSION
96
+ return unless f.readline("\n").strip == sha
97
+ return Marshal.load(f.read)
98
+ end
99
+ rescue TypeError, ArgumentError => e
100
+ warn "Warning. Error encountered while reading cache #{compiled_filename}: #{e}"
101
+ end
102
+
103
+ def try_to_write_sassc(root, compiled_filename, sha, options)
104
+ return unless File.writable?(File.dirname(options[:cache_location]))
105
+ return if File.exists?(options[:cache_location]) && !File.writable?(options[:cache_location])
106
+ return if File.exists?(File.dirname(compiled_filename)) && !File.writable?(File.dirname(compiled_filename))
107
+ return if File.exists?(compiled_filename) && !File.writable?(compiled_filename)
108
+ FileUtils.mkdir_p(File.dirname(compiled_filename))
109
+ File.open(compiled_filename, "wb") do |f|
110
+ f.write(Sass::VERSION)
111
+ f.write("\n")
112
+ f.write(sha)
113
+ f.write("\n")
114
+ f.write(Marshal.dump(root))
115
+ end
116
+ end
117
+
118
+ def find_full_path(filename, load_paths)
119
+ partial_name = File.join(File.dirname(filename), "_#{File.basename(filename)}")
120
+
121
+ if Pathname.new(filename).absolute?
122
+ [partial_name, filename].each do |name|
123
+ return name if File.readable?(name)
124
+ end
125
+ return nil
126
+ end
127
+
128
+ load_paths.each do |path|
129
+ [partial_name, filename].each do |name|
130
+ full_path = File.join(path, name)
131
+ if File.readable?(full_path)
132
+ return full_path
133
+ end
134
+ end
135
+ end
136
+ nil
137
+ end
138
+ end
139
+ end
@@ -1,114 +1,154 @@
1
1
  require 'sass/engine'
2
2
 
3
3
  module Sass
4
- # This module contains methods to aid in using Sass
5
- # as a stylesheet-rendering plugin for various systems.
6
- # Currently Rails/ActionController and Merb are supported out of the box.
4
+ # This module handles the compilation of Sass files.
5
+ # It provides global options and checks whether CSS files
6
+ # need to be updated.
7
+ #
8
+ # This module is used as the primary interface with Sass
9
+ # when it's used as a plugin for various frameworks.
10
+ # Currently Rails and Merb are supported out of the box.
7
11
  module Plugin
8
- class << self
9
- @@options = {
10
- :template_location => './public/stylesheets/sass',
11
- :css_location => './public/stylesheets',
12
- :always_update => false,
13
- :always_check => true,
14
- :full_exception => true
15
- }
16
- @@checked_for_updates = false
17
-
18
- # Whether or not Sass has *ever* checked if the stylesheets need updates
19
- # (in this Ruby instance).
20
- def checked_for_updates
21
- @@checked_for_updates
22
- end
23
-
24
- # Gets various options for Sass. See README.rdoc for details.
25
- #--
26
- # TODO: *DOCUMENT OPTIONS*
27
- #++
28
- def options
29
- @@options
30
- end
31
-
32
- # Sets various options for Sass.
33
- def options=(value)
34
- @@options.merge!(value)
35
- end
12
+ extend self
13
+
14
+ @options = {
15
+ :css_location => './public/stylesheets',
16
+ :always_update => false,
17
+ :always_check => true,
18
+ :full_exception => true
19
+ }
20
+ @checked_for_updates = false
21
+
22
+ # Whether or not Sass has **ever** checked if the stylesheets need to be updated
23
+ # (in this Ruby instance).
24
+ #
25
+ # @return [Boolean]
26
+ attr_reader :checked_for_updates
27
+
28
+ # An options hash.
29
+ # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
30
+ #
31
+ # @return [Hash<Symbol, Object>]
32
+ attr_reader :options
33
+
34
+ # Sets the options hash.
35
+ # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
36
+ #
37
+ # @param value [Hash<Symbol, Object>] The options hash
38
+ def options=(value)
39
+ @options.merge!(value)
40
+ end
36
41
 
37
- # Get the options ready to be passed to the Sass::Engine
38
- def engine_options(additional_options = {})
39
- opts = options.dup.merge(additional_options)
40
- opts[:load_paths] = load_paths(opts)
41
- opts
42
- end
42
+ # Non-destructively modifies \{#options} so that default values are properly set.
43
+ #
44
+ # @param additional_options [Hash<Symbol, Object>] An options hash with which to merge \{#options}
45
+ # @return [Hash<Symbol, Object>] The modified options hash
46
+ def engine_options(additional_options = {})
47
+ opts = options.dup.merge(additional_options)
48
+ opts[:load_paths] = load_paths(opts)
49
+ opts
50
+ end
43
51
 
44
- # Checks each stylesheet in <tt>options[:css_location]</tt>
45
- # to see if it needs updating,
46
- # and updates it using the corresponding template
47
- # from <tt>options[:templates]</tt>
48
- # if it does.
49
- def update_stylesheets
50
- return if options[:never_update]
52
+ # Updates out-of-date stylesheets.
53
+ #
54
+ # Checks each Sass file in {file:SASS_REFERENCE.md#template_location-option `:template_location`}
55
+ # to see if it's been modified more recently than the corresponding CSS file
56
+ # in {file:SASS_REFERENCE.md#css_location-option} `:css_location`}.
57
+ # If it has, it updates the CSS file.
58
+ def update_stylesheets
59
+ return if options[:never_update]
51
60
 
52
- @@checked_for_updates = true
53
- Dir.glob(File.join(options[:template_location], "**", "*.sass")).entries.each do |file|
61
+ @checked_for_updates = true
62
+ template_locations.zip(css_locations).each do |template_location, css_location|
54
63
 
64
+ Dir.glob(File.join(template_location, "**", "*.sass")).each do |file|
55
65
  # Get the relative path to the file with no extension
56
- name = file.sub(options[:template_location] + "/", "")[0...-5]
57
-
58
- if !forbid_update?(name) && (options[:always_update] || stylesheet_needs_update?(name))
59
- css = css_filename(name)
60
- File.delete(css) if File.exists?(css)
61
-
62
- filename = template_filename(name)
63
- engine = Engine.new(File.read(filename), engine_options(:filename => filename))
64
- result = begin
65
- engine.render
66
- rescue Exception => e
67
- exception_string(e)
68
- end
69
-
70
- # Create any directories that might be necessary
71
- dirs = [options[:css_location]]
72
- name.split("/")[0...-1].each { |dir| dirs << "#{dirs[-1]}/#{dir}" }
73
- dirs.each { |dir| Dir.mkdir(dir) unless File.exist?(dir) }
74
-
75
- # Finally, write the file
76
- File.open(css, 'w') do |file|
77
- file.print(result)
78
- end
66
+ name = file.sub(template_location + "/", "")[0...-5]
67
+
68
+ if !forbid_update?(name) && (options[:always_update] || stylesheet_needs_update?(name, template_location, css_location))
69
+ update_stylesheet(name, template_location, css_location)
79
70
  end
80
71
  end
81
72
  end
73
+ end
82
74
 
83
- private
75
+ private
84
76
 
85
- def load_paths(opts = options)
86
- (opts[:load_paths] || []) + [options[:template_location]]
77
+ def update_stylesheet(name, template_location, css_location)
78
+ css = css_filename(name, css_location)
79
+ File.delete(css) if File.exists?(css)
80
+
81
+ filename = template_filename(name, template_location)
82
+ result = begin
83
+ Sass::Files.tree_for(filename, engine_options(:css_filename => css, :filename => filename)).render
84
+ rescue Exception => e
85
+ exception_string(e)
86
+ end
87
+
88
+ # Create any directories that might be necessary
89
+ mkpath(css_location, name)
90
+
91
+ # Finally, write the file
92
+ File.open(css, 'w') do |file|
93
+ file.print(result)
94
+ end
95
+ end
96
+
97
+ # Create any successive directories required to be able to write a file to: File.join(base,name)
98
+ def mkpath(base, name)
99
+ dirs = [base]
100
+ name.split(File::SEPARATOR)[0...-1].each { |dir| dirs << File.join(dirs[-1],dir) }
101
+ dirs.each { |dir| Dir.mkdir(dir) unless File.exist?(dir) }
102
+ end
103
+
104
+ def load_paths(opts = options)
105
+ (opts[:load_paths] || []) + template_locations
106
+ end
107
+
108
+ def template_locations
109
+ location = (options[:template_location] || File.join(options[:css_location],'sass'))
110
+ if location.is_a?(String)
111
+ [location]
112
+ else
113
+ location.to_a.map { |l| l.first }
114
+ end
115
+ end
116
+
117
+ def css_locations
118
+ if options[:template_location] && !options[:template_location].is_a?(String)
119
+ options[:template_location].to_a.map { |l| l.last }
120
+ else
121
+ [options[:css_location]]
87
122
  end
123
+ end
88
124
 
89
- def exception_string(e)
90
- if options[:full_exception]
91
- e_string = "#{e.class}: #{e.message}"
125
+ def exception_string(e)
126
+ if options[:full_exception]
127
+ e_string = "#{e.class}: #{e.message}"
92
128
 
93
- if e.is_a? Sass::SyntaxError
94
- e_string << "\non line #{e.sass_line}"
129
+ if e.is_a? Sass::SyntaxError
130
+ e_string << "\non line #{e.sass_line}"
95
131
 
96
- if e.sass_filename
97
- e_string << " of #{e.sass_filename}"
132
+ if e.sass_filename
133
+ e_string << " of #{e.sass_filename}"
98
134
 
99
- if File.exists?(e.sass_filename)
100
- e_string << "\n\n"
135
+ if File.exists?(e.sass_filename)
136
+ e_string << "\n\n"
101
137
 
102
- min = [e.sass_line - 5, 0].max
138
+ min = [e.sass_line - 5, 0].max
139
+ begin
103
140
  File.read(e.sass_filename).rstrip.split("\n")[
104
141
  min .. e.sass_line + 5
105
142
  ].each_with_index do |line, i|
106
143
  e_string << "#{min + i + 1}: #{line}\n"
107
144
  end
145
+ rescue
146
+ e_string << "Couldn't read sass file: #{e.sass_filename}"
108
147
  end
109
148
  end
110
149
  end
111
- <<END
150
+ end
151
+ <<END
112
152
  /*
113
153
  #{e_string}
114
154
 
@@ -119,49 +159,52 @@ body:before {
119
159
  font-family: monospace;
120
160
  content: "#{e_string.gsub('"', '\"').gsub("\n", '\\A ')}"; }
121
161
  END
122
- # Fix an emacs syntax-highlighting hiccup: '
123
- else
124
- "/* Internal stylesheet error */"
125
- end
162
+ # Fix an emacs syntax-highlighting hiccup: '
163
+ else
164
+ "/* Internal stylesheet error */"
126
165
  end
166
+ end
127
167
 
128
- def template_filename(name)
129
- "#{options[:template_location]}/#{name}.sass"
130
- end
168
+ def template_filename(name, path)
169
+ "#{path}/#{name}.sass"
170
+ end
131
171
 
132
- def css_filename(name)
133
- "#{options[:css_location]}/#{name}.css"
134
- end
172
+ def css_filename(name, path)
173
+ "#{path}/#{name}.css"
174
+ end
135
175
 
136
- def forbid_update?(name)
137
- name.sub(/^.*\//, '')[0] == ?_
138
- end
176
+ def forbid_update?(name)
177
+ name.sub(/^.*\//, '')[0] == ?_
178
+ end
139
179
 
140
- def stylesheet_needs_update?(name)
141
- if !File.exists?(css_filename(name))
142
- return true
143
- else
144
- css_mtime = File.mtime(css_filename(name))
145
- File.mtime(template_filename(name)) > css_mtime ||
146
- dependencies(template_filename(name)).any?(&dependency_updated?(css_mtime))
147
- end
148
- end
180
+ def stylesheet_needs_update?(name, template_path, css_path)
181
+ css_file = css_filename(name, css_path)
182
+ template_file = template_filename(name, template_path)
183
+ exact_stylesheet_needs_update?(css_file, template_file)
184
+ end
149
185
 
150
- def dependency_updated?(css_mtime)
151
- lambda do |dep|
152
- File.mtime(dep) > css_mtime ||
153
- dependencies(dep).any?(&dependency_updated?(css_mtime))
154
- end
155
- end
186
+ def exact_stylesheet_needs_update?(css_file, template_file)
187
+ return true unless File.exists?(css_file)
156
188
 
157
- def dependencies(filename)
158
- File.readlines(filename).grep(/^@import /).map do |line|
159
- line[8..-1].split(',').map do |inc|
160
- Sass::Engine.find_file_to_import(inc.strip, load_paths)
161
- end
162
- end.flatten.grep(/\.sass$/)
189
+ css_mtime = File.mtime(css_file)
190
+ File.mtime(template_file) > css_mtime ||
191
+ dependencies(template_file).any?(&dependency_updated?(css_mtime))
192
+ end
193
+
194
+ def dependency_updated?(css_mtime)
195
+ lambda do |dep|
196
+ File.mtime(dep) > css_mtime ||
197
+ dependencies(dep).any?(&dependency_updated?(css_mtime))
163
198
  end
164
199
  end
200
+
201
+ def dependencies(filename)
202
+ File.readlines(filename).grep(/^@import /).map do |line|
203
+ line[8..-1].split(',').map do |inc|
204
+ Sass::Files.find_file_to_import(inc.strip, [File.dirname(filename)] + load_paths)
205
+ end
206
+ end.flatten.grep(/\.sass$/)
207
+ end
165
208
  end
166
209
  end
167
210