haml-edge 2.1.44 → 2.1.45

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.
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: