aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/engine.rb
blob: effbfee6c13f1e41521ebb37c5c4cbcef1ab5930 (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
require 'active_support/core_ext/module/delegation'

module Rails
  # TODO Move I18n here
  # TODO Set routes namespaces
  class Engine < Railtie

    class << self
      attr_accessor :called_from

      def original_root
        @original_root ||= find_root_with_file_flag("lib")
      end

      def config
        @config ||= Configuration.new(original_root)
      end

      def inherited(base)
        base.called_from = begin
          call_stack = caller.map { |p| p.split(':').first }
          File.dirname(call_stack.detect { |p| p !~ %r[railties/lib/rails|rack/lib/rack] })
        end

        super
      end

    protected

      def find_root_with_file_flag(flag, default=nil)
        root_path = self.called_from

        while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/#{flag}")
          parent = File.dirname(root_path)
          root_path = parent != root_path && parent
        end

        root = File.exist?("#{root_path}/#{flag}") ? root_path : default

        raise "Could not find root path for #{self}" unless root

        RUBY_PLATFORM =~ /(:?mswin|mingw)/ ?
          Pathname.new(root).expand_path :
          Pathname.new(root).realpath
      end
    end

    delegate :config, :to => :'self.class'
    delegate :middleware, :root, :to => :config

    # Add configured load paths to ruby load paths and remove duplicates.
    initializer :set_load_path, :before => :container do
      expand_load_path(config.load_paths).reverse_each do |path|
        $LOAD_PATH.unshift(path) if File.directory?(path)
      end
      $LOAD_PATH.uniq!
    end

    # Set the paths from which Rails will automatically load source files,
    # and the load_once paths.
    initializer :set_autoload_paths, :before => :container do
      require 'active_support/dependencies'

      ActiveSupport::Dependencies.load_paths      = expand_load_path(config.load_paths)
      ActiveSupport::Dependencies.load_once_paths = expand_load_path(config.load_once_paths)

      extra = ActiveSupport::Dependencies.load_once_paths -
              ActiveSupport::Dependencies.load_paths

      unless extra.empty?
        abort <<-end_error
          load_once_paths must be a subset of the load_paths.
          Extra items in load_once_paths: #{extra * ','}
        end_error
      end

      # Freeze the arrays so future modifications will fail rather than do nothing mysteriously
      config.load_once_paths.freeze
    end

    # Routing must be initialized after plugins to allow the former to extend the routes
    initializer :add_routing_files do |app|
      routes = select_existing(config.paths.config.routes)
      app.route_configuration_files.concat(routes)
    end

    initializer :add_view_paths do
      views = select_existing(config.paths.app.views)
      ActionController::Base.view_paths.concat(views) if defined? ActionController
      ActionMailer::Base.view_paths.concat(views)     if defined? ActionMailer
    end

    initializer :load_application_initializers do
      select_existing(config.paths.config.initializers).each do |initializers|
        Dir["#{initializers}/**/*.rb"].sort.each do |initializer|
          load(initializer)
        end
      end
    end

    # Eager load application classes
    initializer :load_application_classes do |app|
      next if $rails_rake_task

      if app.config.cache_classes
        config.eager_load_paths.each do |load_path|
          matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/
          Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
            require_dependency file.sub(matcher, '\1')
          end
        end
      end
    end

  private

    def select_existing(paths)
      paths.to_a.select { |path| File.exists?(path) }.uniq
    end

    def expand_load_path(load_paths)
      load_paths.map { |path| Dir.glob(path.to_s) }.flatten.uniq
    end
  end
end