haml 1.5.2 → 1.7.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 (66) hide show
  1. data/MIT-LICENSE +1 -1
  2. data/Rakefile +1 -0
  3. data/VERSION +1 -1
  4. data/bin/css2sass +7 -0
  5. data/bin/html2haml +0 -82
  6. data/lib/haml.rb +43 -6
  7. data/lib/haml/buffer.rb +81 -72
  8. data/lib/haml/engine.rb +240 -110
  9. data/lib/haml/exec.rb +120 -5
  10. data/lib/haml/helpers.rb +88 -3
  11. data/lib/haml/helpers/action_view_extensions.rb +45 -0
  12. data/lib/haml/helpers/action_view_mods.rb +30 -17
  13. data/lib/haml/html.rb +173 -0
  14. data/lib/haml/template.rb +1 -26
  15. data/lib/haml/util.rb +18 -0
  16. data/lib/sass.rb +181 -3
  17. data/lib/sass/constant.rb +38 -9
  18. data/lib/sass/constant/color.rb +25 -1
  19. data/lib/sass/constant/literal.rb +10 -8
  20. data/lib/sass/css.rb +197 -0
  21. data/lib/sass/engine.rb +239 -68
  22. data/lib/sass/error.rb +2 -2
  23. data/lib/sass/plugin.rb +11 -3
  24. data/lib/sass/tree/attr_node.rb +25 -17
  25. data/lib/sass/tree/comment_node.rb +14 -0
  26. data/lib/sass/tree/node.rb +18 -1
  27. data/lib/sass/tree/rule_node.rb +17 -5
  28. data/lib/sass/tree/value_node.rb +4 -0
  29. data/test/haml/engine_test.rb +42 -25
  30. data/test/haml/helper_test.rb +28 -3
  31. data/test/haml/results/eval_suppressed.xhtml +6 -0
  32. data/test/haml/results/helpers.xhtml +26 -2
  33. data/test/haml/results/helpful.xhtml +2 -0
  34. data/test/haml/results/just_stuff.xhtml +17 -2
  35. data/test/haml/results/standard.xhtml +1 -1
  36. data/test/haml/results/whitespace_handling.xhtml +1 -11
  37. data/test/haml/rhtml/standard.rhtml +1 -1
  38. data/test/haml/template_test.rb +7 -2
  39. data/test/haml/templates/eval_suppressed.haml +7 -2
  40. data/test/haml/templates/helpers.haml +16 -1
  41. data/test/haml/templates/helpful.haml +2 -0
  42. data/test/haml/templates/just_stuff.haml +23 -4
  43. data/test/haml/templates/standard.haml +3 -3
  44. data/test/haml/templates/whitespace_handling.haml +0 -50
  45. data/test/sass/engine_test.rb +35 -10
  46. data/test/sass/plugin_test.rb +10 -6
  47. data/test/sass/results/alt.css +4 -0
  48. data/test/sass/results/complex.css +4 -3
  49. data/test/sass/results/constants.css +3 -3
  50. data/test/sass/results/import.css +27 -0
  51. data/test/sass/results/nested.css +7 -0
  52. data/test/sass/results/parent_ref.css +13 -0
  53. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  54. data/test/sass/results/subdir/subdir.css +1 -0
  55. data/test/sass/templates/alt.sass +16 -0
  56. data/test/sass/templates/bork2.sass +2 -0
  57. data/test/sass/templates/complex.sass +19 -1
  58. data/test/sass/templates/constants.sass +8 -0
  59. data/test/sass/templates/import.sass +8 -0
  60. data/test/sass/templates/importee.sass +10 -0
  61. data/test/sass/templates/nested.sass +8 -0
  62. data/test/sass/templates/parent_ref.sass +25 -0
  63. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  64. data/test/sass/templates/subdir/subdir.sass +6 -0
  65. metadata +95 -75
  66. data/test/haml/results/semantic.cache +0 -15
@@ -40,7 +40,6 @@ module Haml
40
40
  # to render its templates.
41
41
  def initialize(view)
42
42
  @view = view
43
- @@precompiled_templates ||= {}
44
43
  end
45
44
 
46
45
  # Renders the file at the location <tt>template</tt>,
