haml-edge 2.1.44 → 2.1.45

Sign up to get free protection for your applications and to get access to all the features.
data/EDGE_GEM_VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.44
1
+ 2.1.45
data/README.md CHANGED
@@ -57,11 +57,15 @@ check out the [YARD documentation](http://haml-lang.com/docs/yardoc).
57
57
  ### Haml
58
58
 
59
59
  The most basic element of Haml
60
- is a shorthand for creating HTML
60
+ is a shorthand for creating HTML:
61
61
 
62
62
  %tagname{:attr1 => 'value1', :attr2 => 'value2'} Contents
63
63
 
64
64
  No end-tag is needed; Haml handles that automatically.
65
+ If you prefer HTML-style attributes, you can also use:
66
+
67
+ %tagname(attr1='value1' attr2='value2') Contents
68
+
65
69
  Adding `class` and `id` attributes is even easier.
66
70
  Haml uses the same syntax as the CSS that styles the document:
67
71
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.44
1
+ 2.1.45
data/extra/haml-mode.el CHANGED
@@ -61,8 +61,12 @@ if the next line could be nested within this line.
61
61
  The function can also return a positive integer to indicate
62
62
  a specific level to which the current line could be indented.")
63
63
 
64
+ (defconst haml-tag-beg-re
65
+ "^ *\\(?:[%\\.#][a-z0-9_:\\-]*\\)+\\(?:(.*)\\|{.*}\\|\\[.*\\]\\)*"
66
+ "A regexp matching the beginning of a Haml tag, through (), {}, and [].")
67
+
64
68
  (defvar haml-block-openers
65
- `("^ *\\([%\\.#][a-z0-9_:\\-]*\\)+\\({.*}\\)?\\(\\[.*\\]\\)?[><]*[ \t]*$"
69
+ `(,(concat haml-tag-beg-re "[><]*[ \t]*$")
66
70
  "^ *[&!]?[-=~].*do[ \t]*\\(|.*|[ \t]*\\)?$"
67
71
  ,(concat "^ *[&!]?[-=~][ \t]*\\("
68
72
  (regexp-opt '("if" "unless" "while" "until" "else"
@@ -122,53 +126,73 @@ For example, this will highlight all of the following:
122
126
  %p= 'baz'
123
127
  %p{:foo => 'bar'}[@bar]= 'baz'"
124
128
  (when (re-search-forward "^ *[%.#]" limit t)
125
- (let ((eol (save-excursion (end-of-line) (point))))
129
+ (forward-char -1)
130
+
131
+ ;; Highlight tag, classes, and ids
132
+ (while (haml-move "\\([.#%]\\)[a-z0-9_:\\-]*")
133
+ (put-text-property (match-beginning 0) (match-end 0) 'face
134
+ (case (char-after (match-beginning 1))
135
+ (?% font-lock-function-name-face)
136
+ (?# font-lock-keyword-face)
137
+ (?. font-lock-type-face))))
138
+
139
+ (block loop
140
+ (while t
141
+ (let ((eol (save-excursion (end-of-line) (point))))
142
+ (case (char-after)
143
+ ;; Highlight obj refs
144
+ (?\[
145
+ (let ((beg (point)))
146
+ (haml-limited-forward-sexp eol)
147
+ (haml-fontify-region-as-ruby beg (point))))
148
+ ;; Highlight new attr hashes
149
+ (?\(
150
+ (forward-char 1)
151
+ (while
152
+ (and (haml-parse-new-attr-hash
153
+ (lambda (type beg end)
154
+ (case type
155
+ (name (put-text-property beg end 'face font-lock-constant-face))
156
+ (value (haml-fontify-region-as-ruby beg end)))))
157
+ (not (eobp)))
158
+ (forward-line 1)
159
+ (beginning-of-line)))
160
+ ;; Highlight old attr hashes
161
+ (?\{
162
+ (let ((beg (point)))
163
+ (haml-limited-forward-sexp eol)
164
+
165
+ ;; Check for multiline
166
+ (while (and (eolp) (eq (char-before) ?,) (not (eobp)))
167
+ (forward-line)
168
+ (let ((eol (save-excursion (end-of-line) (point))))
169
+ ;; If no sexps are closed,
170
+ ;; we're still continuing a multiline hash
171
+ (if (>= (car (parse-partial-sexp (point) eol)) 0)
172
+ (end-of-line)
173
+ ;; If sexps have been closed,
174
+ ;; set the point at the end of the total sexp
175
+ (goto-char beg)
176
+ (haml-limited-forward-sexp eol))))
177
+
178
+ (haml-fontify-region-as-ruby (+ 1 beg) (point))))
179
+ (t (return-from loop))))))
180
+
181
+ ;; Move past end chars
182
+ (when (looking-at "[<>&!]+") (goto-char (match-end 0)))
183
+ ;; Highlight script
184
+ (if (looking-at "\\([=~]\\) \\(.*\\)$")
185
+ (haml-fontify-region-as-ruby (match-beginning 2) (match-end 2))
186
+ ;; Give font-lock something to highlight
126
187
  (forward-char -1)
188
+ (looking-at "\\(\\)"))
189
+ t))
127
190
 
128
- ;; Highlight tag, classes, and ids
129
- (while (looking-at "[.#%][a-z0-9_:\\-]*")
130
- (put-text-property (match-beginning 0) (match-end 0) 'face
131
- (case (char-after)
132
- (?% font-lock-function-name-face)
133
- (?# font-lock-keyword-face)
134
- (?. font-lock-type-face)))
135
- (goto-char (match-end 0)))
136
-
137
- ;; Highlight obj refs
138
- (when (eq (char-after) ?\[)
139
- (let ((beg (point)))
140
- (haml-limited-forward-sexp eol)
141
- (haml-fontify-region-as-ruby beg (point))))
142
-
143
- ;; Highlight attr hashes
144
- (when (eq (char-after) ?\{)
145
- (let ((beg (point)))
146
- (haml-limited-forward-sexp eol)
147
-
148
- ;; Check for multiline
149
- (while (and (eolp) (eq (char-before) ?,) (not (eobp)))
150
- (forward-line)
151
- (let ((eol (save-excursion (end-of-line) (point))))
152
- ;; If no sexps are closed,
153
- ;; we're still continuing a multiline hash
154
- (if (>= (car (parse-partial-sexp (point) eol)) 0)
155
- (end-of-line)
156
- ;; If sexps have been closed,
157
- ;; set the point at the end of the total sexp
158
- (goto-char beg)
159
- (haml-limited-forward-sexp eol))))
160
-
161
- (haml-fontify-region-as-ruby (+ 1 beg) (point))))
162
-
163
- ;; Move past end chars
164
- (when (looking-at "[<>&!]+") (goto-char (match-end 0)))
165
- ;; Highlight script
166
- (if (looking-at "\\([=~]\\) \\(.*\\)$")
167
- (haml-fontify-region-as-ruby (match-beginning 2) (match-end 2))
168
- ;; Give font-lock something to highlight
169
- (forward-char -1)
170
- (looking-at "\\(\\)"))
171
- t)))
191
+ (defun haml-move (re)
192
+ "Try matching and moving to the end of a regular expression."
193
+ (when (looking-at re)
194
+ (goto-char (match-end 0))
195
+ t))
172
196
 
173
197
  (defun haml-highlight-interpolation (limit)
174
198
  "Highlight Ruby interpolation (#{foo})."
@@ -441,10 +465,8 @@ character of the next line."
441
465
  "Returns t if the current line can have lines nested beneath it."
442
466
  (let ((attr-props (haml-parse-multiline-attr-hash)))
443
467
  (when attr-props
444
- (end-of-line)
445
468
  (return-from haml-indent-p
446
- (if (eq (char-before) ?,) (cdr (assq 'hash-indent attr-props))
447
- (beginning-of-line)
469
+ (if (haml-unclosed-attr-hash-p) (cdr (assq 'hash-indent attr-props))
448
470
  (list (+ (cdr (assq 'indent attr-props)) haml-indent-offset) nil)))))
449
471
  (loop for opener in haml-block-openers
450
472
  if (looking-at opener) return t
@@ -464,20 +486,51 @@ beginning the hash."
464
486
  (save-excursion
465
487
  (while t
466
488
  (beginning-of-line)
467
- (if (looking-at "^ *\\(?:[.#%][a-z0-9_:\\-]+\\)+{")
489
+ (if (looking-at (eval-when-compile (concat haml-tag-beg-re "\\([{(]\\)")))
468
490
  (progn
469
491
  (goto-char (- (match-end 0) 1))
470
492
  (haml-limited-forward-sexp (save-excursion (end-of-line) (point)))
471
493
  (return-from haml-parse-multiline-attr-hash
472
- (if (eq (char-before) ?,)
473
- `((indent . ,(current-indentation))
474
- (hash-indent . ,(- (match-end 0) (match-beginning 0)))
475
- (point . ,(match-beginning 0)))
476
- nil)))
494
+ (when (or (string-equal (match-string 1) "(") (eq (char-before) ?,))
495
+ `((indent . ,(current-indentation))
496
+ (hash-indent . ,(- (match-end 0) (match-beginning 0)))
497
+ (point . ,(match-beginning 0))))))
498
+ (when (bobp) (return-from haml-parse-multiline-attr-hash))
477
499
  (forward-line -1)
478
- (end-of-line)
479
- (when (not (eq (char-before) ?,))
480
- (return-from haml-parse-multiline-attr-hash nil))))))
500
+ (unless (haml-unclosed-attr-hash-p)
501
+ (return-from haml-parse-multiline-attr-hash))))))
502
+
503
+ (defun* haml-unclosed-attr-hash-p ()
504
+ "Return t if this line has an unclosed attribute hash, new or old."
505
+ (save-excursion
506
+ (end-of-line)
507
+ (when (eq (char-before) ?,) (return-from haml-unclosed-attr-hash-p t))
508
+ (re-search-backward "(\\|^")
509
+ (haml-move "(")
510
+ (haml-parse-new-attr-hash)))
511
+
512
+ (defun* haml-parse-new-attr-hash (&optional (fn (lambda (type beg end) ())))
513
+ "Parse a new-style attribute hash on this line, and returns
514
+ t if it's not finished on the current line.
515
+
516
+ FN should take three parameters: TYPE, BEG, and END.
517
+ TYPE is the type of text parsed ('name or 'value)
518
+ and BEG and END delimit that text in the buffer."
519
+ (let ((eol (save-excursion (end-of-line) (point))))
520
+ (while (not (haml-move ")"))
521
+ (haml-move " *")
522
+ (unless (haml-move "[a-z0-9_:\\-]+")
523
+ (return-from haml-parse-new-attr-hash (haml-move " *$")))
524
+ (funcall fn 'name (match-beginning 0) (match-end 0))
525
+ (haml-move " *")
526
+ (when (haml-move "=")
527
+ (haml-move " *")
528
+ (unless (looking-at "[\"'@a-z]") (return-from haml-parse-new-attr-hash))
529
+ (let ((beg (point)))
530
+ (haml-limited-forward-sexp eol)
531
+ (funcall fn 'value beg (point)))
532
+ (haml-move " *")))
533
+ nil))
481
534
 
482
535
  (defun haml-compute-indentation ()
483
536
  "Calculate the maximum sensible indentation for the current line."
data/lib/haml/buffer.rb CHANGED
@@ -252,7 +252,7 @@ RUBY
252
252
 
253
253
  if to['class'] && from['class']
254
254
  # Make sure we don't duplicate class names
255
- from['class'] = (from['class'].split(' ') | to['class'].split(' ')).join(' ')
255
+ from['class'] = (from['class'].split(' ') | to['class'].split(' ')).sort.join(' ')
256
256
  elsif to['class'] || from['class']
257
257
  from['class'] ||= to['class']
258
258
  end
@@ -456,8 +456,6 @@ END
456
456
  end
457
457
 
458
458
  def parse_static_hash(text)
459
- return {} unless text
460
-
461
459
  attributes = {}
462
460
  scanner = StringScanner.new(text)
463
461
  scanner.scan(/\s+/)
@@ -512,21 +510,38 @@ END
512
510
  def parse_tag(line)
513
511
  raise SyntaxError.new("Invalid tag: \"#{line}\".") unless match = line.scan(/%([-:\w]+)([-\w\.\#]*)(.*)/)[0]
514
512
  tag_name, attributes, rest = match
515
- attributes_hash, rest, last_line = parse_attributes(rest) if rest[0] == ?{
513
+ new_attributes_hash = old_attributes_hash = last_line = object_ref = nil
514
+ attributes_hashes = []
515
+ while rest
516
+ case rest[0]
517
+ when ?{
518
+ break if old_attributes_hash
519
+ old_attributes_hash, rest, last_line = parse_old_attributes(rest)
520
+ attributes_hashes << [:old, old_attributes_hash]
521
+ when ?(
522
+ break if new_attributes_hash
523
+ new_attributes_hash, rest, last_line = parse_new_attributes(rest)
524
+ attributes_hashes << [:new, new_attributes_hash]
525
+ when ?[
526
+ break if object_ref
527
+ object_ref, rest = balance(rest, ?[, ?])
528
+ else; break
529
+ end
530
+ end
531
+
516
532
  if rest
517
- object_ref, rest = balance(rest, ?[, ?]) if rest[0] == ?[
518
- attributes_hash, rest, last_line = parse_attributes(rest) if rest[0] == ?{ && attributes_hash.nil?
519
533
  nuke_whitespace, action, value = rest.scan(/(<>|><|[><])?([=\/\~&!])?(.*)?/)[0]
520
534
  nuke_whitespace ||= ''
521
535
  nuke_outer_whitespace = nuke_whitespace.include? '>'
522
536
  nuke_inner_whitespace = nuke_whitespace.include? '<'
523
537
  end
538
+
524
539
  value = value.to_s.strip
525
- [tag_name, attributes, attributes_hash, object_ref, nuke_outer_whitespace,
540
+ [tag_name, attributes, attributes_hashes, object_ref, nuke_outer_whitespace,
526
541
  nuke_inner_whitespace, action, value, last_line || @index]
527
542
  end
528
543
 
529
- def parse_attributes(line)
544
+ def parse_old_attributes(line)
530
545
  line = line.dup
531
546
  last_line = @index
532
547
 
@@ -547,10 +562,77 @@ END
547
562
  return attributes_hash, rest, last_line
548
563
  end
549
564
 
565
+ def parse_new_attributes(line)
566
+ line = line.dup
567
+ scanner = StringScanner.new(line)
568
+ last_line = @index
569
+ attributes = {}
570
+
571
+ scanner.scan(/\(\s*/)
572
+ until (name, value = parse_new_attribute(scanner)).first.nil?
573
+ if name == false
574
+ text = (Haml::Shared.balance(line, ?(, ?)) || [line]).first
575
+ raise Haml::SyntaxError.new("Invalid attribute list: #{text.inspect}.", last_line - 1)
576
+ end
577
+ attributes[name] = value
578
+ scanner.scan(/\s*/)
579
+
580
+ if scanner.eos?
581
+ line << " " << @next_line.text
582
+ last_line += 1
583
+ next_line
584
+ scanner.scan(/\s*/)
585
+ end
586
+ end
587
+
588
+ static_attributes = {}
589
+ dynamic_attributes = "{"
590
+ attributes.each do |name, (type, val)|
591
+ if type == :static
592
+ static_attributes[name] = val
593
+ else
594
+ dynamic_attributes << name.inspect << " => " << val << ","
595
+ end
596
+ end
597
+ dynamic_attributes << "}"
598
+ dynamic_attributes = nil if dynamic_attributes == "{}"
599
+
600
+ return [static_attributes, dynamic_attributes], scanner.rest, last_line
601
+ end
602
+
603
+ def parse_new_attribute(scanner)
604
+ unless name = scanner.scan(/[-:\w]+/)
605
+ return if scanner.scan(/\)/)
606
+ return false
607
+ end
608
+
609
+ scanner.scan(/\s*/)
610
+ return name, [:static, true] unless scanner.scan(/=/) #/end
611
+
612
+ scanner.scan(/\s*/)
613
+ unless quote = scanner.scan(/["']/)
614
+ return false unless var = scanner.scan(/(@@?|\$)?\w+/)
615
+ return name, [:dynamic, var]
616
+ end
617
+
618
+ re = /((?:\\.|\#[^{]|[^#{quote}\\#])*)(#{quote}|#\{)/
619
+ content = []
620
+ loop do
621
+ return false unless scanner.scan(re)
622
+ content << [:str, scanner[1].gsub(/\\(.)/, '\1')]
623
+ break if scanner[2] == quote
624
+ content << [:ruby, balance(scanner, ?{, ?}, 1).first[0...-1]]
625
+ end
626
+
627
+ return name, [:static, content.first[1]] if content.size == 1
628
+ return name, [:dynamic,
629
+ '"' + content.map {|(t, v)| t == :str ? v.inspect[1...-1] : "\#{#{v}}"}.join + '"']
630
+ end
631
+
550
632
  # Parses a line that will render as an XHTML tag, and adds the code that will
551
633
  # render that tag to <tt>@precompiled</tt>.
552
634
  def render_tag(line)
553
- tag_name, attributes, attributes_hash, object_ref, nuke_outer_whitespace,
635
+ tag_name, attributes, attributes_hashes, object_ref, nuke_outer_whitespace,
554
636
  nuke_inner_whitespace, action, value, last_line = parse_tag(line)
555
637
 
556
638
  raise SyntaxError.new("Illegal element: classes and ids must have values.") if attributes =~ /[\.#](\.|#|\z)/
@@ -597,10 +679,17 @@ END
597
679
 
598
680
  object_ref = "nil" if object_ref.nil? || @options[:suppress_eval]
599
681
 
600
- static_attributes = parse_static_hash(attributes_hash) # Try pre-compiling a static attributes hash
601
- attributes_hash = nil if static_attributes || @options[:suppress_eval]
602
682
  attributes = parse_class_and_id(attributes)
603
- Buffer.merge_attrs(attributes, static_attributes) if static_attributes
683
+ attributes_hashes.map! do |syntax, attributes_hash|
684
+ if syntax == :old
685
+ static_attributes = parse_static_hash(attributes_hash)
686
+ attributes_hash = nil if static_attributes || @options[:suppress_eval]
687
+ else
688
+ static_attributes, attributes_hash = attributes_hash
689
+ end
690
+ Buffer.merge_attrs(attributes, static_attributes) if static_attributes
691
+ attributes_hash
692
+ end.compact!
604
693
 
605
694
  raise SyntaxError.new("Illegal nesting: nesting within a self-closing tag is illegal.", @next_line.index) if block_opened? && self_closing
606
695
  raise SyntaxError.new("Illegal nesting: content can't be both given on the same line as %#{tag_name} and nested within it.", @next_line.index) if block_opened? && !value.empty?
@@ -614,7 +703,7 @@ END
614
703
  (nuke_inner_whitespace && block_opened?)
615
704
 
616
705
  # Check if we can render the tag directly to text and not process it in the buffer
617
- if object_ref == "nil" && attributes_hash.nil? && !preserve_script
706
+ if object_ref == "nil" && attributes_hashes.empty? && !preserve_script
618
707
  tag_closed = !block_opened? && !self_closing && !parse
619
708
 
620
709
  open_tag = prerender_tag(tag_name, self_closing, attributes)
@@ -633,11 +722,18 @@ END
633
722
  else
634
723
  flush_merged_text
635
724
  content = value.empty? || parse ? 'nil' : value.dump
636
- attributes_hash = ', ' + attributes_hash if attributes_hash
725
+ if attributes_hashes.empty?
726
+ attributes_hashes = ''
727
+ elsif attributes_hashes.size == 1
728
+ attributes_hashes = ", #{attributes_hashes.first}"
729
+ else
730
+ attributes_hashes = ", (#{attributes_hashes.join(").merge(")})"
731
+ end
732
+
637
733
  args = [tag_name, self_closing, !block_opened?, preserve_tag, escape_html,
638
734
  attributes, nuke_outer_whitespace, nuke_inner_whitespace
639
735
  ].map { |v| v.inspect }.join(', ')
640
- push_silent "_hamlout.open_tag(#{args}, #{object_ref}, #{content}#{attributes_hash})"
736
+ push_silent "_hamlout.open_tag(#{args}, #{object_ref}, #{content}#{attributes_hashes})"
641
737
  @dont_tab_up_next_text = @dont_indent_next_line = dont_indent_next_line
642
738
  end
643
739
 
@@ -269,9 +269,9 @@ module Sass::Script
269
269
  # An incompatible coercion, e.g. between px and cm, will raise an error.
270
270
  #
271
271
  # @param num_units [Array<String>] The numerator units to coerce this number into.
272
- # See {#numerator\_units}
272
+ # See {\#numerator\_units}
273
273
  # @param den_units [Array<String>] The denominator units to coerce this number into.
274
- # See {#denominator\_units}
274
+ # See {\#denominator\_units}
275
275
  # @return [Number] The number with the new units
276
276
  # @raise [Sass::UnitConversionError] if the given units are incompatible with the number's
277
277
  # current units
@@ -23,6 +23,7 @@ class EngineTest < Test::Unit::TestCase
23
23
  "." => "Illegal element: classes and ids must have values.",
24
24
  ".#" => "Illegal element: classes and ids must have values.",
25
25
  ".{} a" => "Illegal element: classes and ids must have values.",
26
+ ".() a" => "Illegal element: classes and ids must have values.",
26
27
  ".= a" => "Illegal element: classes and ids must have values.",
27
28
  "%p..a" => "Illegal element: classes and ids must have values.",
28
29
  "%a/ b" => "Self-closing tags can't have content.",
@@ -47,6 +48,12 @@ class EngineTest < Test::Unit::TestCase
47
48
  "%p\n foo\n%p\n bar" => ["The line was indented 2 levels deeper than the previous line.", 4],
48
49
  "%p\n foo\n %p\n bar" => ["The line was indented 3 levels deeper than the previous line.", 4],
49
50
  "%p\n \tfoo" => ["Indentation can't use both tabs and spaces.", 2],
51
+ "%p(" => "Invalid attribute list: \"(\".",
52
+ "%p(foo=\nbar)" => ["Invalid attribute list: \"(foo=\".", 1],
53
+ "%p(foo=)" => "Invalid attribute list: \"(foo=)\".",
54
+ "%p(foo 'bar')" => "Invalid attribute list: \"(foo 'bar')\".",
55
+ "%p(foo 'bar'\nbaz='bang')" => ["Invalid attribute list: \"(foo 'bar'\".", 1],
56
+ "%p(foo='bar'\nbaz 'bang'\nbip='bop')" => ["Invalid attribute list: \"(foo='bar' baz 'bang'\".", 2],
50
57
 
51
58
  # Regression tests
52
59
  "- raise 'foo'\n\n\n\nbar" => ["foo", 1],
@@ -805,6 +812,104 @@ END
805
812
  assert_equal %{<!DOCTYPE html>\n}, render('!!!', :format => :html5)
806
813
  end
807
814
 
815
+ # New attributes
816
+
817
+ def test_basic_new_attributes
818
+ assert_equal("<a>bar</a>\n", render("%a() bar"))
819
+ assert_equal("<a href='foo'>bar</a>\n", render("%a(href='foo') bar"))
820
+ assert_equal("<a b='c' c='d' d='e'>baz</a>\n", render(%q{%a(b="c" c='d' d="e") baz}))
821
+ end
822
+
823
+ def test_new_attribute_ids
824
+ assert_equal("<div id='foo_bar'></div>\n", render("#foo(id='bar')"))
825
+ assert_equal("<div id='foo_bar_baz'></div>\n", render("#foo{:id => 'bar'}(id='baz')"))
826
+ assert_equal("<div id='foo_baz_bar'></div>\n", render("#foo(id='baz'){:id => 'bar'}"))
827
+ foo = User.new(42)
828
+ assert_equal("<div class='struct_user' id='foo_baz_bar_struct_user_42'></div>\n",
829
+ render("#foo(id='baz'){:id => 'bar'}[foo]", :locals => {:foo => foo}))
830
+ assert_equal("<div class='struct_user' id='foo_baz_bar_struct_user_42'></div>\n",
831
+ render("#foo(id='baz')[foo]{:id => 'bar'}", :locals => {:foo => foo}))
832
+ assert_equal("<div class='struct_user' id='foo_baz_bar_struct_user_42'></div>\n",
833
+ render("#foo[foo](id='baz'){:id => 'bar'}", :locals => {:foo => foo}))
834
+ assert_equal("<div class='struct_user' id='foo_bar_baz_struct_user_42'></div>\n",
835
+ render("#foo[foo]{:id => 'bar'}(id='baz')", :locals => {:foo => foo}))
836
+ end
837
+
838
+ def test_new_attribute_classes
839
+ assert_equal("<div class='bar foo'></div>\n", render(".foo(class='bar')"))
840
+ assert_equal("<div class='bar baz foo'></div>\n", render(".foo{:class => 'bar'}(class='baz')"))
841
+ assert_equal("<div class='bar baz foo'></div>\n", render(".foo(class='baz'){:class => 'bar'}"))
842
+ foo = User.new(42)
843
+ assert_equal("<div class='bar baz foo struct_user' id='struct_user_42'></div>\n",
844
+ render(".foo(class='baz'){:class => 'bar'}[foo]", :locals => {:foo => foo}))
845
+ assert_equal("<div class='bar baz foo struct_user' id='struct_user_42'></div>\n",
846
+ render(".foo[foo](class='baz'){:class => 'bar'}", :locals => {:foo => foo}))
847
+ assert_equal("<div class='bar baz foo struct_user' id='struct_user_42'></div>\n",
848
+ render(".foo[foo]{:class => 'bar'}(class='baz')", :locals => {:foo => foo}))
849
+ end
850
+
851
+ def test_dynamic_new_attributes
852
+ assert_equal("<a href='12'>bar</a>\n", render("%a(href=foo) bar", :locals => {:foo => 12}))
853
+ assert_equal("<a b='12' c='13' d='14'>bar</a>\n", render("%a(b=b c='13' d=d) bar", :locals => {:b => 12, :d => 14}))
854
+ end
855
+
856
+ def test_new_attribute_interpolation
857
+ assert_equal("<a href='12'>bar</a>\n", render('%a(href="1#{1 + 1}") bar'))
858
+ assert_equal("<a href='2: 2, 3: 3'>bar</a>\n", render(%q{%a(href='2: #{1 + 1}, 3: #{foo}') bar}, :locals => {:foo => 3}))
859
+ assert_equal(%Q{<a href='1\#{1 + 1}'>bar</a>\n}, render('%a(href="1\#{1 + 1}") bar'))
860
+ end
861
+
862
+ def test_truthy_new_attributes
863
+ assert_equal("<a href='href'>bar</a>\n", render("%a(href) bar"))
864
+ assert_equal("<a bar='baz' href>bar</a>\n", render("%a(href bar='baz') bar", :format => :html5))
865
+ assert_equal("<a href='href'>bar</a>\n", render("%a(href=true) bar"))
866
+ assert_equal("<a>bar</a>\n", render("%a(href=false) bar"))
867
+ end
868
+
869
+ def test_new_attribute_parsing
870
+ assert_equal("<a a2='b2'>bar</a>\n", render("%a(a2=b2) bar", :locals => {:b2 => 'b2'}))
871
+ assert_equal(%Q{<a a='foo"bar'>bar</a>\n}, render(%q{%a(a="#{'foo"bar'}") bar})) #'
872
+ assert_equal(%Q{<a a="foo'bar">bar</a>\n}, render(%q{%a(a="#{"foo'bar"}") bar})) #'
873
+ assert_equal(%Q{<a a='foo"bar'>bar</a>\n}, render(%q{%a(a='foo"bar') bar}))
874
+ assert_equal(%Q{<a a="foo'bar">bar</a>\n}, render(%q{%a(a="foo'bar") bar}))
875
+ assert_equal("<a a:b='foo'>bar</a>\n", render("%a(a:b='foo') bar"))
876
+ assert_equal("<a a='foo' b='bar'>bar</a>\n", render("%a(a = 'foo' b = 'bar') bar"))
877
+ assert_equal("<a a='foo' b='bar'>bar</a>\n", render("%a(a = foo b = bar) bar", :locals => {:foo => 'foo', :bar => 'bar'}))
878
+ assert_equal("<a a='foo'>(b='bar')</a>\n", render("%a(a='foo')(b='bar')"))
879
+ assert_equal("<a a='foo)bar'>baz</a>\n", render("%a(a='foo)bar') baz"))
880
+ assert_equal("<a a='foo'>baz</a>\n", render("%a( a = 'foo' ) baz"))
881
+ end
882
+
883
+ def test_new_attribute_escaping
884
+ assert_equal(%Q{<a a='foo " bar'>bar</a>\n}, render(%q{%a(a="foo \" bar") bar}))
885
+ assert_equal(%Q{<a a='foo \\" bar'>bar</a>\n}, render(%q{%a(a="foo \\\\\" bar") bar}))
886
+
887
+ assert_equal(%Q{<a a="foo ' bar">bar</a>\n}, render(%q{%a(a='foo \' bar') bar}))
888
+ assert_equal(%Q{<a a="foo \\' bar">bar</a>\n}, render(%q{%a(a='foo \\\\\' bar') bar}))
889
+
890
+ assert_equal(%Q{<a a='foo \\ bar'>bar</a>\n}, render(%q{%a(a="foo \\\\ bar") bar}))
891
+ assert_equal(%Q{<a a='foo \#{1 + 1} bar'>bar</a>\n}, render(%q{%a(a="foo \#{1 + 1} bar") bar}))
892
+ end
893
+
894
+ def test_multiline_new_attribute
895
+ assert_equal("<a a='b' c='d'>bar</a>\n", render("%a(a='b'\n c='d') bar"))
896
+ assert_equal("<a a='b' b='c' c='d' d='e' e='f' f='j'>bar</a>\n",
897
+ render("%a(a='b' b='c'\n c='d' d=e\n e='f' f='j') bar", :locals => {:e => 'e'}))
898
+ end
899
+
900
+ def test_new_and_old_attributes
901
+ assert_equal("<a a='b' c='d'>bar</a>\n", render("%a(a='b'){:c => 'd'} bar"))
902
+ assert_equal("<a a='b' c='d'>bar</a>\n", render("%a{:c => 'd'}(a='b') bar"))
903
+ assert_equal("<a a='b' c='d'>bar</a>\n", render("%a(c='d'){:a => 'b'} bar"))
904
+ assert_equal("<a a='b' c='d'>bar</a>\n", render("%a{:a => 'b'}(c='d') bar"))
905
+
906
+ assert_equal("<a a='d'>bar</a>\n", render("%a{:a => 'b'}(a='d') bar"))
907
+ assert_equal("<a a='b'>bar</a>\n", render("%a(a='d'){:a => 'b'} bar"))
908
+
909
+ assert_equal("<a a='b' b='c' c='d' d='e'>bar</a>\n",
910
+ render("%a{:a => 'b',\n:b => 'c'}(c='d'\nd='e') bar"))
911
+ end
912
+
808
913
  # Encodings
809
914
 
810
915
  unless Haml::Util.ruby1_8?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haml-edge
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.44
4
+ version: 2.1.45
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Weizenbaum
@@ -12,8 +12,27 @@ cert_chain: []
12
12
 
13
13
  date: 2009-07-04 00:00:00 -04:00
14
14
  default_executable:
15
- dependencies: []
16
-
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: yard
18
+ type: :development
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.2.3
25
+ version:
26
+ - !ruby/object:Gem::Dependency
27
+ name: maruku
28
+ type: :development
29
+ version_requirement:
30
+ version_requirements: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 0.5.9
35
+ version:
17
36
  description: Haml (HTML Abstraction Markup Language) is a layer on top of XHTML or XML that's designed to express the structure of XHTML or XML documents in a non-repetitive, elegant, easy way, using indentation rather than closing tags and allowing Ruby to be embedded with ease. It was originally envisioned as a plugin for Ruby on Rails, but it can function as a stand-alone templating engine.
18
37
  email: haml@googlegroups.com
19
38
  executables: