initializer.rb 9.7 KB
Newer Older
1 2
require 'logger'

3
RAILS_ENV = ENV['RAILS_ENV'] || 'development' unless defined?(RAILS_ENV)
4 5

module Rails
6 7 8 9 10 11 12 13 14 15 16 17 18
  # The Initializer is responsible for processing the Rails configuration, such as setting the $LOAD_PATH, requiring the
  # right frameworks, initializing logging, and more. It can be run either as a single command that'll just use the 
  # default configuration, like this:
  #
  #   Rails::Initializer.run
  #
  # But normally it's more interesting to pass in a custom configuration through the block running:
  #
  #   Rails::Initializer.run do |config|
  #     config.frameworks -= [ :action_web_service ]
  #   end
  #
  # This will use the default configuration options from Rails::Configuration, but allow for overwriting on select areas.
19 20 21 22 23 24 25 26 27 28 29 30 31 32
  class Initializer
    attr_reader :configuration
    
    def self.run(command = :process, configuration = Configuration.new)
      yield configuration if block_given?
      new(configuration).send(command)
    end
    
    def initialize(configuration)
      @configuration = configuration
    end
    
    def process
      set_load_path
33
      set_connection_adapters
34 35

      require_frameworks
J
Jamis Buck 已提交
36
      load_plugins
37
      load_environment
38 39 40 41

      initialize_database
      initialize_logger
      initialize_framework_logging
42
      initialize_framework_views
43
      initialize_routing
44 45 46 47 48 49 50 51 52 53
      initialize_dependency_mechanism
      initialize_breakpoints
      initialize_whiny_nils
      
      intitialize_framework_settings
      
      # Support for legacy configuration style where the environment
      # could overwrite anything set from the defaults/global through
      # the individual base class configurations.
      load_environment
54 55 56 57 58 59 60
    end
    
    def set_load_path
      configuration.load_paths.reverse.each { |dir| $LOAD_PATH.unshift(dir) if File.directory?(dir) }
      $LOAD_PATH.uniq!
    end
    
61
    def set_connection_adapters
62
      Object.const_set("RAILS_CONNECTION_ADAPTERS", configuration.connection_adapters) if configuration.connection_adapters
63 64
    end
    
65 66 67 68
    def require_frameworks
      configuration.frameworks.each { |framework| require(framework.to_s) }
    end
    
J
Jamis Buck 已提交
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
    def load_plugins
      config = configuration

      Dir.glob("#{configuration.plugins_path}/*") do |directory|
        next if File.basename(directory)[0] == ?. || !File.directory?(directory)

        if File.exist?("#{directory}/init.rb")
          silence_warnings do
            eval(IO.read("#{directory}/init.rb"), binding)
          end
        end

        if File.directory?("#{directory}/lib")
          $LOAD_PATH.unshift "#{directory}/lib"
        end
      end

      $LOAD_PATH.uniq!
    end

89 90 91 92 93
    def load_environment
      silence_warnings do
        config = configuration
        eval(IO.read(configuration.environment_path), binding)
      end
94 95 96 97 98 99 100 101 102
    end
    
    def initialize_database
      return unless configuration.frameworks.include?(:active_record)
      ActiveRecord::Base.configurations = configuration.database_configuration
      ActiveRecord::Base.establish_connection
    end
    
    def initialize_logger
103 104 105
      # if the environment has explicitly defined a logger, use it
      return if defined?(RAILS_DEFAULT_LOGGER)

106 107 108 109 110 111 112 113 114 115 116 117
      unless logger = configuration.logger
        begin
          logger = Logger.new(configuration.log_path)
          logger.level = Logger.const_get(configuration.log_level.to_s.upcase)
        rescue StandardError
          logger = Logger.new(STDERR)
          logger.level = Logger::WARN
          logger.warn(
            "Rails Error: Unable to access log file. Please ensure that #{configuration.log_path} exists and is chmod 0666. " +
            "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
          )
        end
118 119
      end
      
120
      silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
121 122 123
    end
    
    def initialize_framework_logging
124 125 126
      for framework in ([ :active_record, :action_controller, :action_mailer ] & configuration.frameworks)
        framework.to_s.camelize.constantize.const_get("Base").logger ||= RAILS_DEFAULT_LOGGER
      end
127 128 129
    end
    
    def initialize_framework_views
130 131 132
      for framework in ([ :action_controller, :action_mailer ] & configuration.frameworks)
        framework.to_s.camelize.constantize.const_get("Base").template_root ||= configuration.view_path
      end
133 134 135 136 137 138 139
    end

    def initialize_routing
      return unless configuration.frameworks.include?(:action_controller)
      ActionController::Routing::Routes.reload
      Object.const_set "Controllers", Dependencies::LoadingModule.root(*configuration.controller_paths)
    end
140
    
141 142
    def initialize_dependency_mechanism
      Dependencies.mechanism = configuration.cache_classes ? :require : :load
143
    end
144 145 146
    
    def initialize_breakpoints
      silence_warnings { Object.const_set("BREAKPOINT_SERVER_PORT", 42531) if configuration.breakpoint_server }
147
    end
148
    