@@ -60,13 +59,7 @@ module Haml
60
59
  engine = Haml::Engine.new(template, options)
61
60
  else
62
61
  options[:filename] ||= template
63
- if @precompiled = get_precompiled(template)
64
- options[:precompiled] ||= @precompiled
65
- engine = Haml::Engine.new("", options)
66
- else
67
- engine = Haml::Engine.new(File.read(template), options)
68
- set_precompiled(template, engine.precompiled)
69
- end
62
+ engine = Haml::Engine.new(File.read(template), options)
70
63
  end
71
64
 
72
65
  yield_proc = @view.instance_eval do
@@ -76,24 +69,6 @@ module Haml
76
69
  engine.to_html(@view) { |*args| yield_proc.call(*args) }
77
70
 
78
71
  end
79
-
80
- private
81
-
82
- # Gets the cached, precompiled version of the template at location <tt>filename</tt>
83
- # as a string.
84
- def get_precompiled(filename)
85
- # Do we have it on file? Is it new enough?
86
- if (precompiled, precompiled_on = @@precompiled_templates[filename]) &&
87
- (precompiled_on == File.mtime(filename).to_i)
88
- precompiled
89
- end
90
- end
91
-
92
- # Sets the cached, precompiled version of the template at location <tt>filename</tt>
93
- # to <tt>precompiled</tt>.
94
- def set_precompiled(filename, precompiled)
95
- @@precompiled_templates[filename] = [precompiled, File.mtime(filename).to_i]
96
- end
97
72
  end
98
73
  end
99
74
 
@@ -0,0 +1,18 @@
1
+ # This file contains various useful bits of code
2
+ # that are shared between Haml and Sass.
3
+
4
+ class Hash # :nodoc:
5
+ # Same as Hash#merge!,
6
+ # but recursively merges sub-hashes
7
+ def rec_merge!(other)
8
+ other.each do |key, value|
9
+ myval = self[key]
10
+ if value.is_a?(Hash) && myval.is_a?(Hash)
11
+ myval.rec_merge!(value)
12
+ else
13
+ self[key] = value
14
+ end
15
+ end
16
+ self
17
+ end
18
+ end
@@ -77,12 +77,27 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
77
77
  #
78
78
  # === Attributes
79
79
  #
80
- # The syntax for attributes is also slightly different.
80
+ # There are two different ways to write CSS attrbibutes.
81
+ # The first is very similar to the how you're used to writing them:
82
+ # with a colon between the name and the value.
83
+ # However, Sass attributes don't have semicolons at the end;
84
+ # each attribute is on its own line, so they aren't necessary.
85
+ # For example:
86
+ #
87
+ # #main p
88
+ # color: #00ff00
89
+ # width: 97%
90
+ #
91
+ # is compiled to:
92
+ #
93
+ # #main p {
94
+ # color: #00ff00;
95
+ # width: 97% }
96
+ #
97
+ # The second syntax for attributes is slightly different.
81
98
  # The colon is at the beginning of the attribute,
82
99
  # rather than between the name and the value,
83
100
  # so it's easier to tell what elements are attributes just by glancing at them.
84
- # Attributes also don't have semicolons at the end;
85
- # each attribute is on its own line, so they aren't necessary.
86
101
  # For example:
87
102
  #
88
103
  # #main p
@@ -142,6 +157,57 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
142
157
  # #main pre {
143
158
  # font-size: 3em; }
144
159
  #
160
+ # === Referencing Parent Rules
161
+ #
162
+ # In addition to the default behavior of inserting the parent selector
163
+ # as a CSS parent of the current selector
164
+ # (e.g. above, "#main" is the parent of "p"),
165
+ # you can have more fine-grained control over what's done with the parent selector
166
+ # by using the ampersand character "&" in your selectors.
167
+ #
168
+ # The ampersand is automatically replaced by the parent selector,
169
+ # instead of having it prepended.
170
+ # This allows you to cleanly create pseudo-attributes:
171
+ #
172
+ # a
173
+ # :font-weight bold
174
+ # :text-decoration none
175
+ # &:hover
176
+ # :text-decoration underline
177
+ # &:visited
178
+ # :font-weight normal
179
+ #
180
+ # Which would become:
181
+ #
182
+ # a {
183
+ # font-weight: bold;
184
+ # text-decoration: none; }
185
+ # a:hover {
186
+ # text-decoration: underline; }
187
+ # a:visited {
188
+ # font-weight: normal; }
189
+ #
190
+ # It also allows you to add selectors at the base of the hierarchy,
191
+ # which can be useuful for targeting certain styles to certain browsers:
192
+ #
193
+ # #main
194
+ # :width 90%
195
+ # #sidebar
196
+ # :float left
197
+ # :margin-left 20%
198
+ # .ie6 &
199
+ # :margin-left 40%
200
+ #
201
+ # Which would become:
202
+ #
203
+ # #main {
204
+ # width: 90%; }
205
+ # #main #sidebar {
206
+ # float: left;
207
+ # margin-left: 20%; }
208
+ # .ie6 #main #sidebar {
209
+ # margin-left: 40%; }
210
+ #
145
211
  # === Attribute Namespaces
146
212
  #
147
213
  # CSS has quite a few attributes that are in "namespaces;"
@@ -232,6 +298,10 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
232
298
  #
233
299
  # === Colors
234
300
  #
301
+ # Colors may be written as three- or six-digit hex numbers prefixed
302
+ # by a pound sign (#), or as HTML4 color names. For example,
303
+ # "#ff0", "#ffff00" and "yellow" all refer to the same color.
304
+ #
235
305
  # Not only can arithmetic be done between colors and other colors,
236
306
  # but it can be done between colors and normal numbers.
237
307
  # In this case, the operation is done piecewise one each of the
@@ -301,6 +371,108 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
301
371
  # #main h6 {
302
372
  # font: italic small-caps bold 1.1em sans-serif; }
303
373
  #
374
+ # == Directives
375
+ #
376
+ # Directives allow the author to directly issue instructions to the Sass compiler.
377
+ # They're prefixed with an at sign, "<tt>@</tt>",
378
+ # followed by the name of the directive,
379
+ # a space, and any arguments to it -
380
+ # just like CSS directives.
381
+ # For example:
382
+ #
383
+ # @import red.sass
384
+ #
385
+ # === Import
386
+ #
387
+ # Currently, the only directive is the "import" directive.
388
+ # It works in a very similar way to the CSS import directive,
389
+ # and sometimes compiles to a literal CSS "@import".
390
+ #
391
+ # Sass can import either other Sass files or plain CSS files.
392
+ # If it imports a Sass file,
393
+ # not only are the rules from that file included,
394
+ # but all constants in that file are made available in the current file.
395
+ #
396
+ # Sass looks for other Sass files in the working directory,
397
+ # and the Sass file directory under Rails.
398
+ # Additional search directories may be specified
399
+ # using the :load_paths option (see below).
400
+ #
401
+ # Sass can also import plain CSS files.
402
+ # In this case, it doesn't literally include the content of the files;
403
+ # rather, it uses the built-in CSS "@import" directive to tell the client program
404
+ # to import the files.
405
+ #
406
+ # The import directive can take either a full filename
407
+ # or a filename without an extension.
408
+ # If an extension isn't provided,
409
+ # Sass will try to find a Sass file with the given basename in the load paths,
410
+ # and, failing that, will assume a relevant CSS file will be available.
411
+ #
412
+ # For example,
413
+ #
414
+ # @import foo.sass
415
+ #
416
+ # would compile to
417
+ #
418
+ # .foo
419
+ # :color #f00
420
+ #
421
+ # whereas
422
+ #
423
+ # @import foo.css
424
+ #
425
+ # would compile to
426
+ #
427
+ # @import foo.css
428
+ #
429
+ # Finally,
430
+ #
431
+ # @import foo
432
+ #
433
+ # might compile to either,
434
+ # depending on whether a file called "foo.sass" existed.
435
+ #
436
+ # == Comments
437
+ #
438
+ # === Silent Comments
439
+ #
440
+ # It's simple to add "silent" comments,
441
+ # which don't output anything to the CSS document,
442
+ # to a Sass document.
443
+ # Simply use the familiar C-style notation for a one-line comment, "//",
444
+ # at the normal indentation level and all text following it won't be output.
445
+ # For example:
446
+ #
447
+ # // A very awesome rule.
448
+ # #awesome.rule
449
+ # // An equally awesome attribute.
450
+ # :awesomeness very
451
+ #
452
+ # becomes
453
+ #
454
+ # #awesome.rule {
455
+ # awesomeness: very; }
456
+ #
457
+ # === Loud Comments
458
+ #
459
+ # "Loud" comments are just as easy as silent ones.
460
+ # These comments output to the document as CSS comments,
461
+ # and thus use the same opening sequence: "/*".
462
+ # For example:
463
+ #
464
+ # /* A very awesome rule.
465
+ # #awesome.rule
466
+ # /* An equally awesome attribute.
467
+ # :awesomeness very
468
+ #
469
+ # becomes
470
+ #
471
+ # /* A very awesome rule. */
472
+ # #awesome.rule {
473
+ # /* An equally awesome attribute. */
474
+ # awesomeness: very; }
475
+ #
304
476
  # == Output Style
