haml 1.8.0 → 2.0.3

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 (89) hide show
  1. data/FAQ +138 -0
  2. data/MIT-LICENSE +1 -1
  3. data/{README → README.rdoc} +66 -3
  4. data/REVISION +1 -0
  5. data/Rakefile +115 -147
  6. data/VERSION +1 -1
  7. data/bin/css2sass +0 -0
  8. data/bin/haml +2 -1
  9. data/bin/html2haml +0 -0
  10. data/bin/sass +0 -0
  11. data/init.rb +6 -1
  12. data/lib/haml/buffer.rb +122 -64
  13. data/lib/haml/engine.rb +77 -46
  14. data/lib/haml/error.rb +15 -6
  15. data/lib/haml/exec.rb +61 -10
  16. data/lib/haml/filters.rb +229 -74
  17. data/lib/haml/helpers/action_view_extensions.rb +1 -1
  18. data/lib/haml/helpers/action_view_mods.rb +109 -24
  19. data/lib/haml/helpers.rb +137 -76
  20. data/lib/haml/html.rb +8 -8
  21. data/lib/haml/precompiler.rb +280 -153
  22. data/lib/haml/template/patch.rb +10 -3
  23. data/lib/haml/template/plugin.rb +61 -10
  24. data/lib/haml/template.rb +14 -9
  25. data/lib/haml.rb +483 -214
  26. data/lib/sass/constant/color.rb +13 -13
  27. data/lib/sass/constant/literal.rb +8 -7
  28. data/lib/sass/constant/nil.rb +9 -0
  29. data/lib/sass/constant/number.rb +10 -10
  30. data/lib/sass/constant/operation.rb +4 -4
  31. data/lib/sass/constant/string.rb +3 -3
  32. data/lib/sass/constant.rb +46 -77
  33. data/lib/sass/css.rb +130 -56
  34. data/lib/sass/engine.rb +131 -43
  35. data/lib/sass/plugin/merb.rb +48 -12
  36. data/lib/sass/plugin/rails.rb +10 -4
  37. data/lib/sass/plugin.rb +33 -10
  38. data/lib/sass/tree/attr_node.rb +5 -5
  39. data/lib/sass/tree/directive_node.rb +2 -7
  40. data/lib/sass/tree/node.rb +1 -12
  41. data/lib/sass/tree/rule_node.rb +39 -31
  42. data/lib/sass/tree/value_node.rb +1 -1
  43. data/lib/sass.rb +194 -19
  44. data/rails/init.rb +1 -0
  45. data/test/benchmark.rb +67 -80
  46. data/test/haml/engine_test.rb +368 -152
  47. data/test/haml/helper_test.rb +68 -16
  48. data/test/haml/html2haml_test.rb +3 -4
  49. data/test/haml/results/content_for_layout.xhtml +1 -2
  50. data/test/haml/results/eval_suppressed.xhtml +2 -4
  51. data/test/haml/results/filters.xhtml +38 -30
  52. data/test/haml/results/helpers.xhtml +4 -8
  53. data/test/haml/results/just_stuff.xhtml +8 -7
  54. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  55. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  56. data/test/haml/results/original_engine.xhtml +3 -7
  57. data/test/haml/results/partials.xhtml +1 -0
  58. data/test/haml/results/tag_parsing.xhtml +1 -6
  59. data/test/haml/results/very_basic.xhtml +2 -4
  60. data/test/haml/results/whitespace_handling.xhtml +13 -21
  61. data/test/haml/template_test.rb +42 -57
  62. data/test/haml/templates/_partial.haml +1 -0
  63. data/test/haml/templates/filters.haml +39 -21
  64. data/test/haml/templates/helpers.haml +10 -10
  65. data/test/haml/templates/just_stuff.haml +8 -3
  66. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  67. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  68. data/test/haml/templates/partials.haml +1 -1
  69. data/test/haml/templates/tag_parsing.haml +0 -3
  70. data/test/haml/templates/whitespace_handling.haml +10 -10
  71. data/test/sass/engine_test.rb +97 -39
  72. data/test/sass/plugin_test.rb +4 -7
  73. data/test/sass/results/constants.css +2 -0
  74. data/test/sass/results/import.css +2 -2
  75. data/test/sass/results/mixins.css +95 -0
  76. data/test/sass/results/multiline.css +24 -0
  77. data/test/sass/templates/constants.sass +3 -0
  78. data/test/sass/templates/import.sass +4 -1
  79. data/test/sass/templates/importee.sass +4 -0
  80. data/test/sass/templates/mixins.sass +76 -0
  81. data/test/sass/templates/multiline.sass +20 -0
  82. data/test/test_helper.rb +18 -0
  83. metadata +70 -53
  84. data/lib/haml/helpers/action_view_mods.rb.rej +0 -30
  85. data/lib/haml/util.rb +0 -18
  86. data/lib/sass/constant.rb.rej +0 -42
  87. data/test/haml/runner.rb +0 -16
  88. data/test/profile.rb +0 -65
  89. data/test/sass/engine_test.rb.rej +0 -18
@@ -1,20 +1,56 @@
1
1
  unless defined?(Sass::MERB_LOADED)
2
2
  Sass::MERB_LOADED = true
3
3
 
4
- Sass::Plugin.options.merge!(:template_location => MERB_ROOT + '/public/stylesheets/sass',
5
- :css_location => MERB_ROOT + '/public/stylesheets',
6
- :always_check => MERB_ENV != "production",
7
- :full_exception => MERB_ENV != "production")
4
+ version = Merb::VERSION.split('.').map { |n| n.to_i }
5
+ if version[0] <= 0 && version[1] < 5
6
+ root = MERB_ROOT
7
+ env = MERB_ENV
8
+ else
9
+ root = Merb.root.to_s
10
+ env = Merb.environment
11
+ end
12
+
13
+ Sass::Plugin.options.merge!(:template_location => root + '/public/stylesheets/sass',
14
+ :css_location => root + '/public/stylesheets',
15
+ :always_check => env != "production",
16
+ :full_exception => env != "production")
8
17
  config = Merb::Plugins.config[:sass] || Merb::Plugins.config["sass"] || {}
9
- config.symbolize_keys!
18
+
19
+ if defined? config.symbolize_keys!
20
+ config.symbolize_keys!
21
+ end
22
+
10
23
  Sass::Plugin.options.merge!(config)
11
-
12
- class MerbHandler # :nodoc:
13
- def process_with_sass(request, response)
14
- Sass::Plugin.update_stylesheets if Sass::Plugin.options[:always_update] || Sass::Plugin.options[:always_check]
15
- process_without_sass(request, response)
24
+
25
+ if version[0] > 0 || version[1] >= 9
26
+
27
+ class Merb::Rack::Application # :nodoc:
28
+ def call_with_sass(env)
29
+ if !Sass::Plugin.checked_for_updates ||
30
+ Sass::Plugin.options[:always_update] || Sass::Plugin.options[:always_check]
31
+ Sass::Plugin.update_stylesheets
32
+ end
33
+
34
+ call_without_sass(env)
35
+ end
36
+ alias_method :call_without_sass, :call
37
+ alias_method :call, :call_with_sass
38
+ end
39
+
40
+ else
41
+
42
+ class MerbHandler # :nodoc:
43
+ def process_with_sass(request, response)
44
+ if !Sass::Plugin.checked_for_updates ||
45
+ Sass::Plugin.options[:always_update] || Sass::Plugin.options[:always_check]
46
+ Sass::Plugin.update_stylesheets
47
+ end
48
+
49
+ process_without_sass(request, response)
50
+ end
51
+ alias_method :process_without_sass, :process
52
+ alias_method :process, :process_with_sass
16
53
  end
17
- alias_method :process_without_sass, :process
18
- alias_method :process, :process_with_sass
54
+
19
55
  end
20
56
  end
@@ -5,14 +5,20 @@ unless defined?(Sass::RAILS_LOADED)
5
5
  :css_location => RAILS_ROOT + '/public/stylesheets',
6
6
  :always_check => RAILS_ENV != "production",
7
7
  :full_exception => RAILS_ENV != "production")
8
-
9
- module ActionController # :nodoc:
10
- class Base # :nodoc:
8
+
9
+ # :stopdoc:
10
+ module ActionController
11
+ class Base
11
12
  alias_method :sass_old_process, :process
