Class: Sord::RbiGenerator
- Inherits:
-
Object
- Object
- Sord::RbiGenerator
- Defined in:
- lib/sord/rbi_generator.rb
Overview
Converts the current working directory’s YARD registry into an RBI file.
Instance Attribute Summary collapse
-
#object_count ⇒ Integer
readonly
The number of objects this generator has processed so far.
-
#rbi_contents ⇒ Array<String>
readonly
The lines of the generated RBI file so far.
Instance Method Summary collapse
-
#add_methods(item) ⇒ void
Given a YARD NamespaceObject, add lines defining its methods and their signatures to the current RBI file.
-
#add_mixins(item) ⇒ void
Given a YARD CodeObject, add lines defining its mixins (that is, extends and includes) to the current RBI file.
-
#count_object ⇒ void
Increment the object counter.
-
#initialize ⇒ RbiGenerator
constructor
Create a new RBI generator.
-
#run(filename) ⇒ void
Generates the RBI file and writes it to the given file path.
Constructor Details
#initialize ⇒ RbiGenerator
Create a new RBI generator.
19 20 21 22 23 24 25 26 27 |
# File 'lib/sord/rbi_generator.rb', line 19 def initialize @rbi_contents = ['# typed: strong'] @object_count = 0 # Hook the logger so that messages are added as comments to the RBI file Logging.add_hook do |type, msg, item| rbi_contents << " # sord #{type} - #{msg}" end end |
Instance Attribute Details
#object_count ⇒ Integer (readonly)
Returns The number of objects this generator has processed so far.
15 16 17 |
# File 'lib/sord/rbi_generator.rb', line 15 def object_count @object_count end |
#rbi_contents ⇒ Array<String> (readonly)
Returns The lines of the generated RBI file so far.
11 12 13 |
# File 'lib/sord/rbi_generator.rb', line 11 def rbi_contents @rbi_contents end |
Instance Method Details
#add_methods(item) ⇒ void
This method returns an undefined value.
Given a YARD NamespaceObject, add lines defining its methods and their signatures to the current RBI file.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/sord/rbi_generator.rb', line 55 def add_methods(item) # TODO: block documentation item.meths.each do |meth| count_object parameter_list = meth.parameters.map do |name, default| # Handle these three main cases: # - def method(param) or def method(param:) # - def method(param: 'default') # - def method(param = 'default') if default.nil? "#{name}" elsif !default.nil? && name.end_with?(':') "#{name} #{default}" else "#{name} = #{default}" end end.join(", ") # This is better than iterating over YARD's "@param" tags directly # because it includes parameters without documentation = meth.parameters.map do |name, _| [name, meth.('param').find { |p| p.name == name }] end.to_h sig_params_list = .map do |name, tag| if tag "#{name}: #{TypeConverter.yard_to_sorbet(tag.types, meth)}" elsif name.start_with? '*' # TODO: is there a YARD definition for this? "args: T::Array[T.any]" elsif meth.path.end_with? '=' # Look for the matching getter method getter_path = meth.path[0...-1] getter = item.meths.find { |m| m.path == getter_path } unless getter Logging.omit("no YARD type given for #{name.inspect}, using T.untyped", meth) next "#{name}: T.untyped" end inferred_type = TypeConverter.yard_to_sorbet( getter.('return').flat_map(&:types), meth) Logging.infer("inferred type of parameter #{name.inspect} as #{inferred_type} using getter's return type", meth) # Get rid of : on keyword arguments. name = name.chop if name.end_with?(':') "#{name}: #{inferred_type}" else Logging.omit("no YARD type given for #{name.inspect}, using T.untyped", meth) # Get rid of : on keyword arguments. name = name.chop if name.end_with?(':') "#{name}: T.untyped" end end.join(", ") = meth.('return') returns = if .length == 0 "void" elsif .length == 1 && .first.types.first.downcase == "void" "void" else "returns(#{TypeConverter.yard_to_sorbet(meth.tag('return').types, meth)})" end prefix = meth.scope == :class ? 'self.' : '' sig = sig_params_list.empty? ? " sig { #{returns} }" : " sig { params(#{sig_params_list}).#{returns} }" rbi_contents << sig rbi_contents << " def #{prefix}#{meth.name}(#{parameter_list}); end" end end |
#add_mixins(item) ⇒ void
This method returns an undefined value.
Given a YARD CodeObject, add lines defining its mixins (that is, extends and includes) to the current RBI file.
39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/sord/rbi_generator.rb', line 39 def add_mixins(item) extends = item.instance_mixins includes = item.class_mixins extends.each do |this_extend| rbi_contents << " extend #{this_extend.path}" end includes.each do |this_include| rbi_contents << " include #{this_include.path}" end end |
#count_object ⇒ void
This method returns an undefined value.
Increment the object counter.
31 32 33 |
# File 'lib/sord/rbi_generator.rb', line 31 def count_object @object_count += 1 end |
#run(filename) ⇒ void
This method returns an undefined value.
Generates the RBI file and writes it to the given file path.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/sord/rbi_generator.rb', line 133 def run(filename) # Get YARD ready YARD::Registry.load! # TODO: constants? # Populate the RBI with modules first YARD::Registry.all(:module).each do |item| count_object rbi_contents << "module #{item.path}" add_mixins(item) add_methods(item) rbi_contents << "end" end # Now populate with classes YARD::Registry.all(:class).each do |item| count_object superclass = (item.superclass if item.superclass.to_s != "Object") rbi_contents << "class #{item.path} #{"< #{superclass}" if superclass}" add_mixins(item) add_methods(item) rbi_contents << "end" end # Write the file raise "no filename specified" unless filename File.write(filename, rbi_contents.join(?\n)) Logging.done("Processed #{object_count} objects") rescue Logging.error($!) $@.each do |line| puts " #{line}".light_white end end |