aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view/template/path.rb
blob: 660a7e91a215014ce7068810e419fe7a67efb1cc (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
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
  
      # TODO: Refactor this to abstract out the file system
      def initialize_template(file)
        t = Template.new(file.split("#{self}/").last, self)
        t.load!
        t
      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.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 = path
      end
    
      # TODO: This is the currently needed API. Make this suck less
      # ==== <suck>
      attr_reader :path
    
      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
      # ==== </suck>
    
      def find_templates(name, details, prefix, partial)
        if glob = parts_to_glob(name, details, prefix, partial)
          cached(glob) do
            Dir[glob].map do |path|
              initialize_template(path) unless File.directory?(path)
            end.compact
          end
        end
      end
    
    private
  
      def parts_to_glob(name, details, prefix, partial)
        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
        
        "#{@path}/#{path}#{extensions}#{handler_glob}"
      end
    end
  end
end