haml 1.8.2 → 2.0.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 (77) hide show
  1. data/FAQ +138 -0
  2. data/MIT-LICENSE +1 -1
  3. data/{README → README.rdoc} +66 -3
  4. data/Rakefile +111 -147
  5. data/VERSION +1 -1
  6. data/bin/css2sass +0 -0
  7. data/bin/haml +0 -0
  8. data/bin/html2haml +0 -0
  9. data/bin/sass +0 -0
  10. data/init.rb +6 -1
  11. data/lib/haml.rb +464 -201
  12. data/lib/haml/buffer.rb +117 -63
  13. data/lib/haml/engine.rb +63 -44
  14. data/lib/haml/error.rb +16 -6
  15. data/lib/haml/exec.rb +37 -7
  16. data/lib/haml/filters.rb +213 -68
  17. data/lib/haml/helpers.rb +95 -60
  18. data/lib/haml/helpers/action_view_extensions.rb +1 -1
  19. data/lib/haml/helpers/action_view_mods.rb +54 -6
  20. data/lib/haml/html.rb +6 -6
  21. data/lib/haml/precompiler.rb +254 -133
  22. data/lib/haml/template.rb +3 -6
  23. data/lib/haml/template/patch.rb +9 -2
  24. data/lib/haml/template/plugin.rb +52 -23
  25. data/lib/sass.rb +157 -12
  26. data/lib/sass/constant.rb +22 -22
  27. data/lib/sass/constant/color.rb +13 -13
  28. data/lib/sass/constant/literal.rb +7 -7
  29. data/lib/sass/constant/number.rb +9 -9
  30. data/lib/sass/constant/operation.rb +4 -4
  31. data/lib/sass/constant/string.rb +3 -3
  32. data/lib/sass/css.rb +104 -31
  33. data/lib/sass/engine.rb +120 -39
  34. data/lib/sass/error.rb +1 -1
  35. data/lib/sass/plugin.rb +14 -3
  36. data/lib/sass/plugin/merb.rb +6 -2
  37. data/lib/sass/tree/attr_node.rb +5 -5
  38. data/lib/sass/tree/directive_node.rb +2 -7
  39. data/lib/sass/tree/node.rb +1 -12
  40. data/lib/sass/tree/rule_node.rb +39 -31
  41. data/lib/sass/tree/value_node.rb +1 -1
  42. data/test/benchmark.rb +67 -80
  43. data/test/haml/engine_test.rb +284 -84
  44. data/test/haml/helper_test.rb +51 -15
  45. data/test/haml/results/content_for_layout.xhtml +1 -2
  46. data/test/haml/results/eval_suppressed.xhtml +2 -4
  47. data/test/haml/results/filters.xhtml +44 -15
  48. data/test/haml/results/helpers.xhtml +2 -3
  49. data/test/haml/results/just_stuff.xhtml +2 -6
  50. data/test/haml/results/nuke_inner_whitespace.xhtml +34 -0
  51. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  52. data/test/haml/results/original_engine.xhtml +3 -7
  53. data/test/haml/results/partials.xhtml +1 -0
  54. data/test/haml/results/tag_parsing.xhtml +1 -6
  55. data/test/haml/results/very_basic.xhtml +2 -4
  56. data/test/haml/results/whitespace_handling.xhtml +13 -21
  57. data/test/haml/template_test.rb +8 -15
  58. data/test/haml/templates/_partial.haml +1 -0
  59. data/test/haml/templates/filters.haml +48 -7
  60. data/test/haml/templates/just_stuff.haml +1 -2
  61. data/test/haml/templates/nuke_inner_whitespace.haml +26 -0
  62. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  63. data/test/haml/templates/tag_parsing.haml +0 -3
  64. data/test/haml/test_helper.rb +15 -0
  65. data/test/sass/engine_test.rb +80 -34
  66. data/test/sass/plugin_test.rb +1 -1
  67. data/test/sass/results/import.css +2 -2
  68. data/test/sass/results/mixins.css +95 -0
  69. data/test/sass/results/multiline.css +24 -0
  70. data/test/sass/templates/import.sass +4 -1
  71. data/test/sass/templates/importee.sass +4 -0
  72. data/test/sass/templates/mixins.sass +76 -0
  73. data/test/sass/templates/multiline.sass +20 -0
  74. metadata +65 -51
  75. data/lib/haml/util.rb +0 -18
  76. data/test/haml/runner.rb +0 -16
  77. data/test/profile.rb +0 -65
@@ -1,19 +1,16 @@
1
1
  require 'haml/engine'
2
- require 'rubygems'
3
- require 'active_support'
4
- require 'action_view'
5
2
 
6
3
  module Haml
7
4
  class Template
8
5
  class << self
9
6
  @@options = {}
10
7
 
11
- # Gets various options for Haml. See README for details.
8
+ # Gets various options for Haml. See README.rdoc for details.
12
9
  def options
13
10
  @@options
14
11
  end
15
12
 
16
- # Sets various options for Haml. See README for details.
13
+ # Sets various options for Haml. See README.rdoc for details.
17
14
  def options=(value)
18
15
  @@options = value
19
16
  end
@@ -38,7 +35,7 @@ if defined?(RAILS_ROOT)
38
35
  # because the new init file is sufficiently flexible
39
36
  # to not need updating.
40
37
  rails_init_file = File.join(RAILS_ROOT, 'vendor', 'plugins', 'haml', 'init.rb')
41
- haml_init_file = File.join(File.dirname(__FILE__), '..', '..', 'init.rb')
38
+ haml_init_file = Haml.scope('init.rb')
42
39
  if File.exists?(rails_init_file)
43
40
  require 'fileutils'
44
41
  FileUtils.cp(haml_init_file, rails_init_file) unless FileUtils.cmp(rails_init_file, haml_init_file)
@@ -26,7 +26,7 @@ module ActionView
26
26
  def compile_haml(template, file_name, local_assigns)
27
27
  render_symbol = assign_method_name(:haml, template, file_name)
28
28
  locals = local_assigns.keys
29
-
29
+
30
30
  @@template_args[render_symbol] ||= {}
31
31
  locals_keys = @@template_args[render_symbol].keys | locals
32
32
  @@template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h }
@@ -42,7 +42,14 @@ module ActionView
42
42
  logger.debug "Backtrace: #{e.backtrace.join("\n")}"
43
43
  end
44
44
 
45
- raise TemplateError.new(@base_path, file_name || template, @assigns, template, e)
45
+ base_path = if defined?(extract_base_path_from)
46
+ # Rails 2.0.x
47
+ extract_base_path_from(file_name) || view_paths.first
48
+ else
49
+ # Rails <=1.2.6
50
+ @base_path
51
+ end
52
+ raise ActionView::TemplateError.new(base_path, file_name || template, @assigns, template, e)
46
53
  end
47
54
 
48
55
  @@compile_time[render_symbol] = Time.now
@@ -3,41 +3,70 @@
3
3
  # using the > 2.0.1 template handler API.
4
4
 
5
5
  module Haml
6
- class Template
6
+ class Plugin < ActionView::TemplateHandler
7
7
  include ActionView::TemplateHandlers::Compilable if defined?(ActionView::TemplateHandlers::Compilable)
8
8
 
9
- def self.line_offset
10
- 1
11
- end
9
+ def compile(template)
10
+ options = Haml::Template.options.dup
12
11
 
13
- def compilable?
14
- true
15
- end
16
-
17
- def line_offset
18
- self.class.line_offset
19
- end
12
+ # template is a template object in Rails >=2.1.0,
13
+ # a source string previously
14
+ if template.respond_to? :source
15
+ options[:filename] = template.filename
16
+ source = template.source
17
+ else
18
+ source = template
19
+ end
20
20
 
21
- def initialize(view)
22
- @view = view
21
+ Haml::Engine.new(source, options).send(:precompiled_with_ambles, [])
23
22
  end