12
13
  def process(*args)
13
- Sass::Plugin.update_stylesheets if Sass::Plugin.options[:always_update] || Sass::Plugin.options[:always_check]
14
+ if !Sass::Plugin.checked_for_updates ||
15
+ Sass::Plugin.options[:always_update] || Sass::Plugin.options[:always_check]
16
+ Sass::Plugin.update_stylesheets
17
+ end
18
+
14
19
  sass_old_process(*args)
15
20
  end
16
21
  end
17
22
  end
23
+ # :startdoc:
18
24
  end
data/lib/sass/plugin.rb CHANGED
@@ -13,8 +13,15 @@ module Sass
13
13
  :always_check => true,
14
14
  :full_exception => true
15
15
  }
16
+ @@checked_for_updates = false
16
17
 
17
- # Gets various options for Sass. See README for details.
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.
18
25
  #--
19
26
  # TODO: *DOCUMENT OPTIONS*
20
27
  #++
@@ -27,6 +34,13 @@ module Sass
27
34
  @@options.merge!(value)
28
35
  end
29
36
 
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
43
+
30
44
  # Checks each stylesheet in <tt>options[:css_location]</tt>
31
45
  # to see if it needs updating,
32
46
  # and updates it using the corresponding template
@@ -35,6 +49,7 @@ module Sass
35
49
  def update_stylesheets
36
50
  return if options[:never_update]
37
51
 
52
+ @@checked_for_updates = true
38
53
  Dir.glob(File.join(options[:template_location], "**", "*.sass")).entries.each do |file|
39
54
 
40
55
  # Get the relative path to the file with no extension
@@ -45,10 +60,7 @@ module Sass
45
60
  File.delete(css) if File.exists?(css)
46
61
 
47
62
  filename = template_filename(name)
48
- l_options = @@options.dup
49
- l_options[:filename] = filename
50
- l_options[:load_paths] = load_paths
51
- engine = Engine.new(File.read(filename), l_options)
63
+ engine = Engine.new(File.read(filename), engine_options(:filename => filename))
52
64
  result = begin
53
65
  engine.render
54
66
  rescue Exception => e
@@ -56,7 +68,7 @@ module Sass
56
68
  end
57
69
 
58
70
  # Create any directories that might be necessary
59
- dirs = [l_options[:css_location]]
71
+ dirs = [options[:css_location]]
60
72
  name.split("/")[0...-1].each { |dir| dirs << "#{dirs[-1]}/#{dir}" }
61
73
  dirs.each { |dir| Dir.mkdir(dir) unless File.exist?(dir) }
62
74
 
@@ -70,8 +82,8 @@ module Sass
70
82
 
71
83
  private
72
84
 
73
- def load_paths
74
- (options[:load_paths] || []) + [options[:template_location]]
85
+ def load_paths(opts = options)
86
+ (opts[:load_paths] || []) + [options[:template_location]]
75
87
  end
76
88
 
77
89
  def exception_string(e)
@@ -96,7 +108,18 @@ module Sass
96
108
  end
97
109
  end
98
110
  end
99
- "/*\n#{e_string}\n\nBacktrace:\n#{e.backtrace.join("\n")}\n*/"
111
+ <<END
112
+ /*
113
+ #{e_string}
114
+
115
+ Backtrace:\n#{e.backtrace.join("\n")}
116
+ */
117
+ body:before {
118
+ white-space: pre;
119
+ font-family: monospace;
120
+ content: "#{e_string.gsub('"', '\"').gsub("\n", '\\A ')}"; }
121
+ END
122
+ # Fix an emacs syntax-highlighting hiccup: '
100
123
  else
101
124
  "/* Internal stylesheet error */"
102
125
  end
@@ -111,7 +134,7 @@ module Sass
111
134
  end
112
135
 
113
136
  def forbid_update?(name)
