123456789_123456789_123456789_123456789_123456789_

Class: Gem::ConfigFile

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
Inherits: Object
Defined in: lib/rubygems/config_file.rb

Overview

ConfigFile RubyGems options and gem command options from gemrc.

gemrc is a YAML file that uses strings to match gem command arguments and symbols to match RubyGems options.

::Gem command arguments use a String key that matches the command name and allow you to specify default arguments:

install: --no-rdoc --no-ri
update: --no-rdoc --no-ri

You can use gem: to set default arguments for all commands.

RubyGems options use symbol keys. Valid options are:

:backtrace

See #backtrace

:sources

Sets sources

:verbose

See #verbose

:concurrent_downloads

See #concurrent_downloads

gemrc files may exist in various locations and are read and merged in the following order:

  • system wide (/etc/gemrc)

  • per user (~/.gemrc)

  • per environment (gemrc files listed in the GEMRC environment variable)

Constant Summary

Class Method Summary

Instance Attribute Summary

DefaultUserInteraction - Included

Instance Method Summary

UserInteraction - Included

#alert

Displays an alert statement.

#alert_error

Displays an error statement to the error output location.

#alert_warning

Displays a warning statement to the warning output location.

#ask

Asks a question and returns the answer.

#ask_for_password

Asks for a password with a prompt

#ask_yes_no

Asks a yes or no question.

#choose_from_list

Asks the user to answer question with an answer from the given list.

#say

Displays the given statement on the standard output (or equivalent).

#terminate_interaction

Terminates the RubyGems process with the given exit_code

#verbose

Calls say with msg or the results of the block if really_verbose is true.

DefaultUserInteraction - Included

Text - Included

#clean_text

Remove any non-printable characters and make the text suitable for printing.

#format_text

Wraps text to wrap characters and optionally indents by indent characters.

#levenshtein_distance

Returns a value representing the “cost” of transforming str1 into str2 Vendored version of DidYouMean::Levenshtein.distance from the ruby/did_you_mean gem @ 1.4.0 github.com/ruby/did_you_mean/blob/2ddf39b874808685965dbc47d344cf6c7651807c/lib/did_you_mean/levenshtein.rb#L7-L37.

#truncate_text, #min3

Constructor Details

.new(args) ⇒ ConfigFile

Create the config file object. #args is the list of arguments from the command line.

The following command line options are handled early here rather than later at the time most command options are processed.

--config-file, --config-file==NAME

Obviously these need to be handled by the ConfigFile object to ensure we get the right config file.

--backtrace

Backtrace needs to be turned on early so that errors before normal option parsing can be properly handled.

--debug

Enable Ruby level debug messages. Handled early for the same reason as –backtrace.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 184

def initialize(args)
  set_config_file_name(args)

  @backtrace = DEFAULT_BACKTRACE
  @bulk_threshold = DEFAULT_BULK_THRESHOLD
  @verbose = DEFAULT_VERBOSITY
  @update_sources = DEFAULT_UPDATE_SOURCES
  @concurrent_downloads = DEFAULT_CONCURRENT_DOWNLOADS
  @cert_expiration_length_days = DEFAULT_CERT_EXPIRATION_LENGTH_DAYS
  @install_extension_in_lib = DEFAULT_INSTALL_EXTENSION_IN_LIB
  @ipv4_fallback_enabled = ENV["IPV4_FALLBACK_ENABLED"] == "true" || DEFAULT_IPV4_FALLBACK_ENABLED

  operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS)
  platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
  system_config = load_file SYSTEM_WIDE_CONFIG_FILE
  user_config = load_file config_file_name

  environment_config = (ENV["GEMRC"] || "").
    split(File::PATH_SEPARATOR).inject({}) do |result, file|
      result.merge load_file file
    end

  @hash = operating_system_config.merge platform_config
  unless args.index "--norc"
    @hash = @hash.merge system_config
    @hash = @hash.merge user_config
    @hash = @hash.merge environment_config
  end

  @hash.transform_keys! do |k|
    # gemhome and gempath are not working with symbol keys
    if %w[backtrace bulk_threshold verbose update_sources cert_expiration_length_days
          install_extension_in_lib ipv4_fallback_enabled sources disable_default_gem_server
          ssl_verify_mode ssl_ca_cert ssl_client_cert].include?(k)
      k.to_sym
    else
      k
    end
  end

  # HACK: these override command-line args, which is bad
  @backtrace                   = @hash[:backtrace]                   if @hash.key? :backtrace
  @bulk_threshold              = @hash[:bulk_threshold]              if @hash.key? :bulk_threshold
  @verbose                     = @hash[:verbose]                     if @hash.key? :verbose
  @update_sources              = @hash[:update_sources]              if @hash.key? :update_sources
  # TODO: We should handle concurrent_downloads same as other options
  @cert_expiration_length_days = @hash[:cert_expiration_length_days] if @hash.key? :cert_expiration_length_days
  @install_extension_in_lib    = @hash[:install_extension_in_lib]    if @hash.key? :install_extension_in_lib
  @ipv4_fallback_enabled       = @hash[:ipv4_fallback_enabled]       if @hash.key? :ipv4_fallback_enabled

  @home                        = @hash[:gemhome]                     if @hash.key? :gemhome
  @path                        = @hash[:gempath]                     if @hash.key? :gempath
  @sources                     = @hash[:sources]                     if @hash.key? :sources
  @disable_default_gem_server  = @hash[:disable_default_gem_server]  if @hash.key? :disable_default_gem_server
  @ssl_verify_mode             = @hash[:ssl_verify_mode]             if @hash.key? :ssl_verify_mode
  @ssl_ca_cert                 = @hash[:ssl_ca_cert]                 if @hash.key? :ssl_ca_cert
  @ssl_client_cert             = @hash[:ssl_client_cert]             if @hash.key? :ssl_client_cert

  @api_keys         = nil
  @rubygems_api_key = nil

  handle_arguments args
