Project

General

Profile

Feature #11936 ยป 0001-Allow-ERB-subclass-to-add-token-easily.patch

k0kubun (Takashi Kokubun), 01/01/2016 11:04 AM

View differences:

lib/erb.rb
371 371
      def initialize(src, trim_mode, percent)
372 372
        @src = src
373 373
        @stag = nil
374
        @stags = %w(<%% <%= <%# <%).freeze
375
        @etags = %w(%%> %>).freeze
374 376
      end
375 377
      attr_accessor :stag
378
      attr_reader :stags, :etags
376 379

  
377 380
      def scan; end
378 381
    end
......
383 386
        @trim_mode = trim_mode
384 387
        @percent = percent
385 388
        if @trim_mode == '>'
389
          @scan_reg  = /(.*?)(%>\n|#{(stags + etags).join('|')}|\n|\z)/m
386 390
          @scan_line = self.method(:trim_line1)
387 391
        elsif @trim_mode == '<>'
392
          @scan_reg  = /(.*?)(%>\n|#{(stags + etags).join('|')}|\n|\z)/m
388 393
          @scan_line = self.method(:trim_line2)
389 394
        elsif @trim_mode == '-'
395
          @scan_reg  = /(.*?)(^[ \t]*<%\-|<%\-|-%>\n|-%>|#{(stags + etags).join('|')}|\z)/m
390 396
          @scan_line = self.method(:explicit_trim_line)
391 397
        else
398
          @scan_reg  = /(.*?)(#{(stags + etags).join('|')}|\n|\z)/m
392 399
          @scan_line = self.method(:scan_line)
393 400
        end
394 401
      end
......
420 427
      end
421 428

  
422 429
      def scan_line(line)
423
        line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens|
430
        line.scan(@scan_reg) do |tokens|
424 431
          tokens.each do |token|
425 432
            next if token.empty?
426 433
            yield(token)
......
429 436
      end
430 437

  
431 438
      def trim_line1(line)
432
        line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens|
439
        line.scan(@scan_reg) do |tokens|
433 440
          tokens.each do |token|
434 441
            next if token.empty?
435 442
            if token == "%>\n"
......
444 451

  
445 452
      def trim_line2(line)
446 453
        head = nil
447
        line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens|
454
        line.scan(@scan_reg) do |tokens|
448 455
          tokens.each do |token|
449 456
            next if token.empty?
450 457
            head = token unless head
......
465 472
      end
466 473

  
467 474
      def explicit_trim_line(line)
468
        line.scan(/(.*?)(^[ \t]*<%\-|<%\-|<%%|%%>|<%=|<%#|<%|-%>\n|-%>|%>|\z)/m) do |tokens|
475
        line.scan(@scan_reg) do |tokens|
469 476
          tokens.each do |token|
470 477
            next if token.empty?
471 478
            if @stag.nil? && /[ \t]*<%-/ =~ token
......
492 499

  
493 500
    class SimpleScanner < Scanner # :nodoc:
494 501
      def scan
495
        @src.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens|
502
        @src.scan(/(.*?)(#{(stags + etags).join('|')}|\n|\z)/m) do |tokens|
496 503
          tokens.each do |token|
497 504
            next if token.empty?
498 505
            yield(token)
......
507 514
      require 'strscan'
508 515
      class SimpleScanner2 < Scanner # :nodoc:
509 516
        def scan
510
          stag_reg = /(.*?)(<%[%=#]?|\z)/m
511
          etag_reg = /(.*?)(%%?>|\z)/m
517
          stag_reg = /(.*?)(#{stags.join('|')}|\z)/m
518
          etag_reg = /(.*?)(#{etags.join('|')}|\z)/m
512 519
          scanner = StringScanner.new(@src)
513 520
          while ! scanner.eos?
514 521
            scanner.scan(@stag ? etag_reg : stag_reg)
......
521 528

  
522 529
      class ExplicitScanner < Scanner # :nodoc:
523 530
        def scan
524
          stag_reg = /(.*?)(^[ \t]*<%-|<%%|<%=|<%#|<%-|<%|\z)/m
525
          etag_reg = /(.*?)(%%>|-%>|%>|\z)/m
531
          stag_reg = /(.*?)(^[ \t]*<%-|<%-|#{stags.join('|')}|\z)/m
532
          etag_reg = /(.*?)(-%>|#{etags.join('|')}|\z)/m
526 533
          scanner = StringScanner.new(@src)
527 534
          while ! scanner.eos?
528 535
            scanner.scan(@stag ? etag_reg : stag_reg)
......
602 609
      enc = detect_magic_comment(s) || enc
603 610
      out = Buffer.new(self, enc)
604 611

  
605
      content = ''
612
      self.content = ''
606 613
      scanner = make_scanner(s)
607 614
      scanner.scan do |token|
608 615
        next if token.nil?
609 616
        next if token == ''
610 617
        if scanner.stag.nil?
611
          case token
612
          when PercentLine
613
            add_put_cmd(out, content) if content.size > 0
614
            content = ''
615
            out.push(token.to_s)
616
            out.cr
617
          when :cr
618
            out.cr
619
          when '<%', '<%=', '<%#'
620
            scanner.stag = token
621
            add_put_cmd(out, content) if content.size > 0
622
            content = ''
623
          when "\n"
624
            content << "\n"
625
            add_put_cmd(out, content)
626
            content = ''
627
          when '<%%'
628
            content << '<%'
629
          else
630
            content << token
631
          end
618
          compile_stag(token, out, scanner)
632 619
        else
633
          case token
634
          when '%>'
635
            case scanner.stag
636
            when '<%'
637
              if content[-1] == ?\n
638
                content.chop!
639
                out.push(content)
640
                out.cr
641
              else
642
                out.push(content)
643
              end
644
            when '<%='
645
              add_insert_cmd(out, content)
646
            when '<%#'
647
              # out.push("# #{content_dump(content)}")
648
            end
649
            scanner.stag = nil
650
            content = ''
651
          when '%%>'
652
            content << '%>'
653
          else
654
            content << token
655
          end
620
          compile_etag(token, out, scanner)
656 621
        end
657 622
      end
658 623
      add_put_cmd(out, content) if content.size > 0
......
660 625
      return out.script, enc
661 626
    end
662 627

  
628
    def compile_stag(stag, out, scanner)
629
      case stag
630
      when PercentLine
631
        add_put_cmd(out, content) if content.size > 0
632
        self.content = ''
633
        out.push(stag.to_s)
634
        out.cr
635
      when :cr
636
        out.cr
637
      when '<%', '<%=', '<%#'
638
        scanner.stag = stag
639
        add_put_cmd(out, content) if content.size > 0
640
        self.content = ''
641
      when "\n"
642
        content << "\n"
643
        add_put_cmd(out, content)
644
        self.content = ''
645
      when '<%%'
646
        content << '<%'
647
      else
648
        content << stag
649
      end
650
    end
651

  
652
    def compile_etag(etag, out, scanner)
653
      case etag
654
      when '%>'
655
        compile_content(scanner.stag, out)
656
        scanner.stag = nil
657
        self.content = ''
658
      when '%%>'
659
        content << '%>'
660
      else
661
        content << etag
662
      end
663
    end
664

  
665
    def compile_content(stag, out)
666
      case stag
667
      when '<%'
668
        if content[-1] == ?\n
669
          content.chop!
670
          out.push(content)
671
          out.cr
672
        else
673
          out.push(content)
674
        end
675
      when '<%='
676
        add_insert_cmd(out, content)
677
      when '<%#'
678
        # out.push("# #{content_dump(content)}")
679
      end
680
    end
681

  
663 682
    def prepare_trim_mode(mode) # :nodoc:
664 683
      case mode
665 684
      when 1
......
712 731
    attr_accessor :post_cmd
713 732

  
714 733
    private
734

  
735
    # A buffered text in #compile
736
    attr_accessor :content
737

  
715 738
    def detect_magic_comment(s)
716 739
      if /\A<%#(.*)%>/ =~ s or (@percent and /\A%#(.*)/ =~ s)
717 740
        comment = $1
test/erb/test_erb.rb
481 481
  def test_percent_after_etag
482 482
    assert_equal("1%", @erb.new("<%= 1 %>%", nil, "%").result)
483 483
  end
484

  
485
  def test_token_extension
486
    extended_erb = Class.new(ERB)
487
    extended_erb.module_eval do
488
      def make_compiler(trim_mode)
489
        compiler = Class.new(ERB::Compiler)
490
        compiler.module_eval do
491
          def compile_stag(stag, out, scanner)
492
            case stag
493
            when '<%=='
494
              scanner.stag = stag
495
              add_put_cmd(out, content) if content.size > 0
496
              self.content = ''
497
            else
498
              super
499
            end
500
          end
501

  
502
          def compile_content(stag, out)
503
            case stag
504
            when '<%=='
505
              out.push("#{@insert_cmd}(::ERB::Util.html_escape(#{content}))")
506
            else
507
              super
508
            end
509
          end
510

  
511
          def make_scanner(src)
512
            scanner = Class.new(ERB::Compiler::SimpleScanner)
513
            scanner.module_eval do
514
              def stags
515
                ['<%=='] + super
516
              end
517
            end
518
            scanner.new(src, @trim_mode, @percent)
519
          end
520
        end
521
        compiler.new(trim_mode)
522
      end
523
    end
524

  
525
    src = <<~EOS
526
      <% tag = '<>' %>
527
      <%= tag %>
528
      <%== tag %>
529
    EOS
530
    ans = <<~EOS
531

  
532
      <>
533
      &lt;&gt;
534
    EOS
535
    assert_equal(ans, extended_erb.new(src).result)
536
  end
484 537
end
485 538

  
486 539
class TestERBCoreWOStrScan < TestERBCore
487
-