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

root/branches/1-2-stable/railties/lib/initializer.rb

Revision 6471, 25.8 kB (checked in by rick, 2 years ago)

documentation project patches, closes #7342, #7319, #7316, #7190 [jeremymcanally]

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
9
10 RAILS_ENV = (ENV['RAILS_ENV'] || 'development').dup unless defined?(RAILS_ENV)
11
12 module Rails
13   # The Initializer is responsible for processing the Rails configuration, such
14   # as setting the $LOAD_PATH, requiring the right frameworks, initializing
15   # logging, and more. It can be run either as a single command that'll just
16   # use the default configuration, like this:
17   #
18   #   Rails::Initializer.run
19   #
20   # But normally it's more interesting to pass in a custom configuration
21   # through the block running:
22   #
23   #   Rails::Initializer.run do |config|
24   #     config.frameworks -= [ :action_web_service ]
25   #   end
26   #
27   # This will use the default configuration options from Rails::Configuration,
28   # but allow for overwriting on select areas.
29   class Initializer
30     # The Configuration instance used by this Initializer instance.
31     attr_reader :configuration
32
33     # The set of loaded plugins.
34     attr_reader :loaded_plugins
35
36     # Runs the initializer. By default, this will invoke the #process method,
37     # which simply executes all of the initialization routines. Alternately,
38     # you can specify explicitly which initialization routine you want:
39     #
40     #   Rails::Initializer.run(:set_load_path)
41     #
42     # This is useful if you only want the load path initialized, without
43     # incuring the overhead of completely loading the entire environment.
44     def self.run(command = :process, configuration = Configuration.new)
45       yield configuration if block_given?
46       initializer = new configuration
47       initializer.send(command)
48       initializer
49     end
50
51     # Create a new Initializer instance that references the given Configuration
52     # instance.
53     def initialize(configuration)
54       @configuration = configuration
55       @loaded_plugins = []
56     end
57
58     # Sequentially step through all of the available initialization routines,
59     # in order:
60     #
61     # * #set_load_path
62     # * #set_connection_adapters
63     # * #require_frameworks
64     # * #load_environment
65     # * #initialize_database
66     # * #initialize_logger
67     # * #initialize_framework_logging
68     # * #initialize_framework_views
69     # * #initialize_dependency_mechanism
70     # * #initialize_breakpoints
71     # * #initialize_whiny_nils
72     # * #initialize_framework_settings
73     # * #load_environment
74     # * #load_plugins
75     # * #load_observers
76     # * #initialize_routing
77     #
78     # (Note that #load_environment is invoked twice, once at the start and
79     # once at the end, to support the legacy configuration style where the
80     # environment could overwrite the defaults directly, instead of via the
81     # Configuration instance.
82     def process
83       check_ruby_version
84       set_load_path
85       set_connection_adapters
86
87       require_frameworks
88       set_autoload_paths
89       load_environment
90
91       initialize_encoding
92       initialize_database
93       initialize_logger
94       initialize_framework_logging
95       initialize_framework_views
96       initialize_dependency_mechanism
97       initialize_breakpoints
98       initialize_whiny_nils
99       initialize_temporary_directories
100       initialize_framework_settings
101
102       # Support for legacy configuration style where the environment
103       # could overwrite anything set from the defaults/global through
104       # the individual base class configurations.
105       load_environment
106
107       add_support_load_paths
108
109       load_plugins
110
111       # Observers are loaded after plugins in case Observers or observed models are modified by plugins.
112       load_observers
113
114       # Routing must be initialized after plugins to allow the former to extend the routes
115       initialize_routing
116
117       # the framework is now fully initialized
118       after_initialize
119     end
120
121     # Check for valid Ruby version
122     # This is done in an external file, so we can use it
123     # from the `rails` program as well without duplication.
124     def check_ruby_version
125       require 'ruby_version_check'
126     end
127
128     # Set the <tt>$LOAD_PATH</tt> based on the value of
129     # Configuration#load_paths. Duplicates are removed.
130     def set_load_path
131       load_paths = configuration.load_paths + configuration.framework_paths
132       load_paths.reverse_each { |dir| $LOAD_PATH.unshift(dir) if File.directory?(dir) }
133       $LOAD_PATH.uniq!
134     end
135
136     # Set the paths from which Rails will automatically load source files, and
137     # the load_once paths.
138     def set_autoload_paths
139       Dependencies.load_paths = configuration.load_paths.uniq
140       Dependencies.load_once_paths = configuration.load_once_paths.uniq
141
142       extra = Dependencies.load_once_paths - Dependencies.load_paths
143       unless extra.empty?
144         abort <<-end_error
145           load_once_paths must be a subset of the load_paths.
146           Extra items in load_once_paths: #{extra * ','}
147         end_error
148       end
149
150       # Freeze the arrays so future modifications will fail rather than do nothing mysteriously
151       configuration.load_once_paths.freeze
152     end
153
154     # Sets the +RAILS_CONNECTION_ADAPTERS+ constant based on the value of
155     # Configuration#connection_adapters. This constant is used to determine
156     # which database adapters should be loaded (by default, all adapters are
157     # loaded).
158     def set_connection_adapters
159       Object.const_set("RAILS_CONNECTION_ADAPTERS", configuration.connection_adapters) if configuration.connection_adapters
160     end
161
162     # Requires all frameworks specified by the Configuration#frameworks
163     # list. By default, all frameworks (ActiveRecord, ActiveSupport,
164     # ActionPack, ActionMailer, and ActionWebService) are loaded.
165     def require_frameworks
166       configuration.frameworks.each { |framework| require(framework.to_s) }
167     end
168
169     # Add the load paths used by support functions such as the info controller
170     def add_support_load_paths
171     end
172
173     # Loads all plugins in <tt>config.plugin_paths</tt>.  <tt>plugin_paths</tt>
174     # defaults to <tt>vendor/plugins</tt> but may also be set to a list of
175     # paths, such as
176     #   config.plugin_paths = ['lib/plugins', 'vendor/plugins']
177     #
178     # Each plugin discovered in <tt>plugin_paths</tt> is initialized:
179     # * add its +lib+ directory, if present, to the beginning of the load path
180     # * evaluate <tt>init.rb</tt> if present
181     #
182     # After all plugins are loaded, duplicates are removed from the load path.
183     # If an array of plugin names is specified in config.plugins, the plugins
184     # will be loaded in that order. Otherwise, plugins are loaded in alphabetical
185     # order.
186     def load_plugins
187       if configuration.plugins.nil?
188         # a nil value implies we don't care about plugins; load 'em all in a reliable order
189         find_plugins(configuration.plugin_paths).sort.each { |path| load_plugin path }
190       elsif !configuration.plugins.empty?
191         # we've specified a config.plugins array, so respect that order
192         plugin_paths = find_plugins(configuration.plugin_paths)
193         configuration.plugins.each do |name|
194           path = plugin_paths.find { |p| File.basename(p) == name }
195           raise(LoadError, "Cannot find the plugin '#{name}'!") if path.nil?
196           load_plugin path
197         end
198       end
199       $LOAD_PATH.uniq!
200     end
201
202     # Loads the environment specified by Configuration#environment_path, which
203     # is typically one of development, testing, or production.
204     def load_environment
205       silence_warnings do
206         config = configuration
207         constants = self.class.constants
208        
209         eval(IO.read(configuration.environment_path), binding, configuration.environment_path)
210        
211         (self.class.constants - constants).each do |const|
212           Object.const_set(const, self.class.const_get(const))
213         end
214       end
215     end
216
217     def load_observers
218       ActiveRecord::Base.instantiate_observers
219     end
220
221     # This initialzation sets $KCODE to 'u' to enable the multibyte safe operations.
222     # Plugin authors supporting other encodings should override this behaviour and
223     # set the relevant +default_charset+ on ActionController::Base
224     def initialize_encoding
225       $KCODE='u'
226     end
227
228     # This initialization routine does nothing unless <tt>:active_record</tt>
229     # is one of the frameworks to load (Configuration#frameworks). If it is,
230     # this sets the database configuration from Configuration#database_configuration
231     # and then establishes the connection.
232     def initialize_database
233       return unless configuration.frameworks.include?(:active_record)
234       ActiveRecord::Base.configurations = configuration.database_configuration
235       ActiveRecord::Base.establish_connection
236     end
237
238     # If the +RAILS_DEFAULT_LOGGER+ constant is already set, this initialization
239     # routine does nothing. If the constant is not set, and Configuration#logger
240     # is not +nil+, this also does nothing. Otherwise, a new logger instance
241     # is created at Configuration#log_path, with a default log level of
242     # Configuration#log_level.
243     #
244     # If the log could not be created, the log will be set to output to
245     # +STDERR+, with a log level of +WARN+.
246     def initialize_logger
247       # if the environment has explicitly defined a logger, use it
248       return if defined?(RAILS_DEFAULT_LOGGER)
249
250       unless logger = configuration.logger
251         begin
252           logger = Logger.new(configuration.log_path)
253           logger.level = Logger.const_get(configuration.log_level.to_s.upcase)
254         rescue StandardError
255           logger = Logger.new(STDERR)
256           logger.level = Logger::WARN
257           logger.warn(
258             "Rails Error: Unable to access log file. Please ensure that #{configuration.log_path} exists and is chmod 0666. " +
259             "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
260           )
261         end
262       end
263
264       silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
265     end
266
267     # Sets the logger for ActiveRecord, ActionController, and ActionMailer
268     # (but only for those frameworks that are to be loaded). If the framework's
269     # logger is already set, it is not changed, otherwise it is set to use
270     # +RAILS_DEFAULT_LOGGER+.
271     def initialize_framework_logging
272       for framework in ([ :active_record, :action_controller, :action_mailer ] & configuration.frameworks)
273         framework.to_s.camelize.constantize.const_get("Base").logger ||= RAILS_DEFAULT_LOGGER
274       end
275     end
276
277     # Sets the +template_root+ for ActionController::Base and ActionMailer::Base
278     # (but only for those frameworks that are to be loaded). If the framework's
279     # +template_root+ has already been set, it is not changed, otherwise it is
280     # set to use Configuration#view_path.
281     def initialize_framework_views
282       for framework in ([ :action_controller, :action_mailer ] & configuration.frameworks)
283         framework.to_s.camelize.constantize.const_get("Base").template_root ||= configuration.view_path
284       end
285     end
286
287     # If ActionController is not one of the loaded frameworks (Configuration#frameworks)
288     # this does nothing. Otherwise, it loads the routing definitions and sets up
289     # loading module used to lazily load controllers (Configuration#controller_paths).
290     def initialize_routing
291       return unless configuration.frameworks.include?(:action_controller)
292       ActionController::Routing.controller_paths = configuration.controller_paths
293       ActionController::Routing::Routes.reload
294     end
295
296     # Sets the dependency loading mechanism based on the value of
297     # Configuration#cache_classes.
298     def initialize_dependency_mechanism
299       Dependencies.mechanism = configuration.cache_classes ? :require : :load
300     end
301
302     # Sets the +BREAKPOINT_SERVER_PORT+ if Configuration#breakpoint_server
303     # is true.
304     def initialize_breakpoints
305       silence_warnings { Object.const_set("BREAKPOINT_SERVER_PORT", 42531) if configuration.breakpoint_server }
306     end
307
308     # Loads support for "whiny nil" (noisy warnings when methods are invoked
309     # on +nil+ values) if Configuration#whiny_nils is true.
310     def initialize_whiny_nils
311       require('active_support/whiny_nil') if configuration.whiny_nils
312     end
313
314     def initialize_temporary_directories
315       if configuration.frameworks.include?(:action_controller)
316         session_path = "#{configuration.root_path}/tmp/sessions/"
317         ActionController::Base.session_options[:tmpdir] = File.exist?(session_path) ? session_path : Dir::tmpdir
318
319         cache_path = "#{configuration.root_path}/tmp/cache/"
320         if File.exist?(cache_path)
321           ActionController::Base.fragment_cache_store = :file_store, cache_path
322         end
323       end
324     end
325
326     # Initializes framework-specific settings for each of the loaded frameworks
327     # (Configuration#frameworks). The available settings map to the accessors
328     # on each of the corresponding Base classes.
329     def initialize_framework_settings
330       configuration.frameworks.each do |framework|
331         base_class = framework.to_s.camelize.constantize.const_get("Base")
332
333         configuration.send(framework).each do |setting, value|
334           base_class.send("#{setting}=", value)
335         end
336       end
337     end
338
339     # Fires the user-supplied after_initialize block (Configuration#after_initialize)
340     def after_initialize
341       configuration.after_initialize_block.call if configuration.after_initialize_block
342     end
343
344     protected
345       # Return a list of plugin paths within base_path.  A plugin path is
346       # a directory that contains either a lib directory or an init.rb file.
347       # This recurses into directories which are not plugin paths, so you
348       # may organize your plugins within the plugin path.
349       def find_plugins(*base_paths)
350         base_paths.flatten.inject([]) do |plugins, base_path|
351           Dir.glob(File.join(base_path, '*')).each do |path|
352             if plugin_path?(path)
353               plugins << path if plugin_enabled?(path)
354             elsif File.directory?(path)
355               plugins += find_plugins(path)
356             end
357           end
358           plugins
359         end
360       end
361
362       def plugin_path?(path)
363         File.directory?(path) and (File.directory?(File.join(path, 'lib')) or File.file?(File.join(path, 'init.rb')))
364       end
365
366       def plugin_enabled?(path)
367         configuration.plugins.nil? || configuration.plugins.include?(File.basename(path))
368       end
369
370       # Load the plugin at <tt>path</tt> unless already loaded.
371       #
372       # Each plugin is initialized:
373       # * add its +lib+ directory, if present, to the beginning of the load path
374       # * evaluate <tt>init.rb</tt> if present
375       #
376       # Returns <tt>true</tt> if the plugin is successfully loaded or
377       # <tt>false</tt> if it is already loaded (similar to Kernel#require).
378       # Raises <tt>LoadError</tt> if the plugin is not found.
379       def load_plugin(directory)
380         name = File.basename(directory)
381         return false if loaded_plugins.include?(name)
382
383         # Catch nonexistent and empty plugins.
384         raise LoadError, "No such plugin: #{directory}" unless plugin_path?(directory)
385
386         lib_path  = File.join(directory, 'lib')
387         init_path = File.join(directory, 'init.rb')
388         has_lib   = File.directory?(lib_path)
389         has_init  = File.file?(init_path)
390
391         # Add lib to load path *after* the application lib, to allow
392         # application libraries to override plugin libraries.
393         if has_lib
394           application_lib_index = $LOAD_PATH.index(File.join(RAILS_ROOT, "lib")) || 0
395           $LOAD_PATH.insert(application_lib_index + 1, lib_path)
396           Dependencies.load_paths << lib_path
397           Dependencies.load_once_paths << lib_path
398         end
399
400         # Allow plugins to reference the current configuration object
401         config = configuration
402        
403         # Add to set of loaded plugins before 'name' collapsed in eval.
404         loaded_plugins << name
405
406         # Evaluate init.rb.
407         silence_warnings { eval(IO.read(init_path), binding, init_path) } if has_init
408
409         true
410       end
411   end
412
413   # The Configuration class holds all the parameters for the Initializer and
414   # ships with defaults that suites most Rails applications. But it's possible
415   # to overwrite everything. Usually, you'll create an Configuration file
416   # implicitly through the block running on the Initializer, but it's also
417   # possible to create the Configuration instance in advance and pass it in
418   # like this:
419   #
420   #   config = Rails::Configuration.new
421   #   Rails::Initializer.run(:process, config)
422   class Configuration
423     # The application's base directory.
424     attr_reader :root_path
425
426     # A stub for setting options on ActionController::Base
427     attr_accessor :action_controller
428
429     # A stub for setting options on ActionMailer::Base
430     attr_accessor :action_mailer
431
432     # A stub for setting options on ActionView::Base
433     attr_accessor :action_view
434
435     # A stub for setting options on ActionWebService::Base
436     attr_accessor :action_web_service
437
438     # A stub for setting options on ActiveRecord::Base
439     attr_accessor :active_record
440
441     # Whether or not to use the breakpoint server (boolean)
442     attr_accessor :breakpoint_server
443
444     # Whether or not classes should be cached (set to false if you want
445     # application classes to be reloaded on each request)
446     attr_accessor :cache_classes
447
448     # The list of connection adapters to load. (By default, all connection
449     # adapters are loaded. You can set this to be just the adapter(s) you
450     # will use to reduce your application's load time.)
451     attr_accessor :connection_adapters
452
453     # The list of paths that should be searched for controllers. (Defaults
454     # to <tt>app/controllers</tt> and <tt>components</tt>.)
455     attr_accessor :controller_paths
456
457     # The path to the database configuration file to use. (Defaults to
458     # <tt>config/database.yml</tt>.)
459     attr_accessor :database_configuration_file
460
461     # The list of rails framework components that should be loaded. (Defaults
462     # to <tt>:active_record</tt>, <tt>:action_controller</tt>,
463     # <tt>:action_view</tt>, <tt>:action_mailer</tt>, and
464     # <tt>:action_web_service</tt>).
465     attr_accessor :frameworks
466
467     # An array of additional paths to prepend to the load path. By default,
468     # all +app+, +lib+, +vendor+ and mock paths are included in this list.
469     attr_accessor :load_paths
470
471     # An array of paths from which Rails will automatically load from only once.
472     # All elements of this array must also be in +load_paths+.
473     attr_accessor :load_once_paths
474
475     # The log level to use for the default Rails logger. In production mode,
476     # this defaults to <tt>:info</tt>. In development mode, it defaults to
477     # <tt>:debug</tt>.
478     attr_accessor :log_level
479
480     # The path to the log file to use. Defaults to log/#{environment}.log
481     # (e.g. log/development.log or log/production.log).
482     attr_accessor :log_path
483
484     # The specific logger to use. By default, a logger will be created and
485     # initialized using #log_path and #log_level, but a programmer may
486     # specifically set the logger to use via this accessor and it will be
487     # used directly.
488     attr_accessor :logger
489
490     # The root of the application's views. (Defaults to <tt>app/views</tt>.)
491     attr_accessor :view_path
492
493     # Set to +true+ if you want to be warned (noisily) when you try to invoke
494     # any method of +nil+. Set to +false+ for the standard Ruby behavior.
495     attr_accessor :whiny_nils
496
497     # The list of plugins to load. If this is set to <tt>nil</tt>, all plugins will
498     # be loaded. If this is set to <tt>[]</tt>, no plugins will be loaded. Otherwise,
499     # plugins will be loaded in the order specified.
500     attr_accessor :plugins
501
502     # The path to the root of the plugins directory. By default, it is in
503     # <tt>vendor/plugins</tt>.
504     attr_accessor :plugin_paths
505
506     # Create a new Configuration instance, initialized with the default
507     # values.
508     def initialize
509       set_root_path!
510
511       self.frameworks                   = default_frameworks
512       self.load_paths                   = default_load_paths
513       self.load_once_paths              = default_load_once_paths
514       self.log_path                     = default_log_path
515       self.log_level                    = default_log_level
516       self.view_path                    = default_view_path
517       self.controller_paths             = default_controller_paths
518       self.cache_classes                = default_cache_classes
519       self.breakpoint_server            = default_breakpoint_server
520       self.whiny_nils                   = default_whiny_nils
521       self.plugins                      = default_plugins
522       self.plugin_paths                 = default_plugin_paths
523       self.database_configuration_file  = default_database_configuration_file
524
525       for framework in default_frameworks
526         self.send("#{framework}=", Rails::OrderedOptions.new)
527       end
528     end
529
530     # Set the root_path to RAILS_ROOT and canonicalize it.
531     def set_root_path!
532       raise 'RAILS_ROOT is not set' unless defined?(::RAILS_ROOT)
533       raise 'RAILS_ROOT is not a directory' unless File.directory?(::RAILS_ROOT)
534
535       @root_path =
536         # Pathname is incompatible with Windows, but Windows doesn't have
537         # real symlinks so File.expand_path is safe.
538         if RUBY_PLATFORM =~ /(:?mswin|mingw)/
539           File.expand_path(::RAILS_ROOT)
540
541         # Otherwise use Pathname#realpath which respects symlinks.
542         else
543           Pathname.new(::RAILS_ROOT).realpath.to_s
544         end
545     end
546
547     # Loads and returns the contents of the #database_configuration_file. The
548     # contents of the file are processed via ERB before being sent through
549     # YAML::load.
550     def database_configuration
551       YAML::load(ERB.new(IO.read(database_configuration_file)).result)
552     end
553
554     # The path to the current environment's file (development.rb, etc.). By
555     # default the file is at <tt>config/environments/#{environment}.rb</tt>.
556     def environment_path
557       "#{root_path}/config/environments/#{environment}.rb"
558     end
559
560     # Return the currently selected environment. By default, it returns the
561     # value of the +RAILS_ENV+ constant.
562     def environment
563       ::RAILS_ENV
564     end
565
566     # Sets a block which will be executed after rails has been fully initialized.
567     # Useful for per-environment configuration which depends on the framework being
568     # fully initialized.
569     def after_initialize(&after_initialize_block)
570       @after_initialize_block = after_initialize_block
571     end
572
573     # Returns the block set in Configuration#after_initialize
574     def after_initialize_block
575       @after_initialize_block
576     end
577
578     # Add a preparation callback that will run before every request in development
579     # mode, or before the first request in production.
580     #
581     # See Dispatcher#to_prepare.
582     def to_prepare(&callback)
583       require 'dispatcher' unless defined?(::Dispatcher)
584       Dispatcher.to_prepare(&callback)
585     end
586
587     def builtin_directories
588       # Include builtins only in the development environment.
589       (environment == 'development') ? Dir["#{RAILTIES_PATH}/builtin/*/"] : []
590     end
591
592     def framework_paths
593       # TODO: Don't include dirs for frameworks that are not used
594       %w(
595         railties
596         railties/lib
597         actionpack/lib
598         activesupport/lib
599         activerecord/lib
600         actionmailer/lib
601         actionwebservice/lib
602       ).map { |dir| "#{framework_root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
603     end
604
605     private
606       def framework_root_path
607         defined?(::RAILS_FRAMEWORK_ROOT) ? ::RAILS_FRAMEWORK_ROOT : "#{root_path}/vendor/rails"
608       end
609
610       def default_frameworks
611         [ :active_record, :action_controller, :action_view, :action_mailer, :action_web_service ]
612       end
613
614       def default_load_paths
615         paths = ["#{root_path}/test/mocks/#{environment}"]
616
617         # Add the app's controller directory
618         paths.concat(Dir["#{root_path}/app/controllers/"])
619
620         # Then components subdirectories.
621         paths.concat(Dir["#{root_path}/components/[_a-z]*"])
622
623         # Followed by the standard includes.
624         paths.concat %w(
625           app
626           app/models
627           app/controllers
628           app/helpers
629           app/services
630           app/apis
631           components
632           config
633           lib
634