aboutsummaryrefslogtreecommitdiffstats
path: root/railties/guides/source/initialization.textile
diff options
context:
space:
mode:
authorXavier Noria <fxn@hashref.com>2010-04-16 15:08:16 -0700
committerXavier Noria <fxn@hashref.com>2010-04-16 15:08:16 -0700
commit09b9add8c5eb85e7369d98a6efa11c5b27787838 (patch)
treef8701879fbf37675467f96a3e47af1173cf8b021 /railties/guides/source/initialization.textile
parent001ca893c6f061796a2c3f03c864fdf06c9efbb1 (diff)
parentf7e9c931412bdee0c6dbce3334fdd66ce226889a (diff)
downloadrails-09b9add8c5eb85e7369d98a6efa11c5b27787838.tar.gz
rails-09b9add8c5eb85e7369d98a6efa11c5b27787838.tar.bz2
rails-09b9add8c5eb85e7369d98a6efa11c5b27787838.zip
Merge commit 'docrails/master'
Diffstat (limited to 'railties/guides/source/initialization.textile')
-rw-r--r--railties/guides/source/initialization.textile482
1 files changed, 24 insertions, 458 deletions
diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile
index a8c7ce0865..9182f89f5b 100644
--- a/railties/guides/source/initialization.textile
+++ b/railties/guides/source/initialization.textile
@@ -1792,487 +1792,53 @@ Now that Rails has finished loading all the Railties by way of +require 'rails/a
NOTE: It is worth mentioning here that you are not tied to using Bundler with Rails 3, but it is (of course) advised that you do. To "turn off" Bundler, comment out or remove the corresponding lines in _config/application.rb_ and _config/boot.rb_.
-Bundler was +require+'d back in _config/boot.rb_ and now we'll dive into the internals of Bundler to determine precisely what this line accomplishes.
+Bundler was +require+'d back in _config/boot.rb_, and so that is what makes it available here. This guide does not dive into the internals of Bundler; it's really it's own separate guide.
-h4. +Bundler.require+
+The +Bundler.require+ method adds all the gems not specified inside a +group+ in the +Gemfile+ and the ones specified in groups for the +Rails.env+ (in this case, _development_), to the load path. This is how an application is able to find them.
-+Bundler.require+ is defined in _lib/bundler.rb_:
+The rest of this file is spent defining your application's main class. This is it without the comments:
<ruby>
- def require(*groups)
- gemfile = default_gemfile
- load(gemfile).require(*groups)
- end
-</ruby>
-
-The +groups+ variable here would be a two-element array of the arguments passed to +Bundler.require+. In this case we're going to assume, +Rails.env+ is +"development"+.
-
-h4. Locating the Gemfile
-
-+default_gemfile+ is defined in _lib/bundler.rb_ and makes a call out to the +SharedHelpers+ module:
-
-<ruby>
- def default_gemfile
- SharedHelpers.default_gemfile
- end
-</ruby>
-
-+SharedHelpers+ defines +default_gemfile+ like this:
-
-<ruby>
- def default_gemfile
- gemfile = find_gemfile
- gemfile or raise GemfileNotFound, "The default Gemfile was not found"
- Pathname.new(gemfile)
- end
-</ruby>
-
-+find_gemfile+ is defined like this:
-
-<ruby>
- def find_gemfile
- return ENV['BUNDLE_GEMFILE'] if ENV['BUNDLE_GEMFILE']
-
- previous = nil
- current = File.expand_path(Dir.pwd)
-
- until !File.directory?(current) || current == previous
- filename = File.join(current, 'Gemfile')
- return filename if File.file?(filename)
- current, previous = File.expand_path("..", current), current
- end
- end
-</ruby>
-
-The first line of course means if you define the environment variable +BUNDLE_GEMFILE+ this is the name of the file that will be used and returned. If not, then Bundler will look for a file called _Gemfile_ in the current directory and if it can find it then it will return the filename. If it cannot, it will recurse up the directory structure until it does. Once the file is found a +Pathname+ is made from the expanded path to _Gemfile_.
-
-If the file cannot be found at all then +GemfileNotFound+ will be raised back in +default_gemfile+.
-
-h4. Loading the Gemfile
-
-Now that Bundler has determined what the _Gemfile_ is, it goes about loading it:
-
-<ruby>
- def require(*groups)
- gemfile = default_gemfile
- load(gemfile).require(*groups)
- end
-</ruby>
-
-+load+ is defined like this in _lib/bundler.rb_:
-
-<ruby>
- def load(gemfile = default_gemfile)
- root = Pathname.new(gemfile).dirname
- Runtime.new root, definition(gemfile)
- end
-</ruby>
-
-The next method to be called here would be +definition+ and it is defined like this:
-
-<ruby>
- def definition(gemfile = default_gemfile)
- configure
- root = Pathname.new(gemfile).dirname
- lockfile = root.join("Gemfile.lock")
- if lockfile.exist?
- Definition.from_lock(lockfile)
- else
- Definition.from_gemfile(gemfile)
- end
- end
-</ruby>
-
-+configure+ is responsible for setting up the path to gem home and gem path:
-
-<ruby>
- def configure
- @configured ||= begin
- configure_gem_home_and_path
- true
- end
- end
-</ruby>
-
-+configure_gem_home_and_path+ defined like this:
-
-<ruby>
- def configure_gem_home_and_path
- if settings[:disable_shared_gems]
- ENV['GEM_HOME'] = File.expand_path(bundle_path, root)
- ENV['GEM_PATH'] = ''
- else
- gem_home, gem_path = Gem.dir, Gem.path
- ENV["GEM_PATH"] = [gem_home, gem_path].flatten.compact.join(File::PATH_SEPARATOR)
- ENV["GEM_HOME"] = bundle_path.to_s
- end
-
- Gem.clear_paths
- end
-</ruby>
-
-We do not have +settings[:disabled_shared_gems]+ set to true so this will execute the code under the +else+. The +ENV["GEM_PATH"]+ will resemble +/usr/local/lib/ruby/gems/1.9.1:/home/you/.gem/ruby/1.9.1+
-
-And +ENV["GEM_HOME"]+ will be the path to the gems installed into your home directory by Bundler, something resembling +/home/you/.bundle/ruby/1.9.1+.
-
-After +configure_gem_home_and_path+ is done the +definition+ method goes about creating a +Definition+ from either +Gemfile.lock+ if it exists, or the +gemfile+ previously located. +Gemfile.lock+ only exists if +bundle lock+ has been ran and so far it has not.
-
-+Definition.from_gemfile+ is defined in _lib/bundler/definition.rb_:
-
-<ruby>
- def self.from_gemfile(gemfile)
- gemfile = Pathname.new(gemfile).expand_path
-
- unless gemfile.file?
- raise GemfileNotFound, "#{gemfile} not found"
- end
-
- Dsl.evaluate(gemfile)
- end
-</ruby>
-
-Now that the +gemfile+ is located +Dsl.evaluate+ goes about loading it. The code for this can be found in _lib/bundler/dsl.rb_:
-
-<ruby>
- def self.evaluate(gemfile)
- builder = new
- builder.instance_eval(File.read(gemfile.to_s), gemfile.to_s, 1)
- builder.to_definition
- end
-</ruby>
-
-+new+ here will, of course, call +initialize+ which sets up a couple of variables:
-
-<ruby>
- def initialize
- @source = nil
- @sources = []
- @dependencies = []
- @group = nil
- end
-</ruby>
-
-When Bundler calls +instance_eval+ on the new +Bundler::Dsl+ object it evaluates the content of the +gemfile+ file within the context of this instance. The Gemfile for a default Rails 3 project with all the comments stripped out looks like this:
-
-<ruby>
- source 'http://rubygems.org'
-
- gem 'rails', '3.0.0.beta1'
-
- # Bundle edge Rails instead:
- # gem 'rails', :git => 'git://github.com/rails/rails.git'
-
- gem 'sqlite3-ruby', :require => 'sqlite3'
-</ruby>
-
-When Bundler loads this file it firstly calls the +source+ method on the +Bundler::Dsl+ object:
-
-<ruby>
- def source(source, options = {})
- @source = case source
- when :gemcutter, :rubygems, :rubyforge then Source::Rubygems.new("uri" => "http://gemcutter.org")
- when String then Source::Rubygems.new("uri" => source)
- else source
- end
-
- options[:prepend] ? @sources.unshift(@source) : @sources << @source
-
- yield if block_given?
- @source
- ensure
- @source = nil
- end
-</ruby>
-
-TODO: Perhaps make this a side-note. However you do that.
-
-The interesting thing to note about this method is that it takes a block, so you may do:
-
-<ruby>
- source 'http://someothergemhost.com' do
- gem 'your_favourite_gem'
- end
-</ruby>
-
-if you wish to install _your_favourite_gem_ from _http://someothergemhost.com_.
-
-In this instance however a block is not specified so this sets up the +@source+ instance variable to be +'http://rubygems.org'+.
-
-The next method that is called is +gem+:
-
-<ruby>
- def gem(name, *args)
- options = Hash === args.last ? args.pop : {}
- version = args.last || ">= 0"
- if options[:group]
- options[:group] = options[:group].to_sym
+ module YourApp
+ class Application < Rails::Application
+ config.encoding = "utf-8"
+ config.filter_parameters += [:password]
end
-
- _deprecated_options(options)
- _normalize_options(name, version, options)
-
- @dependencies << Dependency.new(name, version, options)
end
</ruby>
-This sets up a couple of important things initially. If you specify a gem like the following:
-
-<ruby>
- gem 'rails', "2.3.4"
-</ruby>
-
-This sets +options+ to be an empty hash, but +version+ to be +"2.3.4"+. TODO: How does one pass options and versions at the same time?
+h3. Return to Rails
-In the Gemfile for a default Rails project, the first +gem+ line is:
+On the surface, this looks like a simple class inheritance. There's more underneath though. back in +Rails::Application+, the +inherited+ method is defined:
<ruby>
- gem 'rails', '3.0.0.beta2'
-</ruby>
-
-TODO: change version number.
-
-This line will check that +options+ contains no deprecated options by using the +_deprecated_options+ method, but the +options+ hash is empty. This is of course until +_normalize_options+ has its way:
-
-<ruby>
- def _normalize_options(name, version, opts)
- _normalize_hash(opts)
-
- group = opts.delete("group") || @group
-
- # Normalize git and path options
- ["git", "path"].each do |type|
- if param = opts[type]
- options = _version?(version) ? opts.merge("name" => name, "version" => version) : opts.dup
- source = send(type, param, options, :prepend => true)
- opts["source"] = source
- end
- end
-
- opts["source"] ||= @source
-
- opts["group"] = group
+ def inherited(base)
+ raise "You cannot have more than one Rails::Application" if Rails.application
+ super
+ Rails.application = base.instance
end
</ruby>
-+_normalize_hash+ will convert all the keys in the +opts+ hash to strings. There is neither a +git+ or a +path+ key in the +opts+ hash so the next couple of lines are ignored, then the +source+ and +group+ keys are set up.
-
-TODO: Maybe it is best to cover what would happen in the case these lines did exist?
-
-The next line goes about defining a dependency for this gem:
-
-<ruby>
- @dependencies << Dependency.new(name, version, options)
-</ruby>
-
-This class is defined like this:
+We do not already have a +Rails.application+, so instead this resorts to calling +super+. +Rails::Application+ descends from +Rails::Engine+ and so will call the +inherited+ method in +Rails::Engine+, but before that it's important to note that +called_from+ is defined an +attr_accessor+ on +Rails::Engine+:
<ruby>
- module Bundler
- class Dependency < Gem::Dependency
- attr_reader :autorequire
- attr_reader :groups
-
- def initialize(name, version, options = {}, &blk)
- super(name, version)
-
- @autorequire = nil
- @groups = Array(options["group"] || :default).map { |g| g.to_sym }
- @source = options["source"]
-
- if options.key?('require')
- @autorequire = Array(options['require'] || [])
- end
+ def inherited(base)
+ unless base.abstract_railtie?
+ base.called_from = begin
+ # Remove the line number from backtraces making sure we don't leave anything behind
+ call_stack = caller.map { |p| p.split(':')[0..-2].join(':') }
+ File.dirname(call_stack.detect { |p| p !~ %r[railties[\w\-\.]*/lib/rails|rack[\w\-\.]*/lib/rack] })
end
end
- end
-</ruby>
-
-The +initialize+ method in +Gem::Dependency+ is defined:
-
-<ruby>
- def initialize(name, version_requirements, type=:runtime)
- @name = name
- unless TYPES.include? type
- raise ArgumentError, "Valid types are #{TYPES.inspect}, not #{@type.inspect}"
- end
- @type = type
- @version_requirements = Gem::Requirement.create version_requirements
- @version_requirement = nil # Avoid warnings.
- end
-</ruby>
-
-The +version_requirements+ that was passed in here will be inspected by +Gem::Requirement.create+ and return, for our +3.0.0beta2+ version string a +Gem::Requirement+ object:
-
-<ruby>
- #<Gem::Requirement:0x101dd8c20 @requirements=[["=", #<Gem::Version "3.0.0beta2">]]>
-</ruby>
-
-Going back to +Bundler::Dependency+, the next line simply sets +@autorequire+ to +nil+ and the next line is a little more interesting:
-
-<ruby>
- @autorequire = nil
- @groups = Array(options["group"] || :default).map { |g| g.to_sym }
-</ruby>
-
-Here, bundler sets the +groups+ variable to be whatever +group+ we've set for this gem and also demonstrates through code that the +group+ option allows for multiple groups, so in the _Gemfile_ we can specify the same gem for multiple groups:
-
-<ruby>
- group :test, :cucumber do
- gem 'faker'
- end
-</ruby>
-
-The final lines in +initialize+ work on the +require+ option which is not passed:
-
-<ruby>
- if options.key?('require')
- @autorequire = Array(options['require'] || [])
- end
-</ruby>
-
-If it were to be used in the _Gemfile_, it would look like this:
-
-<ruby>
- gem 'thinking-sphinx', :require => "thinking_sphinx"
-</ruby>
-
-So far, this is what simply loading the _Gemfile_ does.
-
-h3. Bring forth the gems
-Now that the _Gemfile_ has finished being parsed, the next line is:
-
-<ruby>
- builder.to_definition
-</ruby>
-
-This method is defined in _lib/bundler/dsl.rb_ and does this:
-
-<ruby>
- def to_definition
- Definition.new(@dependencies, @sources)
- end
-</ruby>
-
-The +Bundler::Definition#initialize+ method is this:
-
-<ruby>
- def initialize(dependencies, sources)
- @dependencies = dependencies
- @sources = sources
- end
-</ruby>
-
-Now Bundler has a +Bundler::Definition+ object to be passed back to the +load+ method from _lib/bundler.rb_:
-
-<ruby>
- def load(gemfile = default_gemfile)
- root = Pathname.new(gemfile).dirname
- Runtime.new root, definition(gemfile)
- end
-</ruby>
-
-The +Bundler::Runtime+ class inherits from +Bundler::Environment+ and the reason this is pointed out is because +super+ is used in the +initialize+ method in +Bundler::Runtime+:
-
-<ruby>
- super
- if locked?
- write_rb_lock
- end
-</ruby>
-
-Thankfully, the +Bundler::Environment#initialize+ method is nothing too complex:
-
-<ruby>
- def initialize(root, definition)
- @root = root
- @definition = definition
- end
-</ruby>
-
-The +locked?+ method checks if the _Gemfile.lock_ or _.bundler/environment.rb_ files exist:
-
-<ruby>
- def locked?
- File.exist?("#{root}/Gemfile.lock") || File.exist?("#{root}/.bundle/environment.rb")
- end
-</ruby>
-
-And if they do will call +write_rb_lock+:
-
-<ruby>
- def write_rb_lock
- shared_helpers = File.read(File.expand_path("../shared_helpers.rb", __FILE__))
- template = File.read(File.expand_path("../templates/environment.erb", __FILE__))
- erb = ERB.new(template, nil, '-')
- FileUtils.mkdir_p(rb_lock_file.dirname)
- File.open(rb_lock_file, 'w') do |f|
- f.puts erb.result(binding)
- end
- end
-</ruby>
-
-This will write out to _.bundler/environment.rb_ the state of the current environment.
-
-Now a quick refresher. Bundler is still evaulating the code for the +require+ in _lib/bundler.rb_, and the +groups+ variable here is an +Array+ containing two elements: +:default+ and the current Rails environment: +development+:
-
-<ruby>
- def require(*groups)
- gemfile = default_gemfile
- load(gemfile).require(*groups)
- end
-</ruby>
-
-The second +require+ method here:
-
-<ruby>
- load(gemfile).require(*groups)
-</ruby>
-
-Is defined on _bundler/runtime.rb_:
-
-<ruby>
- def require(*groups)
- groups.map! { |g| g.to_sym }
- groups = [:default] if groups.empty?
- autorequires = autorequires_for_groups(*groups)
-
- groups.each do |group|
- (autorequires[group] || [[]]).each do |path, explicit|
- if explicit
- Kernel.require(path)
- else
- begin
- Kernel.require(path)
- rescue LoadError
- end
- end
- end
- end
+ super
end
</ruby>
-This method does TODO: Describe what magic this undertakes.
-
-The first method to be called here is +autorequires_for_groups+:
-
-<ruby>
- def autorequires_for_groups(*groups)
- groups.map! { |g| g.to_sym }
- autorequires = Hash.new { |h,k| h[k] = [] }
-
- ordered_deps = []
- specs_for(*groups).each do |g|
- dep = @definition.dependencies.find{|d| d.name == g.name }
- ordered_deps << dep if dep && !ordered_deps.include?(dep)
- end
-</ruby>
+This +called_from+ setting looks a little overwhelming to begin with, but the short end of it is that it returns the route to your application's config directory, something like: _/home/you/yourapp/config_. After +called_from+ has been set, +super+ is again called and this means the +Rails::Railtie#inherited+ method.
+
-The +specs_for+ method here:
-<ruby>
-
-</ruby>
h3. Firing it up!
@@ -2642,7 +2208,7 @@ The method +find_with_root_flag+ is defined on +Rails::Engine+ (the superclass o
root = File.exist?("#{root_path}/#{flag}") ? root_path : default
raise "Could not find root path for #{self}" unless root
- RUBY_PLATFORM =~ /mswin|mingw/ ?
+ RUBY_PLATFORM =~ /(:?mswin|mingw)/ ?
Pathname.new(root).expand_path : Pathname.new(root).realpath
end
</ruby>