114
- name[0] == ?_
137
+ name.sub(/^.*\//, '')[0] == ?_
115
138
  end
116
139
 
117
140
  def stylesheet_needs_update?(name)
@@ -3,21 +3,21 @@ require 'sass/tree/node'
3
3
  module Sass::Tree
4
4
  class AttrNode < ValueNode
5
5
  attr_accessor :name
6
-
6
+
7
7
  def initialize(name, value, style)
8
8
  @name = name
9
9
  super(value, style)
10
10
  end
11
-
11
+
12
12
  def to_s(tabs, parent_name = nil)
13
13
  if value[-1] == ?;
14
- raise Sass::SyntaxError.new("Invalid attribute: #{declaration.dump} (This isn't CSS!)", @line)
14
+ raise Sass::SyntaxError.new("Invalid attribute: #{declaration.dump} (This isn't CSS!).", @line)
15
15
  end
16
16
  real_name = name
17
17
  real_name = "#{parent_name}-#{real_name}" if parent_name
18
18
 
19
19
  if value.empty? && children.empty?
20
- raise Sass::SyntaxError.new("Invalid attribute: #{declaration.dump}", @line)
20
+ raise Sass::SyntaxError.new("Invalid attribute: #{declaration.dump}.", @line)
21
21
  end
22
22
 
23
23
  join_string = case @style
@@ -34,7 +34,7 @@ module Sass::Tree
34
34
  children.each do |kid|
35
35
  to_return << "#{kid.to_s(tabs, real_name)}" << join_string
36
36
  end
37
-
37
+
38
38
  (@style == :compressed && parent_name) ? to_return : to_return[0...-1]
39
39
  end
40
40
 
@@ -15,11 +15,6 @@ module Sass::Tree
15
15
  was_attr = false
16
16
  first = true
17
17
  children.each do |child|
18
- if child.is_a?(RuleNode) && child.continued?
19
- check_multiline_rule(child)
20
- continued_rule = true
21
- end
22
-
23
18
  if @style == :compact
24
19
  if child.is_a?(AttrNode)
25
20
  result << "#{child.to_s(first || was_attr ? 1 : tabs + 1)} "
@@ -32,12 +27,12 @@ module Sass::Tree
32
27
  result << rendered
33
28
  end
34
29
  was_attr = child.is_a?(AttrNode)
35
- first = continued_rule
30
+ first = false
36
31
  elsif @style == :compressed
37
32
  result << (was_attr ? ";#{child.to_s(1)}" : child.to_s(1))
38
33
  was_attr = child.is_a?(AttrNode)
39
34
  else
40
- result << child.to_s(tabs + 1) + (continued_rule ? '' : "\n")
35
+ result << child.to_s(tabs + 1) + "\n"
41
36
  end
42
37
  end
43
38
  result.rstrip + if @style == :compressed
@@ -16,15 +16,12 @@ module Sass
16
16
  end
17
17
  @children << child
18
18
  end
19
-
19
+
20
20
  def to_s
21
21
  result = String.new
22
22
  children.each do |child|
23
23
  if child.is_a? AttrNode
24
24
  raise SyntaxError.new('Attributes aren\'t allowed at the root of a document.', child.line)
25
- elsif child.is_a?(RuleNode) && child.continued?
26
- check_multiline_rule(child)
27
- result << child.to_s(1)
28
25
  else
29
26
  result << "#{child.to_s(1)}" + (@style == :compressed ? '' : "\n")
30
27
  end
@@ -32,14 +29,6 @@ module Sass
32
29
  @style == :compressed ? result+"\n" : result[0...-1]
33
30
  end
34
31
 
35
- protected
36
-
37
- def check_multiline_rule(rule)
38
- unless rule.children.empty?
39
- raise SyntaxError.new('Rules can\'t end in commas.', rule.line)
40
- end
41
- end
42
-
43
32
  private
44
33
 
45
34
  # This method should be overridden by subclasses to return an error message
@@ -9,33 +9,50 @@ module Sass::Tree
9
9
  alias_method :rule, :value
10
10
  alias_method :rule=, :value=
11
11
 
12
+ def rules
13
+ Array(rule)
14
+ end
15
+
16
+ def add_rules(node)
17
+ self.rule = rules
18
+ self.rule += node.rules
19
+ end
20
+
12
21
  def continued?
13
22
  rule[-1] == ?,
14
23
  end
15
-
24
+
16
25
  def to_s(tabs, super_rules = nil)
17
26
  attributes = []
18
27
  sub_rules = []
19
28
 
20
- # Save this because the comma's removed by the super_rule additions
21
- was_continued = continued?
22
-
29
+ rule_split = /\s*,\s*/
30
+ rule_separator = @style == :compressed ? ',' : ', '
31
+ line_separator = [:nested, :expanded].include?(@style) ? ",\n" : rule_separator
32
+ rule_indent = ' ' * (tabs - 1)
23
33
  total_rule = if super_rules
24
- super_rules.split(/,\s*/).collect! do |s|
25
- self.rule.split(/,\s*/).collect do |r|
26
- if r.include?(PARENT)
27
- r.gsub(PARENT, s)
28
- else
29
- "#{s} #{r}"
30
- end
31
- end.join(", ")
32
- end.join(", ") + (was_continued ? ',' : '')
33
- elsif self.rule.include?(PARENT)
34
- raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '#{PARENT}'", line)
34
+ super_rules.split(",\n").map do |super_line|
35
+ super_line.strip.split(rule_split).map do |super_rule|
36
+ self.rules.map do |line|
37
+ rule_indent + line.gsub(/,$/, '').split(rule_split).map do |rule|
38
+ if rule.include?(PARENT)
39
+ rule.gsub(PARENT, super_rule)
40
+ else
41
+ "#{super_rule} #{rule}"
42
+ end
43
+ end.join(rule_separator)
44
+ end.join(line_separator)
45
+ end.join(rule_separator)
46
+ end.join(line_separator)
47
+ elsif self.rules.any? { |r| r.include?(PARENT) }
48
+ raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '#{PARENT}'.", line)
35
49
  else
36
- self.rule
50
+ per_rule_indent, total_indent = [:nested, :expanded].include?(@style) ? [rule_indent, ''] : ['', rule_indent]
51
+ total_indent + self.rules.map do |r|
52
+ per_rule_indent + r.gsub(/,$/, '').gsub(rule_split, rule_separator).rstrip
53
+ end.join(line_separator)
37
54
  end
38
-
55
+
39
56
  children.each do |child|
40
57
  if child.is_a? RuleNode
41
58
  sub_rules << child
@@ -43,38 +60,29 @@ module Sass::Tree
43
60
  attributes << child
44
61
  end
45
62
  end
46
-
63
+
47
64
  to_return = ''
48
65
  if !attributes.empty?
49
66
  old_spaces = ' ' * (tabs - 1)
50
67
  spaces = ' ' * tabs
51
68
  if @style == :compact
52
69
  attributes = attributes.map { |a| a.to_s(1) }.join(' ')
53
- to_return << "#{old_spaces}#{total_rule} { #{attributes} }\n"
70
+ to_return << "#{total_rule} { #{attributes} }\n"
54
71
  elsif @style == :compressed
55
72
  attributes = attributes.map { |a| a.to_s(1) }.join(';')
56
73
  to_return << "#{total_rule}{#{attributes}}"
57
74
  else
58
75
  attributes = attributes.map { |a| a.to_s(tabs + 1) }.join("\n")
59
76
  end_attrs = (@style == :expanded ? "\n" + old_spaces : ' ')
60
- to_return << "#{old_spaces}#{total_rule} {\n#{attributes}#{end_attrs}}\n"
77
+ to_return << "#{total_rule} {\n#{attributes}#{end_attrs}}\n"
61
78
  end
62
- elsif continued?
63
- to_return << (' ' * (tabs - 1)) + total_rule + case @style
64
- when :compressed; ''
65
- when :compact; ' '
66
- else "\n"
67
- end
68
79
  end
69
-
80
+
70
81
  tabs += 1 unless attributes.empty? || @style != :nested
71
82
  sub_rules.each do |sub|
72
- if sub.continued?
73
- check_multiline_rule(sub)
74
- end
75
-
76
83
  to_return << sub.to_s(tabs, total_rule)
77
84
  end
85
+
78
86
  to_return
79
87
  end
80
88
  end
@@ -3,7 +3,7 @@ require 'sass/tree/node'
3
3
  module Sass::Tree
4
4
  class ValueNode < Node
5
5
  attr_accessor :value
6
-
6
+
7
7
  def initialize(value, style)
8
8
  @value = value
9
9
  super(style)