aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/paths.rb
blob: 1c9e3086314ff034f5a6d707380e33196d939c5b (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
require 'set'

module Rails
  module Paths
    module PathParent
      attr_reader :children

      def method_missing(id, *args)
        name = id.to_s

        if name =~ /^(.*)=$/ || args.any?
          @children[$1 || name] = Path.new(@root, *args)
        elsif path = @children[name]
          path
        else
          super
        end
      end
    end

    class Root
      include PathParent

      attr_accessor :path

      def initialize(path)
        raise if path.is_a?(Array)

        @children = {}

        @path = path
        @root = self
        @all_paths = []
      end

      def all_paths
        @all_paths.uniq!
        @all_paths
      end

      def load_once
        filter_by(:load_once?)
      end

      def eager_load
        filter_by(:eager_load?)
      end

      def load_paths
        filter_by(:load_path?)
      end

      def push(*)
        raise "Application root can only have one physical path"
      end

      alias unshift push
      alias << push
      alias concat push

    protected

      def filter_by(constraint)
        all_paths.map do |path|
          if path.send(constraint)
            paths  = path.paths
            paths -= path.children.values.map { |p| p.send(constraint) ? [] : p.paths }.flatten
            paths
          else
            []
          end
        end.flatten.uniq.select { |p| File.exists?(p) }
      end
    end

    class Path
      include PathParent, Enumerable

      attr_reader :path
      attr_accessor :glob

      def initialize(root, *paths)
        @options  = paths.last.is_a?(::Hash) ? paths.pop : {}
        @children = {}
        @root     = root
        @paths    = paths.flatten
        @glob     = @options.delete(:glob)

        @load_once  = @options[:load_once]
        @eager_load = @options[:eager_load]
        @load_path  = @options[:load_path] || @eager_load || @load_once

        @root.all_paths << self
      end

      def each
        to_a.each { |p| yield p }
      end

      def push(path)
        @paths.push path
      end

      alias << push

      def unshift(path)
        @paths.unshift path
      end

      def concat(paths)
        @paths.concat paths
      end

      def load_once!
        @load_once = true
        @load_path = true
      end

      def load_once?
        @load_once
      end

      def eager_load!
        @eager_load = true
        @load_path  = true
      end

      def eager_load?
        @eager_load
      end

      def load_path!
        @load_path = true
      end

      def load_path?
        @load_path
      end

      def paths
        raise "You need to set a path root" unless @root.path

        result = @paths.map do |p|
          path = File.expand_path(p, @root.path)
          @glob ? Dir[File.join(path, @glob)] : path
        end

        result.flatten!
        result.uniq!
        result
      end

      alias to_a paths
    end
  end
end