github.com/replit/upm@v0.0.0-20240423230255-9ce4fc3ea24c/resources/ruby/guess-gems.rb (about) 1 require 'parser/current' 2 require 'json' 3 4 # For now, we will only guess gems that come preinstalled in Polygott. 5 # The mapping between what you require and the gem name is not always 1-to-1, 6 # so we will need some sort of mapping like what we do with PyPi if we want 7 # to do complete ruby gem guessing. 8 $allowed_gems = { 9 "sinatra" => "sinatra", 10 "sinatra/base" => "sinatra", 11 "stripe" => "stripe" 12 } 13 14 def guess_gems(code, guesses) 15 root = Parser::CurrentRuby.parse(code) 16 traverse_node(root, guesses) 17 end 18 19 def traverse_node(node, guesses) 20 if node.class != Parser::AST::Node 21 return 22 end 23 24 # Looking for any s(:send, nil, :require, s(:str, "gem")) 25 if node.type == :send && node.children[1] == :require 26 req_node = node.children[2] 27 if req_node.class == Parser::AST::Node && req_node.type == :str 28 process_require(req_node.children.first, guesses) 29 return 30 end 31 end 32 33 node.children.each { |child| 34 traverse_node(child, guesses) 35 } 36 end 37 38 def process_require(req_str, guesses) 39 if req_str.empty? 40 return 41 end 42 43 # Skip absolute or relative requires 44 if req_str.start_with?('/') || req_str.start_with?('.') 45 return 46 end 47 48 gem = $allowed_gems[req_str] 49 if gem 50 guesses[gem] = [gem] 51 end 52 end 53 54 guesses = {} 55 56 Dir.glob("**/*.rb").reject {|f| f['./.bundle'] }.each { |file| 57 guess_gems(File.read(file), guesses) 58 } 59 60 puts guesses.to_json()