module Kernel - rubygems: Ruby Standard Library Documentation

# module Kernel

### Private Instance Methods

gem(gem_name, *requirements) click to toggle source

Use `Kernel#gem` to activate a specific version of `gem_name`.

`requirements` is a list of version requirements that the specified gem must match, most commonly “= example.version.number”. See `Gem::Requirement` for how to specify a version requirement.

If you will be activating the latest version of a gem, there is no need to call `Kernel#gem`, `Kernel#require` will do the right thing for you.

`Kernel#gem` returns true if the gem was activated, otherwise false. If the gem could not be found, didn’t match the version requirements, or a different version was already activated, an exception will be raised.

`Kernel#gem` should be called before any require statements (otherwise RubyGems may load a conflicting library version).

`Kernel#gem` only loads prerelease versions when prerelease `requirements` are given:

    gem 'rake', '>= 1.1.a', '< 2'

In older RubyGems versions, the environment variable GEM_SKIP could be used to skip activation of specified gems, for example to test out changes that haven’t been installed yet. Now RubyGems defers to -I and the RUBYLIB environment variable to skip activation of a gem.

Example:

    GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb

    # File rubygems/core_ext/kernel_gem.rb, line 35
    def gem(gem_name, *requirements) # :doc:
      skip_list = (ENV["GEM_SKIP"] || "").split(/:/)
      raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name

      if gem_name.is_a? Gem::Dependency
        unless Gem::Deprecate.skip
          warn "#{Gem.location_of_caller.join ":"}:Warning: Kernel.gem no longer "\
            "accepts a Gem::Dependency object, please pass the name "\
            "and requirements directly"
        end

        requirements = gem_name.requirement
        gem_name = gem_name.name
      end

      dep = Gem::Dependency.new(gem_name, *requirements)

      loaded = Gem.loaded_specs[gem_name]

      return false if loaded && dep.matches_spec?(loaded)

      spec = dep.to_spec

      if spec
        if Gem::LOADED_SPECS_MUTEX.owned?
          spec.activate
        else
          Gem::LOADED_SPECS_MUTEX.synchronize { spec.activate }
        end
      end
    end

require(path) click to toggle source

When RubyGems is required, `Kernel#require` is replaced with our own which is capable of loading gems on demand.

When you call `require 'x'`, this is what happens:

- If the file can be loaded from the existing Ruby loadpath, it is.

- Otherwise, installed gems are searched for a file that matches. If it’s found in gem ‘y’, that gem is activated (added to the loadpath).

The normal `require` functionality of returning false if that file has already been loaded is preserved.

    # File rubygems/core_ext/kernel_require.rb, line 36
    def require(path) # :doc:
      return gem_original_require(path) unless Gem.discover_gems_on_require

      RUBYGEMS_ACTIVATION_MONITOR.synchronize do
        path = File.path(path)

        # If +path+ belongs to a default gem, we activate it and then go straight
        # to normal require

        if spec = Gem.find_default_spec(path)
          name = spec.name

          next if Gem.loaded_specs[name]

          # Ensure -I beats a default gem
          resolved_path = begin
            rp = nil
            load_path_check_index = Gem.load_path_insert_index - Gem.activated_gem_paths
            Gem.suffixes.find do |s|
              $LOAD_PATH[0...load_path_check_index].find do |lp|
                if File.symlink? lp # for backward compatibility
                  next
                end

                full_path = File.expand_path(File.join(lp, "#{path}#{s}"))
                rp = full_path if File.file?(full_path)
              end
            end
            rp
          end

          Kernel.send(:gem, name, Gem::Requirement.default_prerelease) unless
            resolved_path

          next
        end

        # If there are no unresolved deps, then we can use just try
        # normal require handle loading a gem from the rescue below.

        if Gem::Specification.unresolved_deps.empty?
          next
        end

        # If +path+ is for a gem that has already been loaded, don't
        # bother trying to find it in an unresolved gem, just go straight
        # to normal require.
        #--
        # TODO request access to the C implementation of this to speed up RubyGems

        if Gem::Specification.find_active_stub_by_path(path)
          next
        end

        # Attempt to find +path+ in any unresolved gems...

        found_specs = Gem::Specification.find_in_unresolved path

        # If there are no directly unresolved gems, then try and find +path+
        # in any gems that are available via the currently unresolved gems.
        # For example, given:
        #
        #   a => b => c => d
        #
        # If a and b are currently active with c being unresolved and d.rb is
        # requested, then find_in_unresolved_tree will find d.rb in d because
        # it's a dependency of c.
        #
        if found_specs.empty?
          found_specs = Gem::Specification.find_in_unresolved_tree path

          found_specs.each(&:activate)

        # We found +path+ directly in an unresolved gem. Now we figure out, of
        # the possible found specs, which one we should activate.
        else

          # Check that all the found specs are just different
          # versions of the same gem
          names = found_specs.map(&:name).uniq

          if names.size > 1
            raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ", "}"
          end

          # Ok, now find a gem that has no conflicts, starting
          # at the highest version.
          valid = found_specs.find {|s| !s.has_conflicts? }

          unless valid
            le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
            le.name = names.first
            raise le
          end

          valid.activate
        end
      end

      begin
        gem_original_require(path)
      rescue LoadError => load_error
        if load_error.path == path &&
           RUBYGEMS_ACTIVATION_MONITOR.synchronize { Gem.try_activate(path) }

          return gem_original_require(path)
        end

        raise load_error
      end
    end