aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view/template/template.rb
blob: b6967a2013d3beb66523b2ddd3f322524746856c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
require 'set'
require "action_view/template/path"

module ActionView #:nodoc:
  class Template
    extend TemplateHandlers
    extend ActiveSupport::Memoizable
    
    module Loading
      def load!
        @cached = true
        # freeze
      end    
    end
    include Loading    
    
    include Renderable

    # Templates that are exempt from layouts
    @@exempt_from_layout = Set.new([/\.rjs$/])

    # Don't render layouts for templates with the given extensions.
    def self.exempt_from_layout(*extensions)
      regexps = extensions.collect do |extension|
        extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
      end
      @@exempt_from_layout.merge(regexps)
    end

    attr_accessor :template_path, :filename, :load_path, :base_path
    attr_accessor :locale, :name, :format, :extension
    delegate :to_s, :to => :path

    def initialize(template_path, load_paths = [])
      template_path = template_path.dup
      @load_path, @filename = find_full_path(template_path, load_paths)
      @base_path, @name, @locale, @format, @extension = split(template_path)
      @base_path.to_s.gsub!(/\/$/, '') # Push to split method

      # Extend with partial super powers
      extend RenderablePartial if @name =~ /^_/
    end
    
    def accessible_paths
      paths = []

      if valid_extension?(extension)
        paths << path
        paths << path_without_extension
        if multipart?
          formats = format.split(".")
          paths << "#{path_without_format_and_extension}.#{formats.first}"
          paths << "#{path_without_format_and_extension}.#{formats.second}"
        end
      else
        # template without explicit template handler should only be reachable through its exact path
        paths << template_path
      end

      paths
    end
    
    def relative_path
      path = File.expand_path(filename)
      path.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') if defined?(RAILS_ROOT)
      path
    end
    memoize :relative_path
    
    def source
      File.read(filename)
    end
    memoize :source
    
    def exempt_from_layout?
      @@exempt_from_layout.any? { |exempted| path =~ exempted }
    end    
    
    def path_without_extension
      [base_path, [name, locale, format].compact.join('.')].compact.join('/')
    end
    memoize :path_without_extension    

    def path_without_format_and_extension
      [base_path, [name, locale].compact.join('.')].compact.join('/')
    end
    memoize :path_without_format_and_extension
    
    def path
      [base_path, [name, locale, format, extension].compact.join('.')].compact.join('/')
    end
    memoize :path
    
    def mime_type
      Mime::Type.lookup_by_extension(format) if format && defined?(::Mime)
    end
    memoize :mime_type      
    
    def multipart?
      format && format.include?('.')
    end    
    
    def content_type
      format.gsub('.', '/')
    end    
    
  private
    
    def format_and_extension
      (extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
    end
    memoize :format_and_extension

    def mtime
      File.mtime(filename)
    end
    memoize :mtime

    def method_segment
      relative_path.to_s.gsub(/([^a-zA-Z0-9_])/) { $1.ord }
    end
    memoize :method_segment

    def stale?
      File.mtime(filename) > mtime
    end

    def recompile?
      !@cached
    end

    def valid_extension?(extension)
      !Template.registered_template_handler(extension).nil?
    end

    def valid_locale?(locale)
      I18n.available_locales.include?(locale.to_sym)
    end

    def find_full_path(path, load_paths)
      load_paths = Array(load_paths) + [nil]
      load_paths.each do |load_path|
        file = load_path ? "#{load_path.to_str}/#{path}" : path
        return load_path, file if File.file?(file)
      end
      raise MissingTemplate.new(load_paths, path)
    end

    # Returns file split into an array
    #   [base_path, name, locale, format, extension]
    def split(file)
      if m = file.to_s.match(/^(.*\/)?([^\.]+)\.(.*)$/)
        base_path = m[1]
        name = m[2]
        extensions = m[3]
      else
        return
      end

      locale = nil
      format = nil
      extension = nil

      if m = extensions.split(".")
        if valid_locale?(m[0]) && m[1] && valid_extension?(m[2]) # All three
          locale = m[0]
          format = m[1]
          extension = m[2]
        elsif m[0] && m[1] && valid_extension?(m[2]) # Multipart formats
          format = "#{m[0]}.#{m[1]}"
          extension = m[2]
        elsif valid_locale?(m[0]) && valid_extension?(m[1]) # locale and extension
          locale = m[0]
          extension = m[1]
        elsif valid_extension?(m[1]) # format and extension
          format = m[0]
          extension = m[1]
        elsif valid_extension?(m[0]) # Just extension
          extension = m[0]
        else # No extension
          format = m[0]
        end
      end

      [base_path, name, locale, format, extension]
    end
  end
end