24
23
 
25
- def compile(template)
26
- options = Haml::Template.options.dup
27
- Haml::Engine.new(template, options).send(:precompiled_with_ambles, [])
28
- end
29
-
30
24
  def cache_fragment(block, name = {}, options = nil)
31
25
  @view.fragment_for(block, name, options) do
32
26
  eval("_hamlout.buffer", block.binding)
33
27
  end
34
28
  end
35
-
36
- def read_template_file(template_path, extension)
37
- File.read(template_path)
38
- end
39
29
  end
40
30
  end
41
31
 
42
- ActionView::Base.register_template_handler(:haml, Haml::Template)
32
+ if defined? ActionView::Template and ActionView::Template.respond_to? :register_template_handler
33
+ ActionView::Template
34
+ else
35
+ ActionView::Base
36
+ end.register_template_handler(:haml, Haml::Plugin)
37
+
38
+ # In Rails 2.0.2, ActionView::TemplateError took arguments
39
+ # that we can't fill in from the Haml::Plugin context.
40
+ # Thus, we've got to monkeypatch ActionView::Base to catch the error.
41
+ if ActionView::TemplateError.instance_method(:initialize).arity == 5
42
+ class ActionView::Base
43
+ def compile_template(handler, template, file_name, local_assigns)
44
+ render_symbol = assign_method_name(handler, template, file_name)
45
+
46
+ # Move begin up two lines so it captures compilation exceptions.
47
+ begin
48
+ render_source = create_template_source(handler, template, render_symbol, local_assigns.keys)
49
+ line_offset = @@template_args[render_symbol].size + handler.line_offset
50
+
51
+ file_name = 'compiled-template' if file_name.blank?
52
+ CompiledTemplates.module_eval(render_source, file_name, -line_offset)
53
+ rescue Exception => e # errors from template code
54
+ if logger
55
+ logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
56
+ logger.debug "Function body: #{render_source}"
57
+ logger.debug "Backtrace: #{e.backtrace.join("\n")}"
58
+ end
59
+
60
+ # There's no way to tell Haml about the filename,
61
+ # so we've got to insert it ourselves.
62
+ e.backtrace[0].gsub!('(haml)', file_name) if e.is_a?(Haml::Error)
63
+
64
+ raise ActionView::TemplateError.new(extract_base_path_from(file_name) || view_paths.first, file_name || template, @assigns, template, e)
65
+ end
66
+
67
+ @@compile_time[render_symbol] = Time.now
68
+ # logger.debug "Compiled template #{file_name || template}\n ==> #{render_symbol}" if logger
69
+ end
70
+ end
71
+ end
43
72
  # :startdoc:
@@ -11,7 +11,7 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
11
11
  # and implements various features that are useful
12
12
  # for creating manageable stylesheets.
13
13
  #
14
- # == Features
14
+ # == Features
15
15
  #
16
16
  # * Whitespace active
17
17
  # * Well-formatted output
@@ -32,13 +32,13 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
32
32
  #
33
33
  # To enable it as a Rails plugin,
34
34
  # then run
35
- #
35
+ #
36
36
  # haml --rails path/to/rails/app
37
- #
37
+ #
38
38
  # To enable Sass in Merb,
39
39
  # add
40
40
  #
41
- # dependency "haml"
41
+ # dependency "merb-haml"
42
42
  #
43
43
  # to config/dependencies.rb.
44
44
  #
@@ -128,7 +128,7 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
128
128
  #
129
129
  # By default, either attribute syntax may be used.
130
130
  # If you want to force one or the other,
131
- # see the :attribute_syntax option below.
131
+ # see the <tt>:attribute_syntax</tt> option below.
132
132
  #
133
133
  # === Nested Rules
134
134
  #
@@ -157,12 +157,12 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
157
157
  #
158
158
  # #main
159
159
  # :width 97%
160
- #
160
+ #
161
161
  # p, div
162
162
  # :font-size 2em
163
163
  # a
164
164
  # :font-weight bold
