Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/trunk/railties/lib/initializer.rb

Revision 9240, 29.1 kB (checked in by rick, 8 months ago)

Don't require rails/gem_builder during rails initialization, it's only needed for the gems:build task. [rick]

Line 
1 require 'logger'
2 require 'set'
3 require 'pathname'
4
5 $LOAD_PATH.unshift File.dirname(__FILE__)
6 require 'railties_path'
7 require 'rails/version'
8 require 'rails/plugin/locator'
9 require 'rails/plugin/loader'
10 require 'rails/gem_dependency'
11
12
13 RAILS_ENV = (ENV['RAILS_ENV'] || 'development').dup unless defined?(RAILS_ENV)
14
15 module Rails
16   class << self
17     # The Configuration instance used to configure the Rails environment
18     def configuration
19       @@configuration
20     end
21  
22     def configuration=(configuration)
23       @@configuration = configuration
24     end
25  
26     def logger
27       RAILS_DEFAULT_LOGGER
28     end
29  
30     def root
31       RAILS_ROOT
32     end
33  
34     def env
35       RAILS_ENV
36     end
37  
38     def cache
39       RAILS_CACHE
40     end
41   end
42  
43   # The Initializer is responsible for processing the Rails configuration, such
44   # as setting the $LOAD_PATH, requiring the right frameworks, initializing
45   # logging, and more. It can be run either as a single command that'll just
46   # use the default configuration, like this:
47   #
48   #   Rails::Initializer.run
49   #
50   # But normally it's more interesting to pass in a custom configuration
51   # through the block running:
52   #
53   #   Rails::Initializer.run do |config|
54   #     config.frameworks -= [ :action_mailer ]
55   #   end
56   #
57   # This will use the default configuration options from Rails::Configuration,
58   # but allow for overwriting on select areas.
59   class Initializer
60     # The Configuration instance used by this Initializer instance.
61     attr_reader :configuration
62
63     # The set of loaded plugins.
64     attr_reader :loaded_plugins
65    
66     # Runs the initializer. By default, this will invoke the #process method,
67     # which simply executes all of the initialization routines. Alternately,
68     # you can specify explicitly which initialization routine you want:
69     #
70     #   Rails::Initializer.run(:set_load_path)
71     #
72     # This is useful if you only want the load path initialized, without
73     # incuring the overhead of completely loading the entire environment.
74     def self.run(command = :process, configuration = Configuration.new)
75       yield configuration if block_given?
76       initializer = new configuration
77       initializer.send(command)
78       initializer
79     end
80
81     # Create a new Initializer instance that references the given Configuration
82     # instance.
83     def initialize(configuration)
84       @configuration = configuration
85       @loaded_plugins = []
86     end
87
88     # Sequentially step through all of the available initialization routines,
89     # in order (view execution order in source).
90     def process
91       Rails.configuration = configuration
92
93       check_ruby_version
94       install_gem_spec_stubs
95       set_load_path
96      
97       require_frameworks
98       set_autoload_paths
99       add_gem_load_paths
100       add_plugin_load_paths
101       load_environment
102
103       initialize_encoding
104       initialize_database
105
106       initialize_cache
107       initialize_framework_caches
108
109       initialize_logger
110       initialize_framework_logging
111
112       initialize_framework_views
113       initialize_dependency_mechanism
114       initialize_whiny_nils
115       initialize_temporary_session_directory
116       initialize_time_zone
117       initialize_framework_settings
118
119       add_support_load_paths
120
121       load_gems
122       load_plugins
123
124       # pick up any gems that plugins depend on
125       add_gem_load_paths
126       load_gems
127
128       load_application_initializers
129
130       # the framework is now fully initialized
131       after_initialize
132
133       # Routing must be initialized after plugins to allow the former to extend the routes
134       initialize_routing
135
136       # Observers are loaded after plugins in case Observers or observed models are modified by plugins.
137       load_observers
138     end
139
140     # Check for valid Ruby version
141     # This is done in an external file, so we can use it
142     # from the `rails` program as well without duplication.
143     def check_ruby_version
144       require 'ruby_version_check'
145     end
146
147     # If Rails is vendored and RubyGems is available, install stub GemSpecs
148     # for Rails, ActiveSupport, ActiveRecord, ActionPack, ActionMailer, and
149     # ActiveResource. This allows Gem plugins to depend on Rails even when
150     # the Gem version of Rails shouldn't be loaded.
151     def install_gem_spec_stubs
152       if Rails.vendor_rails?
153         begin; require "rubygems"; rescue LoadError; return; end
154
155         stubs = %w(rails activesupport activerecord actionpack actionmailer activeresource)
156         stubs.reject! { |s| Gem.loaded_specs.key?(s) }
157
158         stubs.each do |stub|
159           Gem.loaded_specs[stub] = Gem::Specification.new do |s|
160             s.name = stub
161             s.version = Rails::VERSION::STRING
162           end
163         end
164       end
165     end
166
167     # Set the <tt>$LOAD_PATH</tt> based on the value of
168     # Configuration#load_paths. Duplicates are removed.
169     def set_load_path
170       load_paths = configuration.load_paths + configuration.framework_paths
171       load_paths.reverse_each { |dir| $LOAD_PATH.unshift(dir) if File.directory?(dir) }
172       $LOAD_PATH.uniq!
173     end
174
175     # Set the paths from which Rails will automatically load source files, and
176     # the load_once paths.
177     def set_autoload_paths
178       Dependencies.load_paths = configuration.load_paths.uniq
179       Dependencies.load_once_paths = configuration.load_once_paths.uniq
180
181       extra = Dependencies.load_once_paths - Dependencies.load_paths
182       unless extra.empty?
183         abort <<-end_error
184           load_once_paths must be a subset of the load_paths.
185           Extra items in load_once_paths: #{extra * ','}
186         end_error
187       end
188
189       # Freeze the arrays so future modifications will fail rather than do nothing mysteriously
190       configuration.load_once_paths.freeze
191     end
192
193     # Requires all frameworks specified by the Configuration#frameworks
194     # list. By default, all frameworks (ActiveRecord, ActiveSupport,
195     # ActionPack, ActionMailer, and ActiveResource) are loaded.
196     def require_frameworks
197       configuration.frameworks.each { |framework| require(framework.to_s) }
198     rescue LoadError => e
199       # re-raise because Mongrel would swallow it
200       raise e.to_s
201     end
202
203     # Add the load paths used by support functions such as the info controller
204     def add_support_load_paths
205     end
206
207     # Adds all load paths from plugins to the global set of load paths, so that
208     # code from plugins can be required (explicitly or automatically via Dependencies).
209     def add_plugin_load_paths
210       plugin_loader.add_plugin_load_paths
211     end
212
213     def add_gem_load_paths
214       unless @configuration.gems.empty?
215         require "rubygems"
216         @configuration.gems.each &:add_load_paths
217       end
218     end
219
220     def load_gems
221       @configuration.gems.each &:load
222     end
223
224     # Loads all plugins in <tt>config.plugin_paths</tt>.  <tt>plugin_paths</tt>
225     # defaults to <tt>vendor/plugins</tt> but may also be set to a list of
226     # paths, such as
227     #   config.plugin_paths = ["#{RAILS_ROOT}/lib/plugins", "#{RAILS_ROOT}/vendor/plugins"]
228     #
229     # In the default implementation, as each plugin discovered in <tt>plugin_paths</tt> is initialized:
230     # * its +lib+ directory, if present, is added to the load path (immediately after the applications lib directory)
231     # * <tt>init.rb</tt> is evaluated, if present
232     #
233     # After all plugins are loaded, duplicates are removed from the load path.
234     # If an array of plugin names is specified in config.plugins, only those plugins will be loaded
235     # and they plugins will be loaded in that order. Otherwise, plugins are loaded in alphabetical
236     # order.
237     #
238     # if config.plugins ends contains :all then the named plugins will be loaded in the given order and all other
239     # plugins will be loaded in alphabetical order
240     def load_plugins
241       plugin_loader.load_plugins
242     end
243
244     def plugin_loader
245       @plugin_loader ||= configuration.plugin_loader.new(self)
246     end
247
248     # Loads the environment specified by Configuration#environment_path, which
249     # is typically one of development, test, or production.
250     def load_environment
251       silence_warnings do
252         return if @environment_loaded
253         @environment_loaded = true
254        
255         config = configuration
256         constants = self.class.constants
257        
258         eval(IO.read(configuration.environment_path), binding, configuration.environment_path)
259        
260         (self.class.constants - constants).each do |const|
261           Object.const_set(const, self.class.const_get(const))
262         end
263       end
264     end
265
266     def load_observers
267       if configuration.frameworks.include?(:active_record)
268         if @configuration.gems.any? { |g| !g.loaded? }
269           puts "Unable to instantiate observers, some gems that this application depends on are missing.  Run 'rake gems:install'"
270         else
271           ActiveRecord::Base.instantiate_observers
272         end
273       end
274     end
275
276     # For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the
277     # multibyte safe operations. Plugin authors supporting other encodings
278     # should override this behaviour and set the relevant +default_charset+
279     # on ActionController::Base.
280     #
281     # For Ruby 1.9, this does nothing. Specify the default encoding in the Ruby
282     # shebang line if you don't want UTF-8.
283     def initialize_encoding
284       $KCODE='u' if RUBY_VERSION < '1.9'
285     end
286
287     # This initialization routine does nothing unless <tt>:active_record</tt>
288     # is one of the frameworks to load (Configuration#frameworks). If it is,
289     # this sets the database configuration from Configuration#database_configuration
290     # and then establishes the connection.
291     def initialize_database
292       if configuration.frameworks.include?(:active_record)
293         ActiveRecord::Base.configurations = configuration.database_configuration
294         ActiveRecord::Base.establish_connection
295       end
296     end
297
298     def initialize_cache
299       unless defined?(RAILS_CACHE)
300         silence_warnings { Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(configuration.cache_store) }
301       end
302     end
303
304     def initialize_framework_caches
305       if configuration.frameworks.include?(:action_controller)
306         ActionController::Base.cache_store ||= RAILS_CACHE
307       end
308     end
309
310     # If the +RAILS_DEFAULT_LOGGER+ constant is already set, this initialization
311     # routine does nothing. If the constant is not set, and Configuration#logger
312     # is not +nil+, this also does nothing. Otherwise, a new logger instance
313     # is created at Configuration#log_path, with a default log level of
314     # Configuration#log_level.
315     #
316     # If the log could not be created, the log will be set to output to
317     # +STDERR+, with a log level of +WARN+.
318     def initialize_logger
319       # if the environment has explicitly defined a logger, use it
320       return if defined?(RAILS_DEFAULT_LOGGER)
321
322       unless logger = configuration.logger
323         begin
324           logger = ActiveSupport::BufferedLogger.new(configuration.log_path)
325           logger.level = ActiveSupport::BufferedLogger.const_get(configuration.log_level.to_s.upcase)
326           if configuration.environment == "production"
327             logger.auto_flushing = false
328             logger.set_non_blocking_io
329           end
330         rescue StandardError => e
331           logger = ActiveSupport::BufferedLogger.new(STDERR)
332           logger.level = ActiveSupport::BufferedLogger::WARN
333           logger.warn(
334             "Rails Error: Unable to access log file. Please ensure that #{configuration.log_path} exists and is chmod 0666. " +
335             "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
336           )
337         end
338       end
339
340       silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
341     end
342
343     # Sets the logger for ActiveRecord, ActionController, and ActionMailer
344     # (but only for those frameworks that are to be loaded). If the framework's
345     # logger is already set, it is not changed, otherwise it is set to use
346     # +RAILS_DEFAULT_LOGGER+.
347     def initialize_framework_logging
348       for framework in ([ :active_record, :action_controller, :action_mailer ] & configuration.frameworks)
349         framework.to_s.camelize.constantize.const_get("Base").logger ||= RAILS_DEFAULT_LOGGER
350       end
351      
352       RAILS_CACHE.logger ||= RAILS_DEFAULT_LOGGER
353     end
354
355     # Sets +ActionController::Base#view_paths+ and +ActionMailer::Base#template_root+
356     # (but only for those frameworks that are to be loaded). If the framework's
357     # paths have already been set, it is not changed, otherwise it is
358     # set to use Configuration#view_path.
359     def initialize_framework_views
360       ActionMailer::Base.template_root ||= configuration.view_path  if configuration.frameworks.include?(:action_mailer)
361       ActionController::Base.view_paths = [configuration.view_path] if configuration.frameworks.include?(:action_controller) && ActionController::Base.view_paths.empty?
362     end
363
364     # If ActionController is not one of the loaded frameworks (Configuration#frameworks)
365     # this does nothing. Otherwise, it loads the routing definitions and sets up
366     # loading module used to lazily load controllers (Configuration#controller_paths).
367     def initialize_routing
368       return unless configuration.frameworks.include?(:action_controller)
369       ActionController::Routing.controller_paths = configuration.controller_paths
370       ActionController::Routing::Routes.reload
371     end
372
373     # Sets the dependency loading mechanism based on the value of
374     # Configuration#cache_classes.
375     def initialize_dependency_mechanism
376       Dependencies.mechanism = configuration.cache_classes ? :require : :load
377     end
378
379     # Loads support for "whiny nil" (noisy warnings when methods are invoked
380     # on +nil+ values) if Configuration#whiny_nils is true.
381     def initialize_whiny_nils
382       require('active_support/whiny_nil') if configuration.whiny_nils
383     end
384
385     def initialize_temporary_session_directory
386       if configuration.frameworks.include?(:action_controller)
387         session_path = "#{configuration.root_path}/tmp/sessions/"
388         ActionController::Base.session_options[:tmpdir] = File.exist?(session_path) ? session_path : Dir::tmpdir
389       end
390     end
391
392     # Sets the default value for Time.zone, and turns on ActiveRecord time_zone_aware_attributes.
393     # If assigned value cannot be matched to a TimeZone, an exception will be raised.
394     def initialize_time_zone
395       if configuration.time_zone
396         zone_default = Time.send!(:get_zone, configuration.time_zone)
397         unless zone_default
398           raise "Value assigned to config.time_zone not recognized. Run `rake -D time` for a list of tasks for finding appropriate time zone names."
399         end
400         Time.zone_default = zone_default
401         if configuration.frameworks.include?(:active_record)
402           ActiveRecord::Base.time_zone_aware_attributes = true
403           ActiveRecord::Base.default_timezone = :utc
404         end
405       end
406     end
407
408     # Initializes framework-specific settings for each of the loaded frameworks
409     # (Configuration#frameworks). The available settings map to the accessors
410     # on each of the corresponding Base classes.
411     def initialize_framework_settings
412       configuration.frameworks.each do |framework|
413         base_class = framework.to_s.camelize.constantize.const_get("Base")
414
415         configuration.send(framework).each do |setting, value|
416           base_class.send("#{setting}=", value)
417         end
418       end
419       configuration.active_support.each do |setting, value|
420         ActiveSupport.send("#{setting}=", value)
421       end
422     end
423
424     # Fires the user-supplied after_initialize block (Configuration#after_initialize)
425     def after_initialize
426       configuration.after_initialize_blocks.each do |block|
427         block.call
428       end
429     end
430
431     def load_application_initializers
432       Dir["#{configuration.root_path}/config/initializers/**/*.rb"].sort.each do |initializer|
433         load(initializer)
434       end
435     end
436
437   end
438
439   # The Configuration class holds all the parameters for the Initializer and
440   # ships with defaults that suites most Rails applications. But it's possible
441   # to overwrite everything. Usually, you'll create an Configuration file
442   # implicitly through the block running on the Initializer, but it's also
443   # possible to create the Configuration instance in advance and pass it in
444   # like this:
445   #
446   #   config = Rails::Configuration.new
447   #   Rails::Initializer.run(:process, config)
448   class Configuration
449     # The application's base directory.
450     attr_reader :root_path
451
452     # A stub for setting options on ActionController::Base
453     attr_accessor :action_controller
454
455     # A stub for setting options on ActionMailer::Base
456     attr_accessor :action_mailer
457
458     # A stub for setting options on ActionView::Base
459     attr_accessor :action_view
460
461     # A stub for setting options on ActiveRecord::Base
462     attr_accessor :active_record
463
464     # A stub for setting options on ActiveRecord::Base
465     attr_accessor :active_resource
466
467     # A stub for setting options on ActiveSupport
468     attr_accessor :active_support
469
470     # Whether or not classes should be cached (set to false if you want
471     # application classes to be reloaded on each request)
472     attr_accessor :cache_classes
473
474     # The list of paths that should be searched for controllers. (Defaults
475     # to <tt>app/controllers</tt> and <tt>components</tt>.)
476     attr_accessor :controller_paths
477
478     # The path to the database configuration file to use. (Defaults to
479     # <tt>config/database.yml</tt>.)
480     attr_accessor :database_configuration_file
481
482     # The list of rails framework components that should be loaded. (Defaults
483     # to <tt>:active_record</tt>, <tt>:action_controller</tt>,
484     # <tt>:action_view</tt>, <tt>:action_mailer</tt>, and
485     # <tt>:active_resource</tt>).
486     attr_accessor :frameworks
487
488     # An array of additional paths to prepend to the load path. By default,
489     # all +app+, +lib+, +vendor+ and mock paths are included in this list.
490     attr_accessor :load_paths
491
492     # An array of paths from which Rails will automatically load from only once.
493     # All elements of this array must also be in +load_paths+.
494     attr_accessor :load_once_paths
495
496     # The log level to use for the default Rails logger. In production mode,
497     # this defaults to <tt>:info</tt>. In development mode, it defaults to
498     # <tt>:debug</tt>.
499     attr_accessor :log_level
500
501     # The path to the log file to use. Defaults to log/#{environment}.log
502     # (e.g. log/development.log or log/production.log).
503     attr_accessor :log_path
504
505     # The specific logger to use. By default, a logger will be created and
506     # initialized using #log_path and #log_level, but a programmer may
507     # specifically set the logger to use via this accessor and it will be
508     # used directly.
509     attr_accessor :logger
510
511     # The specific cache store to use. By default, the ActiveSupport::Cache::Store will be used.
512     attr_accessor :cache_store
513
514     # The root of the application's views. (Defaults to <tt>app/views</tt>.)
515     attr_accessor :view_path
516
517     # Set to +true+ if you want to be warned (noisily) when you try to invoke
518     # any method of +nil+. Set to +false+ for the standard Ruby behavior.
519     attr_accessor :whiny_nils
520
521     # The list of plugins to load. If this is set to <tt>nil</tt>, all plugins will
522     # be loaded. If this is set to <tt>[]</tt>, no plugins will be loaded. Otherwise,
523     # plugins will be loaded in the order specified.
524     attr_reader :plugins
525     def plugins=(plugins)
526       @plugins = plugins.nil? ? nil : plugins.map { |p| p.to_sym }
527     end
528
529     # The path to the root of the plugins directory. By default, it is in
530     # <tt>vendor/plugins</tt>.
531     attr_accessor :plugin_paths
532
533     # The classes that handle finding the desired plugins that you'd like to load for
534     # your application. By default it is the Rails::Plugin::FileSystemLocator which finds
535     # plugins to load in <tt>vendor/plugins</tt>. You can hook into gem location by subclassing
536     # Rails::Plugin::Locator and adding it onto the list of <tt>plugin_locators</tt>.
537     attr_accessor :plugin_locators
538
539     # The class that handles loading each plugin. Defaults to Rails::Plugin::Loader, but
540     # a sub class would have access to fine grained modification of the loading behavior. See
541     # the implementation of Rails::Plugin::Loader for more details.
542     attr_accessor :plugin_loader
543    
544     # Enables or disables plugin reloading.  You can get around this setting per plugin.
545     # If #reload_plugins? == false, add this to your plugin's init.rb to make it reloadable:
546     #
547     #   Dependencies.load_once_paths.delete lib_path
548     #
549     # If #reload_plugins? == true, add this to your plugin's init.rb to only load it once:
550     #
551     #   Dependencies.load_once_paths << lib_path
552     #
553     attr_accessor :reload_plugins
554
555     # Returns true if plugin reloading is enabled.
556     def reload_plugins?
557       !!@reload_plugins
558     end
559
560     # An array of gems that this rails application depends on.  Rails will automatically load
561     # these gems during installation, and allow you to install any missing gems with:
562     #
563     #   rake gems:install
564     #
565     # You can add gems with the #gem method.
566     attr_accessor :gems
567
568     # Adds a single Gem dependency to the rails application.
569     #
570     #   # gem 'aws-s3', '>= 0.4.0'
571     #   # require 'aws/s3'
572     #   config.gem 'aws-s3', :lib => 'aws/s3', :version => '>= 0.4.0', \
573     #     :source => "http://code.whytheluckystiff.net"
574     #
575     def gem(name, options = {})
576       @gems << Rails::GemDependency.new(name, options)
577     end
578    
579     # Deprecated options:
580     def breakpoint_server(_ = nil)
581       $stderr.puts %(
582       *******************************************************************
583       * config.breakpoint_server has been deprecated and has no effect. *
584       *******************************************************************
585       )
586     end
587     alias_method :breakpoint_server=, :breakpoint_server
588
589     # Sets the default time_zone.  Setting this will enable time_zone
590     # awareness for ActiveRecord models and set the ActiveRecord default
591     # timezone to :utc.
592     attr_accessor :time_zone
593
594     # Create a new Configuration instance, initialized with the default
595     # values.
596     def initialize
597       set_root_path!
598
599       self.frameworks                   = default_frameworks
600       self.load_paths                   = default_load_paths
601       self.load_once_paths              = default_load_once_paths
602       self.log_path                     = default_log_path
603       self.log_level                    = default_log_level
604       self.view_path                    = default_view_path
605       self.controller_paths             = default_controller_paths
606       self.cache_classes                = default_cache_classes
607       self.whiny_nils                   = default_whiny_nils
608       self.plugins                      = default_plugins
609       self.plugin_paths                 = default_plugin_paths
610       self.plugin_locators              = default_plugin_locators
611       self.plugin_loader                = default_plugin_loader
612       self.database_configuration_file  = default_database_configuration_file
613       self.gems                         = default_gems
614
615       for framework in default_frameworks
616         self.send("#{framework}=", Rails::OrderedOptions.new)
617       end
618       self.active_support = Rails::OrderedOptions.new
619     end
620
621     # Set the root_path to RAILS_ROOT and canonicalize it.
622     def set_root_path!
623       raise 'RAILS_ROOT is not set' unless defined?(::RAILS_ROOT)
624       raise 'RAILS_ROOT is not a directory' unless File.directory?(::RAILS_ROOT)
625
626       @root_path =
627         # Pathname is incompatible with Windows, but Windows doesn't have
628         # real symlinks so File.expand_path is safe.
629         if RUBY_PLATFORM =~ /(:?mswin|mingw)/
630           File.expand_path(::RAILS_ROOT)
631
632         # Otherwise use Pathname#realpath which respects symlinks.
633         else
634           Pathname.new(::RAILS_ROOT).realpath.to_s
635         end
636      
637       Object.const_set(:RELATIVE_RAILS_ROOT, ::RAILS_ROOT.dup) unless defined?(::RELATIVE_RAILS_ROOT)
638       ::RAILS_ROOT.replace @root_path
639     end
640
641     # Loads and returns the contents of the #database_configuration_file. The
642     # contents of the file are processed via ERB before being sent through
643     # YAML::load.
644     def database_configuration
645       YAML::load(ERB.new(IO.read(database_configuration_file)).result)
646     end
647
648     # The path to the current environment's file (development.rb, etc.). By
649     # default the file is at <tt>config/environments/#{environment}.rb</tt>.
650     def environment_path
651       "#{root_path}/config/environments/#{environment}.rb"
652     end
653
654     # Return the currently selected environment. By default, it returns the
655   &n