305
477
  #
306
478
  # Although the default CSS style that Sass outputs is very nice,
@@ -412,6 +584,12 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
412
584
  # [<tt>:filename</tt>] The filename of the file being rendered.
413
585
  # This is used solely for reporting errors,
414
586
  # and is automatically set when using Rails.
587
+ #
588
+ # [<tt>:load_paths</tt>] An array of filesystem paths which should be searched
589
+ # for Sass templates imported with the "@import" directive.
590
+ # This defaults to the working directory and, in Rails,
591
+ # whatever <tt>:template_location</tt> is
592
+ # (by default <tt>RAILS_ROOT + "/public/stylesheets/sass"</tt>).
415
593
  #
416
594
  module Sass; end
417
595
 
@@ -76,9 +76,11 @@ module Sass
76
76
 
77
77
  last = to_return[-1]
78
78
 
79
+ # Do we need to open or close a string literal?
79
80
  if byte == STRING_CHAR
80
81
  is_string = !is_string
81
82
 
83
+ # Adjacent strings should be concatenated
82
84
  if is_string && last && (!last.is_a?(Symbol) || last == :close)
83
85
  to_return << :concat
84
86
  end
@@ -89,6 +91,7 @@ module Sass
89
91
 
90
92
  unless is_string
91
93
 
94
+ # Are we looking at whitespace?
92
95
  if WHITESPACE.include?(byte)
93
96
  str = reset_str.call
94
97
  next
@@ -96,13 +99,21 @@ module Sass
96
99
 
97
100
  symbol = SYMBOLS[byte]
98
101
 
102
+ # Adjacent values without an operator should be concatenated
99
103
  if (symbol.nil? || symbol == :open) &&
100
104
  last && (!last.is_a?(Symbol) || last == :close)
101
- # Two values connected without an operator
102
105
  to_return << :concat
103
106
  end
104
107
 
105
- if symbol && !(negative_okay && symbol == :minus)
108
+ # Time for a unary minus!
109
+ if negative_okay && symbol == :minus
110
+ negative_okay = true
111
+ to_return << :neg
112
+ next
113
+ end
114
+
115
+ # Are we looking at an operator?
116
+ if symbol && (str.empty? || symbol != :mod)
106
117
  str = reset_str.call
107
118
  negative_okay = true
108
119
  to_return << symbol
@@ -132,12 +143,25 @@ module Sass
132
143
  beginning = i
133
144
  token = value[i]
134
145
 
135
- while i < value_len && token != :close
146
+ while i < value_len && token != :close
136
147
  if token == :open
137
148
  to_return.push(*value[beginning...i])
138
149
  sub, i = parenthesize_helper(i + 1, value, value_len)
139
150
  beginning = i
140
151
  to_return << sub
152
+ elsif token == :neg
153
+ if value[i + 1].nil?
154
+ raise Sass::SyntaxError("Unterminated unary minus.")
155
+ elsif value[i + 1] == :open
156
+ to_return.push(*value[beginning...i])
157
+ sub, i = parenthesize_helper(i + 2, value, value_len)
158
+ beginning = i
159
+ to_return << [:neg, sub]
160
+ else
161
+ to_return.push(*value[beginning...i])
162
+ to_return << [:neg, value[i + 1]]
163
+ beginning = i = i + 2
164
+ end
141
165
  else
142
166
  i += 1
143
167
  end
