diff options
author | Xavier Noria <fxn@hashref.com> | 2011-06-06 22:23:30 +0200 |
---|---|---|
committer | Xavier Noria <fxn@hashref.com> | 2011-06-06 22:23:30 +0200 |
commit | 5e21247131fa7d5484190c9a71b74f9d3f090684 (patch) | |
tree | 5eee39fcabb952cafd513caf7e67943bc6b26e98 /railties/guides/source/initialization.textile | |
parent | 38ad6bb2f566202dd522a0cf31a55a746f122d53 (diff) | |
parent | 689e12b828665b7b2cfcda85d46249c5cf2aa713 (diff) | |
download | rails-5e21247131fa7d5484190c9a71b74f9d3f090684.tar.gz rails-5e21247131fa7d5484190c9a71b74f9d3f090684.tar.bz2 rails-5e21247131fa7d5484190c9a71b74f9d3f090684.zip |
Merge branch 'master' of git://github.com/lifo/docrails
Conflicts:
activerecord/RUNNING_UNIT_TESTS
Diffstat (limited to 'railties/guides/source/initialization.textile')
-rw-r--r-- | railties/guides/source/initialization.textile | 623 |
1 files changed, 312 insertions, 311 deletions
diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index 638830cd83..1d5b0c0c11 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -20,15 +20,15 @@ h4. +bin/rails+ The actual +rails+ command is kept in _bin/rails_ at the and goes like this: <ruby> - #!/usr/bin/env ruby - - begin - require "rails/cli" - rescue LoadError - railties_path = File.expand_path('../../railties/lib', __FILE__) - $:.unshift(railties_path) - require "rails/cli" - end +#!/usr/bin/env ruby + +begin + require "rails/cli" +rescue LoadError + railties_path = File.expand_path('../../railties/lib', __FILE__) + $:.unshift(railties_path) + require "rails/cli" +end </ruby> This file will attempt to load +rails/cli+ and if it cannot find it then add the +railties/lib+ path to the load path (+$:+) and will then try to require it again. @@ -38,22 +38,22 @@ h4. +railites/lib/rails/cli.rb+ This file looks like this: <ruby> - require 'rbconfig' - require 'rails/script_rails_loader' +require 'rbconfig' +require 'rails/script_rails_loader' - # If we are inside a Rails application this method performs an exec and thus - # the rest of this script is not run. - Rails::ScriptRailsLoader.exec_script_rails! +# If we are inside a Rails application this method performs an exec and thus +# the rest of this script is not run. +Rails::ScriptRailsLoader.exec_script_rails! - require 'rails/ruby_version_check' - Signal.trap("INT") { puts; exit } +require 'rails/ruby_version_check' +Signal.trap("INT") { puts; exit } - if ARGV.first == 'plugin' - ARGV.shift - require 'rails/commands/plugin_new' - else - require 'rails/commands/application' - end +if ARGV.first == 'plugin' + ARGV.shift + require 'rails/commands/plugin_new' +else + require 'rails/commands/application' +end </ruby> The +rbconfig+ file here is out of Ruby's standard library and provides us with the +RbConfig+ class which contains useful information dependent on how Ruby was compiled. We'll see this in use in +railties/lib/rails/script_rails_loader+. @@ -76,46 +76,46 @@ The +rails/script_rails_loader+ file uses +RbConfig::Config+ to gather up the +b Back in +rails/cli+, the next line is this: <ruby> - Rails::ScriptRailsLoader.exec_script_rails! +Rails::ScriptRailsLoader.exec_script_rails! </ruby> This method is defined in +rails/script_rails_loader+ like this: <ruby> - def self.exec_script_rails! - cwd = Dir.pwd - return unless in_rails_application? || in_rails_application_subdirectory? - exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application? - Dir.chdir("..") do - # Recurse in a chdir block: if the search fails we want to be sure - # the application is generated in the original working directory. - exec_script_rails! unless cwd == Dir.pwd - end - rescue SystemCallError - # could not chdir, no problem just return +def self.exec_script_rails! + cwd = Dir.pwd + return unless in_rails_application? || in_rails_application_subdirectory? + exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application? + Dir.chdir("..") do + # Recurse in a chdir block: if the search fails we want to be sure + # the application is generated in the original working directory. + exec_script_rails! unless cwd == Dir.pwd end +rescue SystemCallError + # could not chdir, no problem just return +end </ruby> This method will first check if the current working directory (+cwd+) is a Rails application or is a subdirectory of one. The way to determine this is defined in the +in_rails_application?+ method like this: <ruby> - def self.in_rails_application? - File.exists?(SCRIPT_RAILS) - end +def self.in_rails_application? + File.exists?(SCRIPT_RAILS) +end </ruby> The +SCRIPT_RAILS+ constant defined earlier is used here, with +File.exists?+ checking for its presence in the current directory. If this method returns +false+, then +in_rails_application_subdirectory?+ will be used: <ruby> - def self.in_rails_application_subdirectory?(path = Pathname.new(Dir.pwd)) - File.exists?(File.join(path, SCRIPT_RAILS)) || !path.root? && in_rails_application_subdirectory?(path.parent) - end +def self.in_rails_application_subdirectory?(path = Pathname.new(Dir.pwd)) + File.exists?(File.join(path, SCRIPT_RAILS)) || !path.root? && in_rails_application_subdirectory?(path.parent) +end </ruby> This climbs the directory tree until it reaches a path which contains a +script/rails+ file. If a directory is reached which contains this file then this line will run: <ruby> - exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application? +exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application? </ruby> This is effectively the same as doing +ruby script/rails [arguments]+. Where +[arguments]+ at this point in time is simply "server". @@ -125,9 +125,9 @@ h4. +script/rails+ This file looks like this: <ruby> - APP_PATH = File.expand_path('../../config/application', __FILE__) - require File.expand_path('../../config/boot', __FILE__) - require 'rails/commands' +APP_PATH = File.expand_path('../../config/application', __FILE__) +require File.expand_path('../../config/boot', __FILE__) +require 'rails/commands' </ruby> The +APP_PATH+ constant here will be used later in +rails/commands+. The +config/boot+ file that +script/rails+ references is the +config/boot.rb+ file in our application which is responsible for loading Bundler and setting it up. @@ -137,19 +137,19 @@ h4. +config/boot.rb+ +config/boot.rb+ contains this: <ruby> - require 'rubygems' +require 'rubygems' - # Set up gems listed in the Gemfile. - gemfile = File.expand_path('../../Gemfile', __FILE__) - begin - ENV['BUNDLE_GEMFILE'] = gemfile - require 'bundler' - Bundler.setup - rescue Bundler::GemNotFound => e - STDERR.puts e.message - STDERR.puts "Try running `bundle install`." - exit! - end if File.exist?(gemfile) +# Set up gems listed in the Gemfile. +gemfile = File.expand_path('../../Gemfile', __FILE__) +begin + ENV['BUNDLE_GEMFILE'] = gemfile + require 'bundler' + Bundler.setup +rescue Bundler::GemNotFound => e + STDERR.puts e.message + STDERR.puts "Try running `bundle install`." + exit! +end if File.exist?(gemfile) </ruby> In a standard Rails application, there's a +Gemfile+ which declares all dependencies of the application. +config/boot.rb+ sets +ENV["BUNDLE_GEMFILE"]+ to the location of this file, then requires Bundler and calls +Bundler.setup+ which adds the dependencies of the application (including all the Rails parts) to the load path, making them available for the application to load. The gems that a Rails 3.1 application depends on are as follows: @@ -186,34 +186,34 @@ h4. +rails/commands.rb+ Once +config/boot.rb+ has finished, the next file that is required is +rails/commands+ which will execute a command based on the arguments passed in. In this case, the +ARGV+ array simply contains +server+ which is extracted into the +command+ variable using these lines: <ruby> - aliases = { - "g" => "generate", - "c" => "console", - "s" => "server", - "db" => "dbconsole" - } +aliases = { + "g" => "generate", + "c" => "console", + "s" => "server", + "db" => "dbconsole" +} - command = ARGV.shift - command = aliases[command] || command +command = ARGV.shift +command = aliases[command] || command </ruby> If we used <tt>s</tt> rather than +server+, Rails will use the +aliases+ defined in the file and match them to their respective commands. With the +server+ command, Rails will run this code: <ruby> - when 'server' - # Change to the application's path if there is no config.ru file in current dir. - # This allows us to run script/rails server from other directories, but still get - # the main config.ru and properly set the tmp directory. - Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru")) - - require 'rails/commands/server' - Rails::Server.new.tap { |server| - # We need to require application after the server sets environment, - # otherwise the --environment option given to the server won't propagate. - require APP_PATH - Dir.chdir(Rails.application.root) - server.start - } +when 'server' + # Change to the application's path if there is no config.ru file in current dir. + # This allows us to run script/rails server from other directories, but still get + # the main config.ru and properly set the tmp directory. + Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru")) + + require 'rails/commands/server' + Rails::Server.new.tap { |server| + # We need to require application after the server sets environment, + # otherwise the --environment option given to the server won't propagate. + require APP_PATH + Dir.chdir(Rails.application.root) + server.start + } </ruby> This file will change into the root of the directory (a path two directories back from +APP_PATH+ which points at +config/application.rb+), but only if the +config.ru+ file isn't found. This then requires +rails/commands/server+ which requires +action_dispatch+ and sets up the +Rails::Server+ class. @@ -239,7 +239,7 @@ The +methods.rb+ file is responsible for defining methods such as +camelize+, +u In this file there are a lot of lines such as this inside the +ActiveSupport+ module: <ruby> - autoload :Inflector +autoload :Inflector </ruby> Due to the overriding of the +autoload+ method, Ruby will know to look for this file at +activesupport/lib/active_support/inflector.rb+ when the +Inflector+ class is first referenced. @@ -263,10 +263,10 @@ h4. +rails/commands/server.rb+ The +Rails::Server+ class is defined in this file as inheriting from +Rack::Server+. When +Rails::Server.new+ is called, this calls the +initialize+ method in +rails/commands/server.rb+: <ruby> - def initialize(*) - super - set_environment - end +def initialize(*) + super + set_environment +end </ruby> Firstly, +super+ is called which calls the +initialize+ method on +Rack::Server+. @@ -278,10 +278,10 @@ h4. Rack: +lib/rack/server.rb+ The +initialize+ method in +Rack::Server+ simply sets a couple of variables: <ruby> - def initialize(options = nil) - @options = options - @app = options[:app] if options && options[:app] - end +def initialize(options = nil) + @options = options + @app = options[:app] if options && options[:app] +end </ruby> In this case, +options+ will be +nil+ so nothing happens in this method. @@ -289,64 +289,64 @@ In this case, +options+ will be +nil+ so nothing happens in this method. After +super+ has finished in +Rack::Server+, we jump back to +rails/commands/server.rb+. At this point, +set_environment+ is called within the context of the +Rails::Server+ object and this method doesn't appear to do much at first glance: <ruby> - def set_environment - ENV["RAILS_ENV"] ||= options[:environment] - end +def set_environment + ENV["RAILS_ENV"] ||= options[:environment] +end </ruby> In fact, the +options+ method here does quite a lot. This method is defined in +Rack::Server+ like this: <ruby> - def options - @options ||= parse_options(ARGV) - end +def options + @options ||= parse_options(ARGV) +end </ruby> Then +parse_options+ is defined like this: <ruby> - def parse_options(args) - options = default_options +def parse_options(args) + options = default_options - # Don't evaluate CGI ISINDEX parameters. - # http://hoohoo.ncsa.uiuc.edu/cgi/cl.html - args.clear if ENV.include?("REQUEST_METHOD") + # Don't evaluate CGI ISINDEX parameters. + # http://hoohoo.ncsa.uiuc.edu/cgi/cl.html + args.clear if ENV.include?("REQUEST_METHOD") - options.merge! opt_parser.parse! args - options[:config] = ::File.expand_path(options[:config]) - ENV["RACK_ENV"] = options[:environment] - options - end + options.merge! opt_parser.parse! args + options[:config] = ::File.expand_path(options[:config]) + ENV["RACK_ENV"] = options[:environment] + options +end </ruby> With the +default_options+ set to this: <ruby> - def default_options - { - :environment => ENV['RACK_ENV'] || "development", - :pid => nil, - :Port => 9292, - :Host => "0.0.0.0", - :AccessLog => [], - :config => "config.ru" - } - end +def default_options + { + :environment => ENV['RACK_ENV'] || "development", + :pid => nil, + :Port => 9292, + :Host => "0.0.0.0", + :AccessLog => [], + :config => "config.ru" + } +end </ruby> There is no +REQUEST_METHOD+ key in +ENV+ so we can skip over that line. The next line merges in the options from +opt_parser+ which is defined plainly in +Rack::Server+ <ruby> - def opt_parser - Options.new - end +def opt_parser + Options.new +end </ruby> The class *is* defined in +Rack::Server+, but is overwritten in +Rails::Server+ to take different arguments. Its +parse!+ method begins like this: <ruby> - def parse!(args) - args, options = args.dup, {} +def parse!(args) + args, options = args.dup, {} opt_parser = OptionParser.new do |opts| opts.banner = "Usage: rails server [mongrel, thin, etc] [options]" @@ -362,100 +362,101 @@ h4. +Rails::Server#start+ This method is defined like this: <ruby> - def start - puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" - puts "=> Rails #{Rails.version} application starting in #{Rails.env} on http://#{options[:Host]}:#{options[:Port]}" - puts "=> Call with -d to detach" unless options[:daemonize] - trap(:INT) { exit } - puts "=> Ctrl-C to shutdown server" unless options[:daemonize] - - #Create required tmp directories if not found - %w(cache pids sessions sockets).each do |dir_to_make| - FileUtils.mkdir_p(Rails.root.join('tmp', dir_to_make)) - end - - super - ensure - # The '-h' option calls exit before @options is set. - # If we call 'options' with it unset, we get double help banners. - puts 'Exiting' unless @options && options[:daemonize] +def start + puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" + puts "=> Rails #{Rails.version} application starting in #{Rails.env} on http://#{options[:Host]}:#{options[:Port]}" + puts "=> Call with -d to detach" unless options[:daemonize] + trap(:INT) { exit } + puts "=> Ctrl-C to shutdown server" unless options[:daemonize] + + #Create required tmp directories if not found + %w(cache pids sessions sockets).each do |dir_to_make| + FileUtils.mkdir_p(Rails.root.join('tmp', dir_to_make)) end + + super +ensure + # The '-h' option calls exit before @options is set. + # If we call 'options' with it unset, we get double help banners. + puts 'Exiting' unless @options && options[:daemonize] +end </ruby> This is where the first output of the Rails initialization happens. This method creates a trap for +INT+ signals, so if you +CTRL+C+ the server, it will exit the process. As we can see from the code here, it will create the +tmp/cache+, +tmp/pids+, +tmp/sessions+ and +tmp/sockets+ directories if they don't already exist prior to calling +super+. The +super+ method will call +Rack::Server.start+ which begins its definition like this: <ruby> - def start - if options[:warn] - $-w = true - end +def start + if options[:warn] + $-w = true + end - if includes = options[:include] - $LOAD_PATH.unshift(*includes) - end + if includes = options[:include] + $LOAD_PATH.unshift(*includes) + end - if library = options[:require] - require library - end + if library = options[:require] + require library + end - if options[:debug] - $DEBUG = true - require 'pp' - p options[:server] - pp wrapped_app - pp app - end + if options[:debug] + $DEBUG = true + require 'pp' + p options[:server] + pp wrapped_app + pp app + end +end </ruby> In a Rails application, these options are not set at all and therefore aren't used at all. The first line of code that's executed in this method is a call to this method: <ruby> - wrapped_app +wrapped_app </ruby> This method calls another method: <ruby> - @wrapped_app ||= build_app app +@wrapped_app ||= build_app app </ruby> Then the +app+ method here is defined like so: <ruby> - def app - @app ||= begin - if !::File.exist? options[:config] - abort "configuration #{options[:config]} not found" - end - - app, options = Rack::Builder.parse_file(self.options[:config], opt_parser) - self.options.merge! options - app +def app + @app ||= begin + if !::File.exist? options[:config] + abort "configuration #{options[:config]} not found" end + + app, options = Rack::Builder.parse_file(self.options[:config], opt_parser) + self.options.merge! options + app end +end </ruby> The +options[:config]+ value defaults to +config.ru+ which contains this: <ruby> - # This file is used by Rack-based servers to start the application. +# This file is used by Rack-based servers to start the application. - require ::File.expand_path('../config/environment', __FILE__) - run YourApp::Application +require ::File.expand_path('../config/environment', __FILE__) +run YourApp::Application </ruby> The +Rack::Builder.parse_file+ method here takes the content from this +config.ru+ file and parses it using this code: <ruby> - app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app", +app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app", TOPLEVEL_BINDING, config </ruby> The <ruby>initialize</ruby> method will take the block here and execute it within an instance of +Rack::Builder+. This is where the majority of the initialization process of Rails happens. The chain of events that this simple line sets off will be the focus of a large majority of this guide. The +require+ line for +config/environment.rb+ in +config.ru+ is the first to run: <ruby> - require ::File.expand_path('../config/environment', __FILE__) +require ::File.expand_path('../config/environment', __FILE__) </ruby> h4. +config/environment.rb+ @@ -475,7 +476,7 @@ h3. Loading Rails The next line in +config/application.rb+ is: <ruby> - require 'rails/all' +require 'rails/all' </ruby> h4. +railties/lib/rails/all.rb+ @@ -483,20 +484,20 @@ h4. +railties/lib/rails/all.rb+ This file is responsible for requiring all the individual parts of Rails like so: <ruby> - require "rails" +require "rails" - %w( +%w( active_record action_controller action_mailer active_resource rails/test_unit - ).each do |framework| - begin - require "#{framework}/railtie" - rescue LoadError - end +).each do |framework| + begin + require "#{framework}/railtie" + rescue LoadError end +end </ruby> First off the line is the +rails+ require itself. @@ -518,9 +519,9 @@ h4. +active_support/core_ext/kernel/reporting.rb+ This is the first of the many Active Support core extensions that come with Rails. This one in particular defines methods in the +Kernel+ module which is mixed in to the +Object+ class so the methods are available on +main+ and can therefore be called like this: <ruby> - silence_warnings do - # some code - end +silence_warnings do + # some code +end </ruby> These methods can be used to silence STDERR responses and the +silence_stream+ allows you to also silence other streams. Additionally, this mixin allows you to suppress exceptions and capture streams. For more information see the "Silencing Warnings, Streams, and Exceptions":http://guides.rubyonrails.org/active_support_core_extensions.html#silencing-warnings-streams-and-exceptions section from the Active Support Core Extensions Guide. @@ -635,14 +636,14 @@ h4. +railties/lib/rails/rack.rb+ The final file to be loaded by +railties/lib/rails/configuration.rb+ is +rails/rack+ which defines some simple autoloads: <ruby> - module Rails - module Rack - autoload :Debugger, "rails/rack/debugger" - autoload :Logger, "rails/rack/logger" - autoload :LogTailer, "rails/rack/log_tailer" - autoload :Static, "rails/rack/static" - end +module Rails + module Rack + autoload :Debugger, "rails/rack/debugger" + autoload :Logger, "rails/rack/logger" + autoload :LogTailer, "rails/rack/log_tailer" + autoload :Static, "rails/rack/static" end +end </ruby> Once this file is finished loading, then the +Rails::Configuration+ class is initialized. This completes the loading of +railties/lib/rails/configuration.rb+ and now we jump back to the loading of +railties/lib/rails/railtie.rb+, where the next file loaded is +active_support/inflector+. @@ -652,12 +653,12 @@ h4. +activesupport/lib/active_support/inflector.rb+ +active_support/inflector.rb+ requires a series of file which are responsible for setting up the basics for knowing how to pluralize and singularize words. These files are: <ruby> - require 'active_support/inflector/inflections' - require 'active_support/inflector/transliterate' - require 'active_support/inflector/methods' +require 'active_support/inflector/inflections' +require 'active_support/inflector/transliterate' +require 'active_support/inflector/methods' - require 'active_support/inflections' - require 'active_support/core_ext/string/inflections' +require 'active_support/inflections' +require 'active_support/core_ext/string/inflections' </ruby> The +active_support/inflector/methods+ file has already been required by +active_support/autoload+ and so won't be loaded again here. @@ -721,22 +722,22 @@ h4. +activesupport/lib/active_support/i18n_railtie.rb+ This file is the first file that sets up configuration with these lines inside the class: <ruby> - class Railtie < Rails::Railtie - config.i18n = ActiveSupport::OrderedOptions.new - config.i18n.railties_load_path = [] - config.i18n.load_path = [] - config.i18n.fallbacks = ActiveSupport::OrderedOptions.new +class Railtie < Rails::Railtie + config.i18n = ActiveSupport::OrderedOptions.new + config.i18n.railties_load_path = [] + config.i18n.load_path = [] + config.i18n.fallbacks = ActiveSupport::OrderedOptions.new </ruby> By inheriting from +Rails::Railtie+ the +Rails::Railtie#inherited+ method is called: <ruby> - def inherited(base) - unless base.abstract_railtie? - base.send(:include, Railtie::Configurable) - subclasses << base - end +def inherited(base) + unless base.abstract_railtie? + base.send(:include, Railtie::Configurable) + subclasses << base end +end </ruby> This first checks if the Railtie that's inheriting it is a component of Rails itself: @@ -763,15 +764,15 @@ end The +config+ method used at the top of +I18n::Railtie+ is defined on +Rails::Railtie+ and is defined like this: <ruby> - def config - @config ||= Railtie::Configuration.new - end +def config + @config ||= Railtie::Configuration.new +end </ruby> At this point, that +Railtie::Configuration+ constant is automatically loaded which causes the +rails/railties/configuration+ file to be loaded. The line for this is this particular line in +railties/lib/rails/railtie.rb+: <ruby> - autoload :Configuration, "rails/railtie/configuration" +autoload :Configuration, "rails/railtie/configuration" </ruby> h4. +railties/lib/rails/railtie/configuration.rb+ @@ -781,15 +782,15 @@ This file begins with a require out to +rails/configuration+ which has already b This file defines the +Rails::Railtie::Configuration+ class which is responsible for providing a way to easily configure railties and it's the +initialize+ method here which is called by the +config+ method back in the +i18n_railtie.rb+ file. The methods on this object don't exist, and so are rescued by the +method_missing+ defined further down in +configuration.rb+: <ruby> - def method_missing(name, *args, &blk) - if name.to_s =~ /=$/ - @@options[$`.to_sym] = args.first - elsif @@options.key?(name) - @@options[name] - else - super - end +def method_missing(name, *args, &blk) + if name.to_s =~ /=$/ + @@options[$`.to_sym] = args.first + elsif @@options.key?(name) + @@options[name] + else + super end +end </ruby> So therefore when an option is referred to it simply stores the value as the key if it's used in a setter context, or retrieves it if used in a getter context. Nothing fancy going on there. @@ -799,21 +800,21 @@ h4. Back to +activesupport/lib/active_support/i18n_railtie.rb+ After the configuration method the +reloader+ method is defined, and then the first of of Railties' initializers is defined: +i18n.callbacks+. <ruby> - initializer "i18n.callbacks" do - ActionDispatch::Reloader.to_prepare do - I18n::Railtie.reloader.execute_if_updated - end +initializer "i18n.callbacks" do + ActionDispatch::Reloader.to_prepare do + I18n::Railtie.reloader.execute_if_updated end +end </ruby> The +initializer+ method (from the +Rails::Initializable+ module) here doesn't run the block, but rather stores it to be run later on: <ruby> - def initializer(name, opts = {}, &blk) - raise ArgumentError, "A block must be passed when defining an initializer" unless blk - opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] } - initializers << Initializer.new(name, nil, opts, &blk) - end +def initializer(name, opts = {}, &blk) + raise ArgumentError, "A block must be passed when defining an initializer" unless blk + opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] } + initializers << Initializer.new(name, nil, opts, &blk) +end </ruby> An initializer can be configured to run before or after another initializer, which we'll see a couple of times throughout this initialization process. Anything that inherits from +Rails::Railtie+ may also make use of the +initializer+ method, something which is covered in the "Configuration guide":[http://ryanbigg.com/guides/configuring.html#rails-railtie-initializer]. @@ -821,25 +822,25 @@ An initializer can be configured to run before or after another initializer, whi The +Initializer+ class here is defined within the +Rails::Initializable+ module and its +initialize+ method is defined to just set up a couple of variables: <ruby> - def initialize(name, context, options, &block) - @name, @context, @options, @block = name, context, options, block - end +def initialize(name, context, options, &block) + @name, @context, @options, @block = name, context, options, block +end </ruby> Once this +initialize+ method is finished, the object is added to the object the +initializers+ method returns: <ruby> - def initializers - @initializers ||= self.class.initializers_for(self) - end +def initializers + @initializers ||= self.class.initializers_for(self) +end </ruby> If +@initializers+ isn't set (which it won't be at this point), the +intializers_for+ method will be called for this class. <ruby> - def initializers_for(binding) - Collection.new(initializers_chain.map { |i| i.bind(binding) }) - end +def initializers_for(binding) + Collection.new(initializers_chain.map { |i| i.bind(binding) }) +end </ruby> The +Collection+ class in +railties/lib/rails/initializable.rb+ inherits from +Array+ and includes the +TSort+ module which is used to sort out the order of the initializers based on the order they are placed in. @@ -847,57 +848,57 @@ The +Collection+ class in +railties/lib/rails/initializable.rb+ inherits from +A The +initializers_chain+ method referenced in the +initializers_for+ method is defined like this: <rub> - def initializers_chain - initializers = Collection.new - ancestors.reverse_each do | klass | - next unless klass.respond_to?(:initializers) - initializers = initializers + klass.initializers - end - initializers +def initializers_chain + initializers = Collection.new + ancestors.reverse_each do | klass | + next unless klass.respond_to?(:initializers) + initializers = initializers + klass.initializers end + initializers +end </ruby> This method collects the initializers from the ancestors of this class and adds them to a new +Collection+ object using the <tt>+</tt> method which is defined like this for the <tt>Collection</tt> class: <ruby> - def +(other) - Collection.new(to_a + other.to_a) - end +def +(other) + Collection.new(to_a + other.to_a) +end </ruby> -So this <tt>+</tt> method is overriden to return a new collection comprising of the existing collection as an array and then using the <tt>Array#+</tt> method combines these two collections, returning a "super" +Collection+ object. In this case, the only initializer that's going to be in this new +Collection+ object is the +i18n.callbacks+ initializer. +So this <tt>+</tt> method is overridden to return a new collection comprising of the existing collection as an array and then using the <tt>Array#+</tt> method combines these two collections, returning a "super" +Collection+ object. In this case, the only initializer that's going to be in this new +Collection+ object is the +i18n.callbacks+ initializer. The next method to be called after this +initializer+ method is the +after_initialize+ method on the +config+ object, which is defined like this: <ruby> - def after_initialize(&block) - ActiveSupport.on_load(:after_initialize, :yield => true, &block) - end +def after_initialize(&block) + ActiveSupport.on_load(:after_initialize, :yield => true, &block) +end </ruby> The +on_load+ method here is provided by the +active_support/lazy_load_hooks+ file which was required earlier and is defined like this: <ruby> - def self.on_load(name, options = {}, &block) - if base = @loaded[name] - execute_hook(base, options, block) - else - @load_hooks[name] << [block, options] - end +def self.on_load(name, options = {}, &block) + if base = @loaded[name] + execute_hook(base, options, block) + else + @load_hooks[name] << [block, options] end +end </ruby> The +@loaded+ variable here is a hash containing elements representing the different components of Rails that have been loaded at this stage. Currently, this hash is empty. So the +else+ is executed here, using the +@load_hooks+ variable defined in +active_support/lazy_load_hooks+: <ruby> - @load_hooks = Hash.new {|h,k| h[k] = [] } +@load_hooks = Hash.new {|h,k| h[k] = [] } </ruby> This defines a new hash which has keys that default to empty arrays. This saves Rails from having to do something like this instead: <ruby> - @load_hooks[name] = [] - @load_hooks[name] << [block, options] +@load_hooks[name] = [] +@load_hooks[name] << [block, options] </ruby> The value added to this array here consists of the block and options passed to +after_initialize+. @@ -929,11 +930,11 @@ h4. +activesupport/lib/action_dispatch.rb+ This file attempts to locate the +active_support+ and +active_model+ libraries by looking a couple of directories back from the current file and then adds the +active_support+ and +active_model+ +lib+ directories to the load path, but only if they aren't already, which they are. <ruby> - activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) - $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) - activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__) - $:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path) +activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__) +$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path) </ruby> In effect, these lines only define the +activesupport_path+ and +activemodel_path+ variables and nothing more. @@ -941,23 +942,23 @@ In effect, these lines only define the +activesupport_path+ and +activemodel_pat The next two requires in this file are already done, so they are not run: <ruby> - require 'active_support' - require 'active_support/dependencies/autoload' +require 'active_support' +require 'active_support/dependencies/autoload' </ruby> The following require is to +action_pack+ (+activesupport/lib/action_pack.rb+) which has a 22-line copyright notice at the top of it and ends in a simple require to +action_pack/version+. This file, like other +version.rb+ files before it, defines the +ActionPack::VERSION+ constant: <ruby> - module ActionPack - module VERSION #:nodoc: - MAJOR = 3 - MINOR = 1 - TINY = 0 - PRE = "beta" - - STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') - end +module ActionPack + module VERSION #:nodoc: + MAJOR = 3 + MINOR = 1 + TINY = 0 + PRE = "beta" + + STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end +end </ruby> Once +action_pack+ is finished, then +active_model+ is required. @@ -967,16 +968,16 @@ h4. +activemodel/lib/active_model.rb+ This file makes a require to +active_model/version+ which defines the version for Active Model: <ruby> - module ActiveModel - module VERSION #:nodoc: - MAJOR = 3 - MINOR = 1 - TINY = 0 - PRE = "beta" - - STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') - end +module ActiveModel + module VERSION #:nodoc: + MAJOR = 3 + MINOR = 1 + TINY = 0 + PRE = "beta" + + STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end +end </ruby> Once the +version.rb+ file is loaded, the +ActiveModel+ module has its autoloaded constants defined as well as a sub-module called +ActiveModel::Serializers+ which has autoloads of its own. When the +ActiveModel+ module is closed the +active_support/i18n+ file is required. @@ -986,15 +987,15 @@ h4. +activesupport/lib/active_support/i18n.rb+ This is where the +i18n+ gem is required and first configured: <ruby> - begin - require 'i18n' - require 'active_support/lazy_load_hooks' - rescue LoadError => e - $stderr.puts "You don't have i18n installed in your application. Please add it to your Gemfile and run bundle install" - raise e - end +begin + require 'i18n' + require 'active_support/lazy_load_hooks' +rescue LoadError => e + $stderr.puts "You don't have i18n installed in your application. Please add it to your Gemfile and run bundle install" + raise e +end - I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml" +I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml" </ruby> In effect, the +I18n+ module first defined by +i18n_railtie+ is extended by the +i18n+ gem, rather than the other way around. This has no ill effect. They both work on the same way. @@ -1012,9 +1013,9 @@ h4. Back to +activesupport/lib/action_dispatch.rb+ The remainder of this file requires the +rack+ file from the Rack gem which defines the +Rack+ module. After +rack+, there's autoloads defined for the +Rack+, +ActionDispatch+, +ActionDispatch::Http+, +ActionDispatch::Session+. A new method called +autoload_under+ is used here, and this simply prefixes the files where the modules are autoloaded from with the path specified. For example here: <ruby> - autoload_under 'testing' do - autoload :Assertions - ... +autoload_under 'testing' do + autoload :Assertions +... </ruby> The +Assertions+ module is in the +action_dispatch/testing+ folder rather than simply +action_dispatch+. @@ -1046,25 +1047,25 @@ This file begins by detecting if the +lib+ directories of +active_support+ and + The first three requires have already been done by other files and so aren't loaded here, but the 4th require, the one to +arel+ will require the file provided by the Arel gem, which defines the +Arel+ module. <ruby> - require 'active_support' - require 'active_support/i18n' - require 'active_model' - require 'arel' +require 'active_support' +require 'active_support/i18n' +require 'active_model' +require 'arel' </ruby> The 5th require in this file is one to +active_record/version+ which defines the +ActiveRecord::VERSION+ constant: <ruby> - module ActiveRecord - module VERSION #:nodoc: - MAJOR = 3 - MINOR = 1 - TINY = 0 - PRE = "beta" - - STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') - end +module ActiveRecord + module VERSION #:nodoc: + MAJOR = 3 + MINOR = 1 + TINY = 0 + PRE = "beta" + + STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end +end </ruby> Once these requires are finished, the base for the +ActiveRecord+ module is defined along with its autoloads. @@ -1072,9 +1073,9 @@ Once these requires are finished, the base for the +ActiveRecord+ module is defi Near the end of the file, we see this line: <ruby> - ActiveSupport.on_load(:active_record) do - Arel::Table.engine = self - end +ActiveSupport.on_load(:active_record) do + Arel::Table.engine = self +end </ruby> This will set the engine for +Arel::Table+ to be +ActiveRecord::Base+. @@ -1082,7 +1083,7 @@ This will set the engine for +Arel::Table+ to be +ActiveRecord::Base+. The file then finishes with this line: <ruby> - I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml' +I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml' </ruby> This will add the translations from +activerecord/lib/active_record/locale/en.yml+ to the load path for +I18n+, with this file being parsed when all the translations are loaded. @@ -1092,8 +1093,8 @@ h4. Back to +activerecord/lib/active_record/railtie.rb+ The next two <tt>require</tt>s in this file aren't run because their files are already required, with +rails+ being required by +rails/all+ and +active_model/railtie+ being required from +action_dispatch+. <ruby> - require "rails" - require "active_model/railtie" +require "rails" +require "active_model/railtie" </ruby> The next +require+ in this file is to +action_controller/railtie+. @@ -1103,9 +1104,9 @@ h4. +actionpack/lib/action_controller/railtie.rb+ This file begins with a couple more requires to files that have already been loaded: <ruby> - require "rails" - require "action_controller" - require "action_dispatch/railtie" +require "rails" +require "action_controller" +require "action_dispatch/railtie" </ruby> However the require after these is to a file that hasn't yet been loaded, +action_view/railtie+, which begins by requiring +action_view+. |