erb.rb 3.5 KB
Newer Older
J
Jeremy Kemper 已提交
1
require 'active_support/core_ext/class/attribute_accessors'
2
require 'active_support/core_ext/string/output_safety'
3 4
require 'action_view/template'
require 'action_view/template/handler'
5
require 'erubis'
6

7
module ActionView
8
  class OutputBuffer < ActiveSupport::SafeBuffer
9 10
    def initialize(*)
      super
11
      encode! if encoding_aware?
12 13
    end

14
    def <<(value)
15
      super(value.to_s)
16
    end
17
    alias :append= :<<
18
    alias :safe_append= :safe_concat
19 20
  end

21 22 23 24 25 26
  class Template
    module Handlers
      class Erubis < ::Erubis::Eruby
        def add_preamble(src)
          src << "@output_buffer = ActionView::OutputBuffer.new;"
        end
27

28 29 30 31
        def add_text(src, text)
          return if text.empty?
          src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
        end
32

33
        BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
C
Carlhuda 已提交
34

35 36 37 38 39 40
        def add_expr_literal(src, code)
          if code =~ BLOCK_EXPR
            src << '@output_buffer.append= ' << code
          else
            src << '@output_buffer.append= (' << code << ');'
          end
41
        end
42

43
        def add_expr_escaped(src, code)
44 45 46 47 48
          if code =~ BLOCK_EXPR
            src << "@output_buffer.safe_append= " << code
          else
            src << "@output_buffer.safe_concat((" << code << ").to_s);"
          end
49
        end
50

51 52 53
        def add_postamble(src)
          src << '@output_buffer.to_s'
        end
54 55
      end

56
      class ERB
57 58
        # Specify trim mode for the ERB compiler. Defaults to '-'.
        # See ERb documentation for suitable values.
59
        class_attribute :erb_trim_mode
60
        self.erb_trim_mode = '-'
61

62 63
        # Default format used by ERB.
        class_attribute :default_format
64
        self.default_format = Mime::HTML
65

R
R.T. Lechow 已提交
66
        # Default implementation used.
67
        class_attribute :erb_implementation
68
        self.erb_implementation = Erubis
69

70 71
        ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")

72 73 74 75 76
        def self.call(template)
          new.call(template)
        end

        def handles_encoding?
77 78
          true
        end
79

80
        def call(template)
81
          if template.source.encoding_aware?
82 83 84 85
            # First, convert to BINARY, so in case the encoding is
            # wrong, we can still find an encoding tag
            # (<%# encoding %>) inside the String using a regular
            # expression
86 87 88 89 90
            template_source = template.source.dup.force_encoding("BINARY")

            erb = template_source.gsub(ENCODING_TAG, '')
            encoding = $2

91 92 93 94
            erb.force_encoding valid_encoding(template.source.dup, encoding)

            # Always make sure we return a String in the default_internal
            erb.encode!
95 96
          else
            erb = template.source.dup
97 98
          end

99
          self.class.erb_implementation.new(
100 101 102
            erb,
            :trim => (self.class.erb_trim_mode == "-")
          ).src
103 104 105
        end

      private
106

107 108 109 110 111 112 113 114 115 116
        def valid_encoding(string, encoding)
          # If a magic encoding comment was found, tag the
          # String with this encoding. This is for a case
          # where the original String was assumed to be,
          # for instance, UTF-8, but a magic comment
          # proved otherwise
          string.force_encoding(encoding) if encoding

          # If the String is valid, return the encoding we found
          return string.encoding if string.valid_encoding?
117

118 119
          # Otherwise, raise an exception
          raise WrongEncodingError.new(string, string.encoding)
120
        end
121 122 123 124
      end
    end
  end
end