165
- #
165
+ #
166
166
  # pre
167
167
  # :font-size 3em
168
168
  #
@@ -363,6 +363,39 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
363
363
  # #main {
364
364
  # content: string(Hello, "Hubert" Bean.) }
365
365
  #
366
+ # === Optional Assignment
367
+ #
368
+ # You can assign Sass constants if they aren't already assigned
369
+ # using the ||= assignment operator.
370
+ # This means that if the constant has already been assigned to,
371
+ # it won't be re-assigned,
372
+ # but if it doesn't have a value yet,
373
+ # it will be given one.
374
+ # For example:
375
+ #
376
+ # !content = "First content"
377
+ # !content ||= "Second content?"
378
+ #
379
+ # #main
380
+ # content = content
381
+ #
382
+ # is compiled to:
383
+ #
384
+ # #main {
385
+ # content: First content; }
386
+ #
387
+ # However,
388
+ #
389
+ # !content ||= "Second content?"
390
+ #
391
+ # #main
392
+ # content = content
393
+ #
394
+ # is compiled to:
395
+ #
396
+ # #main {
397
+ # content: Second content?; }
398
+ #
366
399
  # === Default Concatenation
367
400
  #
368
401
  # All those plusses and quotes for concatenating strings
@@ -453,6 +486,40 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
453
486
  # might compile to either,
454
487
  # depending on whether a file called "foo.sass" existed.
455
488
  #
489
+ # === @font-face, @media, etc.
490
+ #
491
+ # Sass behaves as you'd expect for normal CSS @-directives.
492
+ # For example:
493
+ #
494
+ # @font-face
495
+ # font-family: "Bitstream Vera Sans"
496
+ # src: url(http://foo.bar/bvs")
497
+ #
498
+ # compiles to:
499
+ #
500
+ # @font-face {
501
+ # font-family: "Bitstream Vera Sans";
502
+ # src: url(http://foo.bar/bvs"); }
503
+ #
504
+ # and
505
+ #
506
+ # @media print
507
+ # #sidebar
508
+ # display: none
509
+ #
510
+ # #main
511
+ # background-color: white
512
+ #
513
+ # compiles to:
514
+ #
515
+ # @media print {
516
+ # #sidebar {
517
+ # display: none; }
518
+ #
519
+ # #main {
520
+ # background-color: white; }
521
+ # }
522
+ #
456
523
  # == Comments
457
524
  #
458
525
  # === Silent Comments
@@ -528,6 +595,84 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
528
595
  # background-image: url(/images/pbj.png);
529
596
  # color: red; }
530
597
  #
598
+ # == Mixins
599
+ #
600
+ # Mixins enable you to define groups of CSS attributes and
601
+ # then include them inline in any number of selectors
602
+ # throughout the document.
603
+ #
604
+ # === Defining a Mixin
605
+ #
606
+ # To define a mixin you use a slightly modified form of selector syntax.
607
+ # For example the 'large-text' mixin is defined as follows:
608
+ #
609
+ # =large-text
610
+ # :font
611
+ # :family Arial
612
+ # :size 20px
613
+ # :weight bold
614
+ # :color #ff0000
615
+ #
616
+ # The initial '=' marks this as a mixin rather than a standard selector.
617
+ # The CSS rules that follow won't be included until the mixin is referenced later on.
618
+ # Anything you can put into a standard selector,
619
+ # you can put into a mixin definition. e.g.
620
+ #
621
+ # =clearfix
622
+ # display: inline-block
623
+ # &:after
624
+ # content: "."
625
+ # display: block
626
+ # height: 0
627
+ # clear: both
628
+ # visibility: hidden
629
+ # * html &
630
+ # height: 1px
631
+ #
632
+ #
633
+ # === Mixing it in
634
+ #
635
+ # Inlining a defined mixin is simple,
636
+ # just prepend a '+' symbol to the name of a mixin defined earlier in the document.
637
+ # So to inline the 'large-text' defined earlier,
638
+ # we include the statment '+large-text' in our selector definition thus:
639
+ #
640
+ # .page-title
641
+ # +large-text
642
+ # :padding 4px
643
+ # :margin
644
+ # :top 10px
645
+ #
646
+ #
647
+ # This will produce the following CSS output:
648
+ #
649
+ # .page-title {
650
+ # font-family: Arial;
651
+ # font-size: 20px;
652
+ # font-weight: bold;
653
+ # color: #ff0000;
654
+ # padding: 4px;
655
+ # margin-top: 10px;
656
+ # }
657
+ #
658
+ # Any number of mixins may be defined and there is no limit on
659
+ # the number that can be included in a particular selector.
660
+ #
661
+ # Mixin definitions can also include references to other mixins defined earlier in the file.
662
+ # E.g.
663
+ #
664
+ # =highlighted-background
665
+ # background:
666
+ # color: #fc0
667
+ # =header-text
668
+ # font:
669
+ # size: 20px
670
+ #
671
+ # =compound
672
+ # +highlighted-background
673
+ # +header-text
674
+ #
675
+ #
531
676
  # == Output Style