end

Class Method Details

.dump_with_rubygems_yaml(content)

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 545

def self.dump_with_rubygems_yaml(content)
  content.transform_keys! do |k|
    k.is_a?(Symbol) ? ":#{k}" : k
  end

  require_relative "yaml_serializer"
  Gem::YAMLSerializer.dump(content)
end

.load_with_rubygems_config_hash(yaml)

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 554

def self.load_with_rubygems_config_hash(yaml)
  require_relative "yaml_serializer"

  content = Gem::YAMLSerializer.load(yaml)

  content.transform_keys! do |k|
    if k.match?(/\A:(.*)\Z/)
      k[1..-1].to_sym
    elsif k.include?("__") || k.match?(%r{/\Z})
      if k.is_a?(Symbol)
        k.to_s.gsub(/__/,".").gsub(%r{/\Z}, "").to_sym
      else
        k.dup.gsub(/__/,".").gsub(%r{/\Z}, "")
      end
    else
      k
    end
  end

  content.transform_values! do |v|
    if v.is_a?(String)
      if v.match?(/\A:(.*)\Z/)
        v[1..-1].to_sym
      elsif v.match?(/\A[-]?\d\Z/)
        v.to_i
      elsif v.match?(/\Atrue|false\Z/)
        v == "true"
      elsif v.empty?
        nil
      else
        v
      end
    elsif v.is_a?(Hash) && v.empty?
      nil
    else
      v
    end
  end

  content
end

Instance Attribute Details

#args (readonly)

List of arguments supplied to the config file object.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 82

attr_reader :args

#backtrace (rw)

True if the backtrace option has been specified, or debug is on.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 388

def backtrace
  @backtrace || $DEBUG
end

#backtrace=(value) (rw)

True if we print backtraces on errors.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 97

attr_writer :backtrace

#bulk_threshold (rw)

Bulk threshold value. If the number of missing gems are above this threshold value, then a bulk download technique is used. (deprecated)

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 103

attr_accessor :bulk_threshold

#cert_expiration_length_days (rw)

Expiration length to sign a certificate

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 145

attr_accessor :cert_expiration_length_days

#concurrent_downloads (rw)

Number of gem downloads that should be performed concurrently.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 116

attr_accessor :concurrent_downloads

#disable_default_gem_server (rw)

True if we want to force specification of gem server when pushing a gem

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 126

attr_accessor :disable_default_gem_server

#hash (readonly, protected)

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 542

attr_reader :hash

#home (rw)

Where to install gems (deprecated)

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 92

attr_accessor :home

#install_extension_in_lib (rw)

Install extensions into lib as well as into the extension directory.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 150

attr_accessor :install_extension_in_lib

#ipv4_fallback_enabled (rw)

Experimental ==

Fallback to IPv4 when IPv6 is not reachable or slow (default: false)

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 156

attr_accessor :ipv4_fallback_enabled

#last_update_check (rw)

Reads time of last update check from state file

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 417

def last_update_check
  if File.readable?(state_file_name)
    File.read(state_file_name).to_i
  else
    0
  end
end

#last_update_check=(timestamp) (rw)

