Petit article qui fait la lumière sur les sources de ruby et de ses gems.
load path
Le "load path" est soit $LOAD_PATH ou $ :
% irb
irb(main):001:0> $:
=> ["/usr/local/lib/site_ruby/1.8", "/usr/local/lib/site_ruby/1.8/i486-linux", "/usr/local/lib/site_ruby/1.8/i386-linux", "/usr/local/lib/site_ruby", "/usr/lib/ruby/1.8", "/usr/lib/ruby/1.8/i486-linux", "/usr/lib/ruby/1.8/i386-linux", "."]
Le "load path" est l’ensemble des répertoires dans lesquels ruby va aller chercher les fichiers required.
Ruby sources
Les sources de ruby sont dans un des réportoires de $ :. Apparement, les sources de Ruby 1.8 se trouvent dans le répertoire "/usr/lib/ruby/1.8" :
% ls /usr/lib/ruby/1.8
abbrev.rb cmdparse2.rb digest forwardable.rb io mkmf-gnome2.rb pango.rb rdoc scanf.rb time.rb xmlrpc
atk.rb complex.rb digest.rb ftools.rb ipaddr.rb mkmf.rb parsearg.rb readbytes.rb set.rb tmail xsd
base64.rb csv.rb dl gconf2.rb irb monitor.rb parsedate.rb resolv.rb sha1.rb tmail.rb yaml
benchmark.rb daemonize.rb drb gdk_pixbuf2.rb irb.rb mutex_m.rb pathname.rb resolv-replace.rb shell tmpdir.rb yaml.rb
bigdecimal daemons drb.rb generator.rb jcode.rb ncurses.rb ping.rb rexml shell.rb tracer.rb
binding_of_caller.rb daemons.rb e2mmap.rb getoptlong.rb kconv.rb net pkg-config.rb rinda shellwords.rb tsort.rb
breakpoint_client.rb date English.rb getopts.rb libglade2.rb observer.rb pp.rb RMagick.rb singleton.rb ubygems.rb
breakpoint.rb date2.rb Env.rb glib2.rb log4r open3.rb prettyprint.rb rss soap un.rb
cairo date.rb erb.rb glib-mkenums.rb log4r.rb openssl profile.rb rss.rb sync.rb uri
cairo.rb DBD eregex.rb gserver.rb logger.rb openssl.rb profiler.rb rubygems tempfile.rb uri.rb
cgi dbi expect.rb gtk2 mailread.rb open-uri.rb pstore.rb rubygems.rb test weakref.rb
cgi-lib.rb dbi.rb fileutils.rb gtk2.rb mathn.rb optparse racc rubyunit.rb thread.rb webrick
cgi.rb debug.rb finalize.rb i486-linux matrix.rb optparse.rb rational.rb runit thwait.rb webrick.rb
cmdparse delegate.rb find.rb importenv.rb md5.rb ostruct.rb rbconfig rvg timeout.rb wsdl
rubygems
On constate la présence du fichier ’rubygems.rb’. Lorsque l’on fait un "require ’rubygems’", c’est ce fichier qui est visé.
% head -10 /usr/lib/ruby/1.8/rubygems.rb
# -*- ruby -*-
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
require 'rubygems/rubygems_version'
require 'rubygems/defaults'
require 'thread'
Le fichier "rubygems/defaults.rb" contient l’information sur l’emplacement des gems :
# Default home directory path to be used if an alternate value is not
# specified in the environment.
#
# Debian patch: search order of this directory.
# 1. GEM_HOME enviroment variable
# (Using this, Gems are to be installed in any path as you like)
# 2. /var/lib/gems/{ruby version} (This is the default path in Debian system)
#
def self.default_dir
'/' + File.join('var', 'lib', 'gems', ConfigMap[:ruby_version])
end
les sources des gems
On va aller voir dans le répertoire "/var/lib/gems/1.8" :
% ls /var/lib/gems/1.8
bin cache doc gems source_cache specifications
le répertoire bin/ contient quelques programmes : rake rubyforge sow
le répertoire cache/ est utilisé pour le téléchargement des gems
le répertoire doc/ avec toutes les documentation des gems
le répertoire gems/ contient les sources des gems. C’est ici que vous pourrez consulter le code source de active_record, action_pack etc.
Ce qu’il se passe lorsque vous démarrez le server
Regardons le script de démarrage du serveur :
% cat script/server
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot'
require 'commands/server'
On utilise la syntaxe "/usr/bin/env ruby" pour travailler avec la version courante de ruby installé sur le système.
"__FILE__" donne le chemin du fichier courant.
Le premier require charge le fichier config/boot.rb :
# Don't change this file!
# Configure your app in config/environment.rb and config/environments/*.rb
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
module Rails
class << self
def boot!
unless booted?
preinitialize
pick_boot.run
end
end
def booted?
defined? Rails::Initializer
end
def pick_boot
(vendor_rails? ? VendorBoot : GemBoot).new
end
def vendor_rails?
File.exist?("#{RAILS_ROOT}/vendor/rails")
end
# FIXME : Ruby 1.9
def preinitialize
load(preinitializer_path) if File.exists?(preinitializer_path)
end
def preinitializer_path
"#{RAILS_ROOT}/config/preinitializer.rb"
end
end
class Boot
def run
load_initializer
Rails::Initializer.run(:set_load_path)
end
end
class VendorBoot < Boot
def load_initializer
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
end
end
class GemBoot < Boot
def load_initializer
self.class.load_rubygems
load_rails_gem
require 'initializer'
end
def load_rails_gem
if version = self.class.gem_version
gem 'rails', version
else
gem 'rails'
end
rescue Gem::LoadError => load_error
$stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
exit 1
end
class << self
def rubygems_version
Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
end
def gem_version
if defined? RAILS_GEM_VERSION
RAILS_GEM_VERSION
elsif ENV.include?('RAILS_GEM_VERSION')
ENV['RAILS_GEM_VERSION']
else
parse_gem_version(read_environment_rb)
end
end
def load_rubygems
require 'rubygems'
unless rubygems_version >= '0.9.4'
$stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.)
exit 1
end
rescue LoadError
$stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org)
exit 1
end
def parse_gem_version(text)
$1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
end
private
def read_environment_rb
File.read("#{RAILS_ROOT}/config/environment.rb")
end
end
end
end
# All that for this:
Rails.boot!
Donc on appelle seulement la méthode Rails.boot ! qui va appeler config/preinitialize.rb si le fichier existe dans votre projet rails.
Ensuite, on va déterminer si la version de rails à utiliser est la version gem ou si une version spéciale de rails réside dans votre répertoire vendor/. La version trouvée est chargée.
Dans mon cas, mon rails a été installé avec gem, donc c’est le ’GemBoot’ qui est utilisé. On fait donc un require ’rubygems’.
Par acquis de conscience, vérifions que ce fichier est bien dans le load path :
% irb
irb(main):001:0> $:.each {|d| if File.exists?(d) then Dir.new(d).each {|f| p f if f =~ /rubygems\.rb/ } end }
"rubygems.rb"
=> ["/usr/local/lib/site_ruby/1.8", "/usr/local/lib/site_ruby/1.8/i486-linux", "/usr/local/lib/site_ruby/1.8/i386-linux", "/usr/local/lib/site_ruby", "/usr/lib/ruby/1.8", "/usr/lib/ruby/1.8/i486-linux", "/usr/lib/ruby/1.8/i386-linux", "."]
irb(main):002:0>
Note : le "load path" est accessible par les variables $ : et $LOAD_PATH.
’rubygems’ est maintenant chargé. On va également charger la gem rails (en précisant la version de la gem si c’est indiqué dans environment.rb).
Enfin, on require ’initializer’ (/var/lib/gems/1.8/gems/rails-2.0.2/lib/initializer.rb), puis :
Rails::Initializer.run(:set_load_path)
Comme précisé dans les commentaires de initializer.rb :
# Runs the initializer. By default, this will invoke the #process method,
# which simply executes all of the initialization routines. Alternately,
# you can specify explicitly which initialization routine you want:
#
# Rails::Initializer.run(:set_load_path)
#
# This is useful if you only want the load path initialized, without
# incuring the overhead of completely loading the entire environment.
Démarrage du serveur
Ensuite, dans script/server :
require 'commands/server'
On va charger le fichier /var/lib/gems/1.8/gems/rails-2.0.2/lib/commands/server.rb qui effectue le choix du serveur à démarrer, puis le démarre en faisant :
require "commands/servers/#{server}"
ce qui va charger le fichier : /var/lib/gems/1.8/gems/rails-2.0.2/lib/commands/servers/webrick.rb