aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view/template/path.rb
blob: 478bf96c9a698e6cba94677c4fce014aa781647b (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
require "pathname"

module ActionView
  class Template
  # Abstract super class
    class Path
      def initialize(options)
        @cache  = options[:cache]
        @cached = {}
      end
    
      # Normalizes the arguments and passes it on to find_template
      def find_by_parts(*args)
        find_all_by_parts(*args).first
      end
      
      def find_all_by_parts(name, details = {}, prefix = nil, partial = nil)
        details[:locales] = [I18n.locale]
        name = name.to_s.gsub(handler_matcher, '').split("/")
        find_templates(name.pop, details, [prefix, *name].compact.join("/"), partial)
      end
    
    private
      
      # This is what child classes implement. No defaults are needed
      # because Path guarentees that the arguments are present and
      # normalized.
      def find_templates(name, details, prefix, partial)
        raise NotImplementedError
      end
      
      def valid_handlers
        @valid_handlers ||= TemplateHandlers.extensions
      end
      
      def handler_matcher
        @handler_matcher ||= begin
          e = valid_handlers.join('|')
          /\.(?:#{e})$/
        end
      end
       
      def handler_glob
        e = TemplateHandlers.extensions.map{|h| ".#{h},"}.join
        "{#{e}}"
      end
      
      def formats_glob
        @formats_glob ||= begin
          formats = Mime::SET.map { |m| m.symbol }
          '{' + formats.map { |l| ".#{l}," }.join + '}'
        end
      end
      
      def cached(key)
        return yield unless @cache
        return @cached[key] if @cached.key?(key)
        @cached[key] = yield
      end
    end
  
    class FileSystemPath < Path
    
      def initialize(path, options = {})
        raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
        super(options)
        @path = Pathname.new(path).expand_path
      end

      # TODO: This is the currently needed API. Make this suck less
      # ==== <suck>
      attr_reader :path
    
      def to_s
        path.to_s
      end

      def to_str
        path.to_s
      end

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

      def eql?(path)
        to_str == path.to_str
      end
      # ==== </suck>
    
      def find_templates(name, details, prefix, partial, root = "#{@path}/")
        if glob = details_to_glob(name, details, prefix, partial, root)
          cached(glob) do
            Dir[glob].map do |path|
              next if File.directory?(path)
              source = File.read(path)
              identifier = Pathname.new(path).expand_path.to_s
              
              Template.new(source, identifier, *path_to_details(path))
            end.compact
          end
        end
      end
    
    private
  
      # :api: plugin
      def details_to_glob(name, details, prefix, partial, root)
        path = ""
        path << "#{prefix}/" unless prefix.empty?
        path << (partial ? "_#{name}" : name)
      
        extensions = ""
        [:locales, :formats].each do |k|
          extensions << if exts = details[k]
            '{' + exts.map {|e| ".#{e},"}.join + '}'
          else
            k == :formats ? formats_glob : ''
          end
        end
        
        "#{root}#{path}#{extensions}#{handler_glob}"
      end
      
      # TODO: fix me
      # :api: plugin
      def path_to_details(path)
        # [:erb, :format => :html, :locale => :en, :partial => true/false]
        if m = path.match(%r'/(_)?[\w-]+(\.[\w-]+)*\.(\w+)$')
          partial = m[1] == '_'
          details = (m[2]||"").split('.').reject { |e| e.empty? }
          handler = Template.handler_class_for_extension(m[3])

          format  = Mime[details.last] && details.pop.to_sym
          locale  = details.last && details.pop.to_sym
          
          return handler, :format => format, :locale => locale, :partial => partial
        end
      end
    end

    class FileSystemPathWithFallback < FileSystemPath
      
      def find_templates(name, details, prefix, partial)
        templates = super
        return super(name, details, prefix, partial, '') if templates.empty?
        templates
      end
      
    end
  end
end