Writes time of last update check to state file

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 426

def last_update_check=(timestamp)
  File.write(state_file_name, timestamp.to_s) if state_file_writable?
end

#path (rw)

Where to look for gems (deprecated)

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 87

attr_accessor :path

#rubygems_api_key (rw)

Returns the RubyGems.org API key

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 321

def rubygems_api_key
  load_api_keys unless @rubygems_api_key

  @rubygems_api_key
end

#rubygems_api_key=(api_key) (rw)

Sets the RubyGems.org API key to api_key

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 330

def rubygems_api_key=(api_key)
  set_api_key :rubygems_api_key, api_key

  @rubygems_api_key = api_key
end

#sources (rw)

sources to look for gems

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 140

attr_accessor :sources

#ssl_ca_cert (rw)

Path name of directory or file of openssl CA certificate, used for remote https connection

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 136

attr_accessor :ssl_ca_cert

#ssl_client_cert (readonly)

Path name of directory or file of openssl client certificate, used for remote https connection with client authentication

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 161

attr_reader :ssl_client_cert

#ssl_verify_mode (readonly)

openssl verify mode value, used for remote https connection

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 130

attr_reader :ssl_verify_mode

#state_file_writable?Boolean (readonly)

Check state file is writable. Creates empty file if not present to ensure we can write to it.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 393

def state_file_writable?
  if File.exist?(state_file_name)
    File.writable?(state_file_name)
  else
    require "fileutils"
    FileUtils.mkdir_p File.dirname(state_file_name)
    File.open(state_file_name, "w") {}
    true
  end
rescue Errno::EACCES
  false
end

#update_sources (rw)

True if we want to update the SourceInfoCache every time, false otherwise

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 121

attr_accessor :update_sources

#verbose (rw)

Verbose level of output:

  • false – No output

  • true – Normal output

  • :loud – Extra output

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 111

attr_accessor :verbose

Instance Method Details

#==(other)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 533

def ==(other) # :nodoc:
  self.class === other &&
    @backtrace == other.backtrace &&
    @bulk_threshold == other.bulk_threshold &&
    @verbose == other.verbose &&
    @update_sources == other.update_sources &&
    @hash == other.hash
end

#[](key)

Return the configuration information for key.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 524

def [](key)
  @hash[key.to_s]
end

#[]=(key, value)

Set configuration option key to value.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 529

def []=(key, value)
  @hash[key.to_s] = value
end

#api_keys

Hash of RubyGems.org and alternate API keys

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 251

def api_keys
  load_api_keys unless @api_keys

  @api_keys
end

#check_credentials_permissions

Checks the permissions of the credentials file. If they are not 0600 an error message is displayed and RubyGems aborts.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 261

def check_credentials_permissions
  return if Gem.win_platform? # windows doesn't write 0600 as 0600
  return unless File.exist? credentials_path

  existing_permissions = File.stat(credentials_path).mode & 0o777

  return if existing_permissions == 0o600

  alert_error <<-ERROR
Your gem push credentials file located at:

\t#{credentials_path}

has file permissions of 0#{existing_permissions.to_s 8} but 0600 is required.

To fix this error run:

\tchmod 0600 #{credentials_path}

You should reset your credentials at:

\thttps://rubygems.org/profile/edit

if you believe they were disclosed to a third party.
  ERROR

  terminate_interaction 1
end

#config_file_name

The name of the configuration file.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 407

def config_file_name
  @config_file_name || Gem.config_file
end

#credentials_path

Location of RubyGems.org credentials

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 293

def credentials_path
  credentials = File.join Gem.user_home, ".gem", "credentials"
  if File.exist? credentials
    credentials
  else
    File.join Gem.data_home, "gem", "credentials"
  end
end

#each {|:update_sources, @update_sources| ... }

Delegates to @hash

Yields:

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 431

def each(&block)
  hash = @hash.dup
  hash.delete :update_sources
  hash.delete :verbose
  hash.delete :backtrace
  hash.delete :bulk_threshold

  yield :update_sources, @update_sources
  yield :verbose, @verbose
  yield :backtrace, @backtrace
  yield :bulk_threshold, @bulk_threshold

  yield "config_file_name", @config_file_name if @config_file_name

  hash.each(&block)
end

#handle_arguments(arg_list)

Handle the command arguments.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 449

def handle_arguments(arg_list)
  @args = []

  arg_list.each do |arg|
    case arg
    when /^--(backtrace|traceback)$/ then
      @backtrace = true
    when /^--debug$/ then
      $DEBUG = true

      warn "NOTE:  Debugging mode prints all exceptions even when rescued"
    else
      @args << arg
    end
  end
