????
Current Path : C:/opt/ruby/ridk_use/ |
Current File : C:/opt/ruby/ridk_use/ridk_use.rb |
require "yaml" def ridkuse_dirname File.expand_path("..", __FILE__) end def rubies_filename ENV['RIDK_USE_RUBIES'] || File.join(ridkuse_dirname, "rubies.yml") end def backslachs(path) path.gsub("/", "\\") end def forwardslachs(path) path.gsub("\\", "/") end RUBY_INSTALL_KEY = "SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall/" def find_each_ruby_from_registry return to_enum(:find_each_ruby_from_registry) unless block_given? require "win32/registry" begin Win32::Registry::HKEY_CURRENT_USER.open(backslachs(RUBY_INSTALL_KEY)) do |reg| reg.each_key do |subkey| subreg = reg.open(subkey) begin if subreg['DisplayName'] =~ /^Ruby / && File.directory?(il=subreg['InstallLocation']) yield il end rescue Encoding::InvalidByteSequenceError, Win32::Registry::Error # Ignore entries without valid installer data or broken character encoding end end end rescue Win32::Registry::Error end end def find_each_ruby_from_yml return to_enum(:find_each_ruby_from_yml) unless block_given? YAML.load_file(rubies_filename).each do |rubypath| yield rubypath end end def find_each_ruby(&block) return to_enum(:find_each_ruby) unless block_given? if File.exist?(rubies_filename) find_each_ruby_from_yml(&block) else find_each_ruby_from_registry.sort.each(&block) end end def each_ruby return to_enum(:each_ruby) unless block_given? find_each_ruby.each_with_index do |rubypath, idx| yield(idx + 1, File.expand_path(rubypath)) end end def list_rubies each_ruby do |idx, rubypath| rubyver = begin `#{File.join(rubypath, "bin/ruby")} -v` rescue => err err.to_s end $stderr.puts "#{idx} - #{rubypath} \t#{rubyver}" end end def update_rubies if File.exist?(rubies_filename) rubies = YAML.load_file(rubies_filename) else rubies = [] end find_each_ruby_from_registry.sort.each do |rubypath| rubypath = File.expand_path(rubypath) unless rubies.find{|r| File.expand_path(r) == rubypath } rubies << rubypath end end $stderr.puts "Update #{rubies_filename}" File.write(rubies_filename, YAML.dump(rubies)) end def in_path_regex(path) pathregex = Regexp.escape(forwardslachs(path)).gsub(/\//, "[\\/\\\\\\\\]") /(^|;)#{pathregex}[^;]*(;|$)/i end def remove_rubies_from_path(vars, rubies, desc) if path=vars['PATH'] rubies.each do |rubypath| path = path.gsub(in_path_regex(rubypath)) do |a| res = $1.empty? || $2.empty? ? "" : ";" $stderr.puts "Disable #{rubypath} #{desc}" res end end vars['PATH'] = path end end def enable_ruby_in_path(vars, rubypath, desc) if (path=vars["PATH"]) && !in_path_regex(rubypath).match(path) $stderr.puts "Enable #{rubypath} #{desc}" vars['PATH'] = backslachs(File.join(rubypath, "bin")) + ";" + vars['PATH'] end end def ensure_ridk_use_in_path(vars, rubypath) ridkusepath = backslachs(ridkuse_dirname) # No need to add ridk_use to PATH if it belongs to current ruby ridkusepath = nil if ridkusepath == backslachs(File.join(rubypath, "ridk_use")) if ridkusepath && (path=vars['PATH']) unless in_path_regex(ridkusepath).match(path) path = ridkusepath + ";" + path end vars['PATH'] = path end vars['RIDK_USE_PATH'] = ridkusepath end def adjust_path_vars(rubypath, rubies, vars=nil, desc="in current shell") vars ||= { "PATH" => ENV['PATH'], "RIDK_USE_PATH" => nil, } remove_rubies_from_path(vars, rubies, desc) enable_ruby_in_path(vars, rubypath, desc) ensure_ridk_use_in_path(vars, rubypath) vars end def switch_ruby_per_cmd(rubypath, rubies, ps1) vars = adjust_path_vars(rubypath, rubies) if ps1 vars.map do |key, val| "$env:#{key}=\"#{val.to_s.gsub('"', '`"')}\"" end.join(";") else vars.map do |key, val| "#{key}=#{val}" end.join("\n") end end def modify_default(rubypath, rubies, default) return unless default require "win32/registry" if default == :system reg_root = Win32::Registry::HKEY_LOCAL_MACHINE reg_key = "SYSTEM/CurrentControlSet/Control/Session Manager/Environment" elsif default == :user reg_root = Win32::Registry::HKEY_CURRENT_USER reg_key = "Environment" end reg_root.open(backslachs(reg_key), Win32::Registry::KEY_ALL_ACCESS) do |reg| vars = reg.select do |k,t,v| ['PATH', 'RIDK_USE_PATH'].include?(k.upcase) end.map { |k,t,v| [k.upcase, v] }.to_h vars = adjust_path_vars(rubypath, rubies, vars, "in #{default} settings") vars.each do |k, v| if v reg.write(k, Win32::Registry::REG_EXPAND_SZ, v) else reg.delete_key(k) end end end end def select_ruby(rubies, selector) ridx = selector.to_i if ridx > 0 rubies = each_ruby.to_h selpath = rubies[ridx] end return selpath if selpath if selector =~ /^\/(.*)\/$/ regex = $1 _, selpath = rubies.find do |idx, rubypath| /#{regex}/i.match(rubypath) end return selpath end end def print_help $stderr.puts <<-EOT Usage: ridk use [<option>] [--default] [--system-default] Option: Start interactive version selection list Search and list installed ruby versions update Save or update the found ruby versions to rubies.yml <number> Change the active ruby version by index /<regex>/ Change the active ruby version by regex help Display this help and exit --default Store the active ruby version in the user or --system-default system environment variables permanently EOT end def run!(args) case args[0] when "use", "useps1" ps1 = args[0] == "useps1" default = if args.delete("--default") :user elsif args.delete("--system-default") :system end case args[1] when 'help' print_help when 'list' list_rubies when 'update' update_rubies when String rubies = each_ruby.to_h rubypath = select_ruby(rubies, args[1]) unless rubypath $stderr.print "Invalid ruby: #{args[1].inspect}" exit 1 end modify_default(rubypath, rubies.values, default) puts switch_ruby_per_cmd(rubypath, rubies.values, ps1) else list_rubies rubies = each_ruby.to_h loop do $stderr.print "Select ruby version to enable: " selector = $stdin.gets.strip rubypath = select_ruby(rubies, selector) next unless rubypath modify_default(rubypath, rubies.values, default) puts switch_ruby_per_cmd(rubypath, rubies.values, ps1) break end end else $stderr.puts "Invalid option #{args[0].inspect}" end end if $0 == __FILE__ run!(ARGV) end