@@ -154,17 +178,22 @@ module Sass
154
178
  #++
155
179
  def operationalize(value, constants)
156
180
  value = [value] unless value.is_a?(Array)
157
- length = value.length
158
- if length == 1
181
+ if value.length == 1
159
182
  value = value[0]
160
- if value.is_a? Operation
183
+ if value.is_a? Array
184
+ operationalize(value, constants)
185
+ elsif value.is_a? Operation
161
186
  value
162
187
  else
163
188
  Literal.parse(insert_constant(value, constants))
164
189
  end
165
- elsif length == 2
166
- raise SyntaxError.new("Constant arithmetic error")
167
- elsif length == 3
190
+ elsif value.length == 2
191
+ if value[0] == :neg
192
+ Operation.new(Sass::Constant::Number.new('0'), operationalize(value[1], constants), :minus)
193
+ else
194
+ raise SyntaxError.new("Constant arithmetic error")
195
+ end
196
+ elsif value.length == 3
168
197
  Operation.new(operationalize(value[0], constants), operationalize(value[2], constants), value[1])
169
198
  else
170
199
  if SECOND_ORDER.include?(value[1]) && FIRST_ORDER.include?(value[3])
@@ -2,11 +2,35 @@ require 'sass/constant/literal'
2
2
 
3
3
  module Sass::Constant # :nodoc:
4
4
  class Color < Literal # :nodoc:
5
+
6
+ HTML4_COLORS = {
7
+ 'black' => 0x000000,
8
+ 'silver' => 0xc0c0c0,
9
+ 'gray' => 0x808080,
10
+ 'white' => 0xffffff,
11
+ 'maroon' => 0x800000,
12
+ 'red' => 0xff0000,
13
+ 'purple' => 0x800080,
14
+ 'fuchsia' => 0xff00ff,
15
+ 'green' => 0x008000,
16
+ 'lime' => 0x00ff00,
17
+ 'olive' => 0x808000,
18
+ 'yellow' => 0xffff00,
19
+ 'navy' => 0x000080,
20
+ 'blue' => 0x0000ff,
21
+ 'teal' => 0x008080,
22
+ 'aqua' => 0x00ffff
23
+ }
5
24
 
6
25
  REGEXP = /\##{"([0-9a-fA-F]{1,2})" * 3}/
7
26
 
8
27
  def parse(value)
9
- @value = value.scan(REGEXP)[0].map { |num| num.ljust(2, 'f').to_i(16) }
28
+ if (value =~ REGEXP)
29
+ @value = value.scan(REGEXP)[0].map { |num| num.ljust(2, num).to_i(16) }
30
+ else
31
+ color = HTML4_COLORS[value.downcase]
32
+ @value = (0..2).map{ |n| color >> (n << 3) & 0xff }.reverse
33
+ end
10
34
  end
11
35
 
12
36
  def plus(other)
@@ -7,19 +7,21 @@ require 'sass/constant/color'
7
7
 
8
8
  class Sass::Constant::Literal # :nodoc:
9
9
  # The regular expression matching numbers.
10
- NUMBER = /^(-?[0-9]*?\.?)([0-9]+)([^0-9\s]*)$/
10
+ NUMBER = /^(-?\d*?\.?)(\d+)([^\d\s]*)$/
11
+
12
+ html_color_matcher = Sass::Constant::Color::HTML4_COLORS.keys.join '|'
11
13
 
12
14
  # The regular expression matching colors.
13
- COLOR = /^\#(#{"[0-9a-fA-F]" * 3}|#{"[0-9a-fA-F]" * 6})/
15
+ COLOR = /^\# (?: [\da-f]{3} | [\da-f]{6} ) | #{html_color_matcher}/ix
14
16
 
15
17
  def self.parse(value)
16
18
  case value
17
- when NUMBER
18
- Sass::Constant::Number.new(value)
19
- when COLOR
20
- Sass::Constant::Color.new(value)
21
- else
22
- Sass::Constant::String.new(value)
19
+ when NUMBER
20
+ Sass::Constant::Number.new(value)
21
+ when COLOR
22
+ Sass::Constant::Color.new(value)
23
+ else
24
+ Sass::Constant::String.new(value)
23
25
  end
24
26
  end
25
27