532
677
  #
533
678
  # Although the default CSS style that Sass outputs is very nice,
@@ -601,7 +746,7 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
601
746
  # #main { color: #fff; background-color: #000; }
602
747
  # #main p { width: 10em; }
603
748
  #
604
- # .huge { font-size: 10em; font-weight: bold; text-decoration: underline; }
749
+ # .huge { font-size: 10em; font-weight: bold; text-decoration: underline; }
605
750
  #
606
751
  # === <tt>:compressed</tt>
607
752
  #
@@ -611,7 +756,7 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
611
756
  # It's not meant to be human-readable.
612
757
  # For example:
613
758
  #
614
- # #main{color:#fff;background-color:#000}#main p{width:10em}.huge{font-size:10em;font-weight:bold;text-decoration:underline}
759
+ # #main{color:#fff;background-color:#000}#main p{width:10em}.huge{font-size:10em;font-weight:bold;text-decoration:underline}
615
760
  #
616
761
  # == Sass Options
617
762
  #
@@ -634,7 +779,7 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
634
779
  # For example: <tt>color: #0f3</tt>
635
780
  # or <tt>width = !main_width</tt>.
636
781
  # By default, either syntax is valid.
637
- #
782
+ #
638
783
  # [<tt>:never_update</tt>] Whether the CSS files should never be updated,
639
784
  # even if the template file changes.
640
785
  # Setting this to true may give small performance gains.
@@ -646,7 +791,7 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
646
791
  # as opposed to only when the template has been modified.
647
792
  # Defaults to false.
648
793
  # Only has meaning within Ruby on Rails or Merb.
649
- #
794
+ #
650
795
  # [<tt>:always_check</tt>] Whether a Sass template should be checked for updates every
651
796
  # time a controller is accessed,
652
797
  # as opposed to only when the Rails server starts.
@@ -681,7 +826,7 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
681
826
  # for Sass templates imported with the "@import" directive.
682
827
  # This defaults to the working directory and, in Rails or Merb,
683
828
  # whatever <tt>:template_location</tt> is.
684
- #
829
+ #
685
830
  module Sass; end
686
831
 
687
832
  require 'sass/engine'
@@ -8,13 +8,13 @@ module Sass
8
8
 
9
9
  # Whitespace characters
10
10
  WHITESPACE = [?\ , ?\t, ?\n, ?\r]
11
-
11
+
12
12
  # The character used to escape values
13
13
  ESCAPE_CHAR = ?\\
14
14
 
15
15
  # The character used to open and close strings
16
16
  STRING_CHAR = ?"
17
-
17
+
18
18
  # A mapping of syntactically-significant characters
19
19
  # to parsed symbols
20
20
  SYMBOLS = {
@@ -31,14 +31,14 @@ module Sass
31
31
  }
32
32
 
33
33
  # The regular expression used to parse constants
34
- MATCH = /^#{Regexp.escape(CONSTANT_CHAR.chr)}([^\s#{(SYMBOLS.keys + [ ?= ]).map {|c| Regexp.escape("#{c.chr}") }.join}]+)\s*=\s*(.+)/
35
-
34
+ MATCH = /^#{Regexp.escape(CONSTANT_CHAR.chr)}([^\s#{(SYMBOLS.keys + [ ?= ]).map {|c| Regexp.escape("#{c.chr}") }.join}]+)\s*((?:\|\|)?=)\s*(.+)/
35
+
36
36
  # First-order operations
37
37
  FIRST_ORDER = [:times, :div, :mod]
38
-
38
+
39
39
  # Second-order operations
40
40
  SECOND_ORDER = [:plus, :minus]
41
-
41
+
42
42
  class << self
43
43
  def parse(value, constants, line)
44
44
  begin
@@ -46,28 +46,28 @@ module Sass
46
46
  rescue Sass::SyntaxError => e
47
47
  if e.message == "Constant arithmetic error"
48
48
  e.instance_eval do
49
- @message += ": #{value.dump}"
49
+ @message += ": #{value.dump}."
50
50
  end
51
51
  end
52
52
  e.sass_line = line
53
53
  raise e
54
54
  end
55
55
  end
56
-
56
+
57
57
  private
58
-
58
+
59
59
  def tokenize(value)
60
60
  escaped = false
61
61
  is_string = false
62
62
  beginning_of_token = true
63
63
  str = ''
64
64
  to_return = []
65
-
65
+
66
66
  reset_str = Proc.new do
67
67
  to_return << str unless str.empty?
68
68
  ''
69
69
  end
70
-
70
+
71
71
  value.each_byte do |byte|
72
72
  unless escaped
73
73
  if byte == ESCAPE_CHAR
@@ -97,7 +97,7 @@ module Sass
97
97
  str = reset_str.call
98
98
  next
99
99
  end
100
-
100
+
101
101
  symbol = SYMBOLS[byte]
102
102
 
103
103
  # Adjacent values without an operator should be concatenated
@@ -135,28 +135,28 @@ module Sass
135
135
  end
136
136
  end
137
137
  end
138
-
138
+
139
139
  escaped = false
140
140
  beginning_of_token = false
141
141
  str << byte.chr
142
142
  end
143
-
143
+
144
144
  if is_string
145
- raise Sass::SyntaxError.new("Unterminated string: #{value.dump}")
145
+ raise Sass::SyntaxError.new("Unterminated string: #{value.dump}.")
146
146
  end
147
147
  str = reset_str.call
148
148
  to_return
149
149
  end
150
-
150
+
151
151
  def parenthesize(value)
152
152
  parenthesize_helper(0, value, value.length)[0]
153
153
  end
154
-
154
+
155
155
  def parenthesize_helper(i, value, value_len, return_after_expr = false)
156
156
  to_return = []
157
157
  beginning = i
158
158
  token = value[i]
159
-
159
+
160
160
  while i < value_len && token != :close
161
161
  if token == :open
162
162
  to_return.push(*value[beginning...i])
@@ -194,13 +194,13 @@ module Sass
194
194
  else
195
195
  i += 1
196
196
  end
197
-
197
+
198
198
  token = value[i]
199
199
  end
200
200
  to_return.push(*value[beginning...i])
201
201
  return to_return, i + 1
202
202
  end
203
-
203
+
204
204
  #--
205
205
  # TODO: Don't pass around original value;
206
206
  # have Constant.parse automatically add it to exception.
@@ -234,10 +234,10 @@ module Sass
234
234
  end
235
235
  end
236
236
  end
237
-
237
+
238
238
  def get_constant(value, constants)
239
239
  to_return = constants[value]
240
- raise SyntaxError.new("Undefined constant: \"!#{value}\"") unless to_return
240
+ raise SyntaxError.new("Undefined constant: \"!#{value}\".") unless to_return
241
241
  to_return
242
242
  end
243
243
  end