aboutsummaryrefslogblamecommitdiffstats
path: root/actionpack/lib/action_view/template.rb
blob: b8e2165ddf9e7d0ec88e4a02ec6f112508a31765 (plain) (tree)
1
2
3
4
5
6
7
8
9
                          
                





                                                                               
                                        
         
 


                                                                                             































                                                                                
         






                                     
           

         
 



                          
 




                                       




                                                  


                      
 







                                                                                 



                                                                    

                                                         
           

       
                           
                                    
                      
 










                                                                                   

                                                     
                                
 
                                            
                                       
                                                                            
                                                                            
                                                             




                                               











                                                                         
                            
                                                                                    
       
                                 
 







                                    
                 
                                                                          


                      
            
                                                                                        
       
                 
 
                              
                                                                             
       
                                   
 
                                         
                                                                     
       
                                              
 






                                                                                                



                                                               
              
                         
       
                   
 
                      
                                                           
       
                           


                                                 


                             
                               

               
                                                      

         
 
             
            
       
 
           
                                     
                                                             

         



                                                      
                                        
                                                      
                     











                                                    


                                                                              

                            




                                                                                    
                            




                                                             
                             
                         

             

                                                    
         
     
   
module ActionView #:nodoc:
  class Template
    class Path
      attr_reader :path, :paths
      delegate :hash, :inspect, :to => :path

      def initialize(path)
        raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
        @path = expand_path(path).freeze
      end

      def expand_path(path)
        # collapse any directory dots in path ('.' or '..')
        path.starts_with?('/') ? File.expand_path(path) : File.expand_path(path, '/').from(1)
      end

      def to_s
        if defined?(RAILS_ROOT)
          path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '')
        else
          path.to_s
        end
      end

      def to_str
        path.to_str
      end

      def ==(path)
        to_str == path.to_str
      end

      def eql?(path)
        to_str == path.to_str
      end

      # Returns a ActionView::Template object for the given path string. The
      # input path should be relative to the view path directory,
      # +hello/index.html.erb+. This method also has a special exception to
      # match partial file names without a handler extension. So
      # +hello/index.html+ will match the first template it finds with a
      # known template extension, +hello/index.html.erb+. Template extensions
      # should not be confused with format extensions +html+, +js+, +xml+,
      # etc. A format must be supplied to match a formated file. +hello/index+
      # will never match +hello/index.html.erb+.
      def [](path)
      end
      
      def load!
      end
      
      def self.new_and_loaded(path)
        returning new(path) do |path|
          path.load!
        end
      end
    end

    class EagerPath < Path
      def initialize(path)
        super
      end

      def load!
        return if @loaded
        
        @paths = {}
        templates_in_path do |template|
          template.load!
          template.accessible_paths.each do |path|
            @paths[path] = template
          end
        end
        @paths.freeze
        @loaded = true
      end

      def [](path)
        load! unless @loaded
        @paths[path]
      end
      
      private
        def templates_in_path
          (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
            yield create_template(file) unless File.directory?(file)
          end
        end

        def create_template(file)
          Template.new(file.split("#{self}/").last, self)
        end
    end

    extend TemplateHandlers
    extend ActiveSupport::Memoizable
    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 :filename, :load_path, :base_path
    attr_accessor :locale, :name, :format, :extension
    delegate :to_s, :to => :path

    def initialize(template_path, load_path)
      template_path = template_path.dup
      @load_path, @filename = load_path, File.join(load_path, template_path)
      @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 = []
      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
      paths
    end

    def format_and_extension
      (extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
    end
    memoize :format_and_extension

    def multipart?
      format && format.include?('.')
    end

    def content_type
      format.gsub('.', '/')
    end

    def mime_type
      Mime::Type.lookup_by_extension(format) if format && defined?(::Mime)
    end
    memoize :mime_type

    def path
      [base_path, [name, locale, format, extension].compact.join('.')].compact.join('/')
    end
    memoize :path

    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 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 exempt_from_layout?
      @@exempt_from_layout.any? { |exempted| path =~ exempted }
    end

    def source
      File.read(filename)
    end
    memoize :source

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

    def render_template(view, local_assigns = {})
      render(view, local_assigns)
    rescue Exception => e
      raise e unless filename
      if TemplateError === e
        e.sub_template_of(self)
        raise e
      else
        raise TemplateError.new(self, view.assigns, e)
      end
    end

    def load!
      freeze
    end

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

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

      # Returns file split into an array
      #   [base_path, name, locale, format, extension]
      def split(file)
        if m = file.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