end

#load_api_keys

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 302

def load_api_keys
  check_credentials_permissions

  @api_keys = if File.exist? credentials_path
    load_file(credentials_path)
  else
    @hash
  end

  if @api_keys.key? :rubygems_api_key
    @rubygems_api_key    = @api_keys[:rubygems_api_key]
    @api_keys[:rubygems] = @api_keys.delete :rubygems_api_key unless
      @api_keys.key? :rubygems
  end
end

#load_file(filename)

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 365

def load_file(filename)
  yaml_errors = [ArgumentError]

  return {} unless filename && !filename.empty? && File.exist?(filename)

  begin
    config = self.class.load_with_rubygems_config_hash(File.read(filename))
    if config.keys.any? {|k| k.to_s.gsub(%r{https?:\/\/}, "").include?(": ") }
      warn "Failed to load #{filename} because it doesn't contain valid YAML hash"
      return {}
    else
      return config
    end
  rescue *yaml_errors => e
    warn "Failed to load #{filename}, #{e}"
  rescue Errno::EACCES
    warn "Failed to load #{filename} due to permissions problem."
  end

  {}
end

#really_verbose

Really verbose mode gives you extra output.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 467

def really_verbose
  case verbose
  when true, false, nil then
    false
  else
    true
  end
end

#set_api_key(host, api_key)

Set a specific host’s API key to api_key

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 339

def set_api_key(host, api_key)
  check_credentials_permissions

  config = load_file(credentials_path).merge(host => api_key)

  dirname = File.dirname credentials_path
  require "fileutils"
  FileUtils.mkdir_p(dirname)

  permissions = 0o600 & (~File.umask)
  File.open(credentials_path, "w", permissions) do |f|
    f.write self.class.dump_with_rubygems_yaml(config)
  end

  load_api_keys # reload
end

#set_config_file_name(args) (private)

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 598

def set_config_file_name(args)
  @config_file_name = ENV["GEMRC"]
  need_config_file_name = false

  args.each do |arg|
    if need_config_file_name
      @config_file_name = arg
      need_config_file_name = false
    elsif arg =~ /^--config-file=(.*)/
      @config_file_name = $1
    elsif /^--config-file$/.match?(arg)
      need_config_file_name = true
    end
  end
end

#state_file_name

The name of the state file.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 412

def state_file_name
  Gem.state_file
end

#to_yaml

This method is for internal use only.

to_yaml only overwrites things you can’t override on the command line.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 477

def to_yaml # :nodoc:
  yaml_hash = {}
  yaml_hash[:backtrace] = @hash.fetch(:backtrace, DEFAULT_BACKTRACE)
  yaml_hash[:bulk_threshold] = @hash.fetch(:bulk_threshold, DEFAULT_BULK_THRESHOLD)
  yaml_hash[:sources] = Gem.sources.to_a
  yaml_hash[:update_sources] = @hash.fetch(:update_sources, DEFAULT_UPDATE_SOURCES)
  yaml_hash[:verbose] = @hash.fetch(:verbose, DEFAULT_VERBOSITY)

  yaml_hash[:concurrent_downloads] =
    @hash.fetch(:concurrent_downloads, DEFAULT_CONCURRENT_DOWNLOADS)

  yaml_hash[:install_extension_in_lib] =
    @hash.fetch(:install_extension_in_lib, DEFAULT_INSTALL_EXTENSION_IN_LIB)

  yaml_hash[:ssl_verify_mode] =
    @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode

  yaml_hash[:ssl_ca_cert] =
    @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert

  yaml_hash[:ssl_client_cert] =
    @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert

  keys = yaml_hash.keys.map(&:to_s)
  keys << "debug"
  re = Regexp.union(*keys)

  @hash.each do |key, value|
    key = key.to_s
    next if key&.match?(re)
    yaml_hash[key.to_s] = value
  end

  self.class.dump_with_rubygems_yaml(yaml_hash)
end

#unset_api_key!

Remove the ~/.gem/credentials file to clear all the current sessions.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 359

def unset_api_key!
  return false unless File.exist?(credentials_path)

  File.delete(credentials_path)
end

#write

Writes out this config file, replacing its source.

[ GitHub ]

  
# File 'lib/rubygems/config_file.rb', line 514

def write
  require "fileutils"
  FileUtils.mkdir_p File.dirname(config_file_name)

  File.open config_file_name, "w" do |io|
    io.write to_yaml
  end
end