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

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

Revision 8393, 24.3 kB (checked in by david, 1 year ago)

The initial, rough batch of work

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