149 150 151
    def initialize_whiny_nils
      require('active_support/whiny_nil') if configuration.whiny_nils
    end
152

153 154 155
    def intitialize_framework_settings
      configuration.frameworks.each do |framework|
        base_class = framework.to_s.camelize.constantize.const_get("Base")
156

157 158 159
        configuration.send(framework).each do |setting, value|
          base_class.send("#{setting}=", value)
        end
160 161
      end
    end
162 163
  end
  
164 165 166 167 168 169 170
  # The Configuration class holds all the parameters for the Initializer and ships with defaults that suites most
  # Rails applications. But it's possible to overwrite everything. Usually, you'll create an Configuration file implicitly
  # through the block running on the Initializer, but it's also possible to create the Configuration instance in advance and
  # pass it in like this:
  #
  #   config = Rails::Configuration.new
  #   Rails::Initializer.run(:process, config)
171
  class Configuration
172
    attr_accessor :frameworks, :load_paths, :logger, :log_level, :log_path, :database_configuration_file, :view_path, :controller_paths
173
    attr_accessor :cache_classes, :breakpoint_server, :whiny_nils
174
    attr_accessor :connection_adapters
175
    attr_accessor :active_record, :action_controller, :action_view, :action_mailer, :action_web_service
176 177
    
    def initialize
178 179 180 181 182 183 184 185 186
      self.frameworks                   = default_frameworks
      self.load_paths                   = default_load_paths
      self.log_path                     = default_log_path
      self.log_level                    = default_log_level
      self.view_path                    = default_view_path
      self.controller_paths             = default_controller_paths
      self.cache_classes                = default_cache_classes
      self.breakpoint_server            = default_breakpoint_server
      self.whiny_nils                   = default_whiny_nils
187
      self.database_configuration_file  = default_database_configuration_file
188 189

      for framework in default_frameworks
190
        self.send("#{framework}=", OrderedOptions.new)
191
      end
192 193 194
    end
    
    def database_configuration
195
      YAML::load(ERB.new(IO.read(database_configuration_file)).result)
196 197
    end
    
198 199
    def environment_path
      "#{RAILS_ROOT}/config/environments/#{environment}.rb"
200
    end
J
Jamis Buck 已提交
201 202 203 204

    def plugins_path
      "#{RAILS_ROOT}/vendor/plugins"
    end
205 206 207 208
    
    def environment
      ::RAILS_ENV
    end
209

210 211
    private
      def default_frameworks
212
        [ :active_record, :action_controller, :action_view, :action_mailer, :action_web_service ]
213 214 215
      end
    
      def default_load_paths
D
David Heinemeier Hansson 已提交
216
        paths = ["#{RAILS_ROOT}/test/mocks/#{environment}"]
217 218

        # Then model subdirectories.
219
        # TODO: Don't include .rb models as load paths
220 221 222 223
        paths.concat(Dir["#{RAILS_ROOT}/app/models/[_a-z]*"])
        paths.concat(Dir["#{RAILS_ROOT}/components/[_a-z]*"])

        # Followed by the standard includes.
224
        # TODO: Don't include dirs for frameworks that are not used
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
        paths.concat %w(
          app 
          app/models 
          app/controllers 
          app/helpers 
          app/apis 
          components 
          config 
          lib 
          vendor 
          vendor/rails/railties
          vendor/rails/railties/lib
          vendor/rails/actionpack/lib
          vendor/rails/activesupport/lib
          vendor/rails/activerecord/lib
          vendor/rails/actionmailer/lib
          vendor/rails/actionwebservice/lib
        ).map { |dir| "#{RAILS_ROOT}/#{dir}" }.select { |dir| File.directory?(dir) }
      end

      def default_log_path
        File.join(RAILS_ROOT, 'log', "#{environment}.log")
      end
      
      def default_log_level
250
        environment == 'production' ? :info : :debug
251 252 253 254 255 256 257 258 259 260 261 262 263
      end
      
      def default_database_configuration_file
        File.join(RAILS_ROOT, 'config', 'database.yml')
      end
      
      def default_view_path
        File.join(RAILS_ROOT, 'app', 'views')
      end
      
      def default_controller_paths
        [ File.join(RAILS_ROOT, 'app', 'controllers'), File.join(RAILS_ROOT, 'components') ]
      end
264
      
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
      def default_dependency_mechanism
        :load
      end
      
      def default_cache_classes
        false
      end
      
      def default_breakpoint_server
        false
      end
      
      def default_whiny_nils
        false
      end
280
  end
281
end
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313

# Needs to be duplicated from Active Support since its needed before Active Support is available
class OrderedOptions < Array # :nodoc:
  def []=(key, value)
    key = key.to_sym

    if pair = find_pair(key)
      pair.pop
      pair << value
    else
      self << [key, value]
    end
  end

  def [](key)
    pair = find_pair(key.to_sym)
    pair ? pair.last : nil
  end

  def method_missing(name, *args)
    if name.to_s =~ /(.*)=$/
      self[$1.to_sym] = args.first
    else
      self[name]
    end
  end

  private
    def find_pair(key)
      self.each { |i| return i if i.first == key }
      return false
    end
J
Jamis Buck 已提交
314
end