## Rubygems Customization ##
# Customize rubygems install behavior and locations to keep user gems isolated
# from the stuff we bundle with omnibus and any other ruby installations on the
# system.

# Always install and update new gems in "user install mode"
Gem::ConfigFile::OPERATING_SYSTEM_DEFAULTS["install"] = "--user --no-document"
Gem::ConfigFile::OPERATING_SYSTEM_DEFAULTS["update"] = "--user --no-document"

module Gem

  ##
  # Override user_dir to live inside of ~/.chef

  class << self
    # Remove method before redefining so we avoid a ruby warning
    remove_method :user_dir

    def user_dir
      parts = [Gem.user_home, ".chef", "gem", ruby_engine]
      parts << RbConfig::CONFIG["ruby_version"] unless RbConfig::CONFIG["ruby_version"].empty?
      File.join parts
    end
  end

end

# Workaround: serialize Gem.load_yaml across threads to close a Ruby 3.4 /
# RubyGems 3.6 / Bundler 2.6 race where the parallel installer can observe a
# partially-loaded Psych module during `bundle install` for cookbook gem
# dependencies:
#   NoMethodError: undefined method 'safe_load' for module Psych
# while one worker thread is mid-`require "psych"` (only psych/versions.rb
# loaded so far) and another worker thread calls Gem.load_yaml ->
# Gem::SafeYAML.safe_load. Wrapping load_yaml in a mutex forces the second
# thread to wait until psych is fully loaded.
#
# We deliberately do NOT `require "psych"` here: that would activate psych's
# runtime deps (date >= 0) at every `require "rubygems"`, before appbundler
# can pin its own gem versions, producing:
#   Gem::LoadError: can't activate date-3.5.0, already activated date-3.4.1
# in cinc-client startup.
#
# Upstream tracking:
#   - https://github.com/rubygems/rubygems/issues/9347
#   - https://github.com/rubygems/rubygems/pull/9352 (replaces Psych with a
#     pure-ruby YAMLSerializer in Gem.load_yaml's default path; merged to
#     master, not yet released)
#
# Remove this block once cinc-workstation ships on a Ruby whose bundled
# RubyGems contains the YAMLSerializer rewrite (post-v4.0.11), or once the
# chef gem version shipped here carries the gem_installer.rb RUBYOPT
# workaround that landed in our chef fork on stable/cinc.
module Gem
  @yaml_load_mutex = Thread::Mutex.new

  class << self
    alias_method :_load_yaml_without_mutex, :load_yaml
    remove_method :load_yaml

    def load_yaml
      return if @yaml_loaded

      @yaml_load_mutex.synchronize { _load_yaml_without_mutex }
    end
  end
end
