Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

RDoc orders search results alphabetically

Slide 5

Slide 5 text

to_s

Slide 6

Slide 6 text

to_s • ARGF • Addrinfo • Array • Benchmark::Tms Alphabetical Order

Slide 7

Slide 7 text

to_s • Integer • Float • BigDecimal • Object Core Class Order • ARGF • Addrinfo • Array • Benchmark::Tms Alphabetical Order

Slide 8

Slide 8 text

I wanted to build something that understands Ruby-src docs

Slide 9

Slide 9 text

rubyapi.org

Slide 10

Slide 10 text

Supports Ruby 2.3 to 3.1

Slide 11

Slide 11 text

Quick Search & Autocomplete

Slide 12

Slide 12 text

80K visitors, 100K+ page views/month

Slide 13

Slide 13 text

View source code inline or on Github

Slide 14

Slide 14 text

Execute code blocks with Ruby REPL

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

How can I help improve Ruby documentation?

Slide 17

Slide 17 text

Adding Type Signatures into Ruby Docs

Slide 18

Slide 18 text

Colby Swandale @oceanicpanda

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Call Sequences Type Signatures

Slide 26

Slide 26 text

RDoc Ruby standard documentation library

Slide 27

Slide 27 text

github.com/ruby/rdoc

Slide 28

Slide 28 text

module Rake ## # A Task is the basic unit of work in a Rakefile. Tasks have associated # actions (possibly more than one) and a list of prerequisites. When # invoked, a task will first ensure that all of its prerequisites have an # opportunity to run and then it will execute its own actions. # # Tasks are not usually created directly using the new method, but rather # use the +file+ and +task+ convenience methods. # class Task # Create a task named +task_name+ with no actions or prerequisites. Use # +enhance+ to add actions and prerequisites. def initialize(task_name, app) ... end end end

Slide 29

Slide 29 text

= Bug Triaging Guide This guide discusses recommendations for triaging bugs in Ruby's bug tracker. == Bugs with Reproducible Examples These are the best bug reports. First, consider whether the bug reported is actually an issue or if it is expected Ruby behavior. If it is expected Ruby behavior, update the issue with why the behavior is expected, and set the status to Rejected. bug_triaging.rdoc

Slide 30

Slide 30 text

$ rdoc . Parsing sources... 100% [22/22] test/stringio/test_stringio.rb Generating Darkfish format into /Users/colby/Github/stringio/doc... Files: 22 Total: 236 (163 undocumented) 30.93% documented Elapsed: 0.3s

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

$ rdoc . —format pot Parsing sources... 100% [897/897] yjit.c Generating POT format into /Users/colby/Github/ruby/docs... Files: 897 Total: 12129 (2858 undocumented) 76.44% documented Elapsed: 29.7s

Slide 33

Slide 33 text

$ rdoc . —format pot Do you know what this format is?

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

require 'rdoc' @rdoc = RDoc::RDoc.new @rdoc_options = RDoc::Options.load_options.tap |r| r.files = Dir[Dir.pwd] r.template = "rdoc" r.quiet = true end @rdoc.document @rdoc_options

Slide 37

Slide 37 text

/* * call-seq: * empty? -> true or false * * Returns +true+ if the length of +self+ is zero, +false+ otherwise: * * "hello".empty? # => false * " ".empty? # => false * "".empty? # => true * */ static VALUE rb_str_empty(VALUE str) { return RBOOL(RSTRING_LEN(str) == 0); }

Slide 38

Slide 38 text

/* * call-seq: * empty? -> true or false * * Returns +true+ if the length of +self+ is zero, +false+ otherwise: * * "hello".empty? # => false * " ".empty? # => false * "".empty? # => true * */ static VALUE rb_str_empty(VALUE str) { return RBOOL(RSTRING_LEN(str) == 0); } Description Call Sequence

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

Challenge to keep call sequences consistent

Slide 42

Slide 42 text

str[integer] = new_str str[integer, integer] = new_str str[range] = aString str[regexp] = new_str str[regexp, integer] = new_str str[regexp, name] = new_str str[other_str] = new_str String#[]

Slide 43

Slide 43 text

str[integer] = new_str str[integer, integer] = new_str str[range] = aString str[regexp] = new_str str[regexp, integer] = new_str str[regexp, name] = new_str str[other_str] = new_str String#[]

Slide 44

Slide 44 text

str[integer] = new_str str[integer, integer] = new_str str[range] = aString str[regexp] = new_str str[regexp, integer] = new_str str[regexp, name] = new_str str[other_str] = new_str String#[]

Slide 45

Slide 45 text

str[integer] = new_str str[integer, integer] = new_str str[range] = aString str[regexp] = new_str str[regexp, integer] = new_str str[regexp, name] = new_str str[other_str] = new_str Call sequences are plain text

Slide 46

Slide 46 text

Can we automatically generate method sequences?

Slide 47

Slide 47 text

Yes! 🎉

Slide 48

Slide 48 text

RBS Type Signatures for Ruby

Slide 49

Slide 49 text

github.com/ruby/rbs

Slide 50

Slide 50 text

# string.rbs class String include Comparable # https://github.com/ruby/rbs/blob/master/core/string.rbs # Returns a new String containing `other_string` # concatenated to `self`: # # "Hello from " + self.to_s # => "Hello from main” def +: (string other_str) -> String end

Slide 51

Slide 51 text

class String include Comparable def gsub: (Regexp | string pattern, string replacement) -> String | (Regexp | string pattern, Hash[String, String] hash) -> String | (Regexp | string pattern) { (String match) -> _ToS } -> String | (Regexp | string pattern) -> ::Enumerator[String, self] end

Slide 52

Slide 52 text

Create your own RBS f iles

Slide 53

Slide 53 text

# lib/application.rb class Application def hello(name) "Hello, #{name}!" end end

Slide 54

Slide 54 text

# main.rb require_relative ‘./lib/application app = Application.new app.hello(“Colby”)

Slide 55

Slide 55 text

$ rbs prototype rb lib/application.rb class Application def hello: (untyped name) -> ::String end

Slide 56

Slide 56 text

$ rbs prototype rb lib/application.rb class Application def hello: (untyped name) -> ::String end

Slide 57

Slide 57 text

$ rbs prototype rb lib/application.rb class Application def hello: (String name) -> ::String end

Slide 58

Slide 58 text

# sig/lib/application.rbs class Application def hello: (String name) -> ::String end

Slide 59

Slide 59 text

# Gemfile source "https://rubygems.org" gem "steep"

Slide 60

Slide 60 text

# Steepfile target :lib do check "lib" check "main.rb" signature "sig" end

Slide 61

Slide 61 text

$ bundle exec steep check .....................................................................

Slide 62

Slide 62 text

# main.rb require_relative ‘./lib/application app = Application.new app.hello(1)

Slide 63

Slide 63 text

$ bundle exec steep check .....................................................................F main.rb:4:15: [error] Cannot pass a value of type `::Integer` as an argument of type `::String` ! ::Integer <: ::String ! ::Numeric <: ::String ! ::Object <: ::String ! ::BasicObject <: ::String ! ! Diagnostic ID: Ruby::ArgumentTypeMismatch ! " puts app.hello(1)

Slide 64

Slide 64 text

$ ruby main.rb Hello, 1!

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

Ruby API doesn’t support RBI at the moment

Slide 67

Slide 67 text

😮💨

Slide 68

Slide 68 text

Call Sequences Type Signatures

Slide 69

Slide 69 text

$ ./bin/rake import:ruby[3.1.2]

Slide 70

Slide 70 text

def prepare_environment system "unzip #{ruby_src_download_path}” if release.has_type_signatures? system "gem unpack --target #{download_path.join("gems")} “ \ “#{download_path.join(“gems/rbs-*.gem”)}" end end

Slide 71

Slide 71 text

def prepare_environment system "unzip #{ruby_src_download_path}” if release.has_type_signatures? system "gem unpack --target #{download_path.join("gems")} “ \ “#{download_path.join(“gems/rbs-*.gem”)}" end end

Slide 72

Slide 72 text

def prepare_environment system "unzip #{ruby_src_download_path}” if release.has_type_signatures? system "gem unpack --target #{download_path.join("gems")} “ \ “#{download_path.join(“gems/rbs-*.gem”)}" end end

Slide 73

Slide 73 text

def prepare_environment system "unzip #{ruby_src_download_path}” if release.has_type_signatures? system "gem unpack --target #{download_path.join("gems")} “ \ “#{download_path.join(“gems/rbs-*.gem”)}" end end

Slide 74

Slide 74 text

if @release.has_type_signatures? require_relative "./ruby_type_signature_repository" @type_repository = RubyTypeSignatureRepository .new() end

Slide 75

Slide 75 text

if @release.has_type_signatures? require_relative "./ruby_type_signature_repository" @type_repository = RubyTypeSignatureRepository .new() end

Slide 76

Slide 76 text

@repository ||= RBS::Repository.new(no_stdlib: true).tap do |r| r.add(rbs_gem_path.join("stdlib")) end @loader = RBS::EnvironmentLoader .new(core_root: rbs_gem_path.join(CORE_PATH)) @environment = RBS::Environment.from_loader(@loader).resolve_type_names @builder = RBS::DefinitionBuilder.new(env: @environment) Setup RBS environment

Slide 77

Slide 77 text

@repository ||= RBS::Repository.new(no_stdlib: true).tap do |r| r.add(rbs_gem_path.join("stdlib")) end @loader = RBS::EnvironmentLoader .new(core_root: rbs_gem_path.join(CORE_PATH)) @environment = RBS::Environment.from_loader(@loader).resolve_type_names @builder = RBS::DefinitionBuilder.new(env: @environment) Setup RBS environment

Slide 78

Slide 78 text

@repository ||= RBS::Repository.new(no_stdlib: true).tap do |r| r.add(rbs_gem_path.join("stdlib")) end @loader = RBS::EnvironmentLoader .new(core_root: rbs_gem_path.join(CORE_PATH)) @environment = RBS::Environment.from_loader(@loader).resolve_type_names @builder = RBS::DefinitionBuilder.new(env: @environment) Setup RBS environment

Slide 79

Slide 79 text

@repository ||= RBS::Repository.new(no_stdlib: true).tap do |r| r.add(rbs_gem_path.join("stdlib")) end @loader = RBS::EnvironmentLoader .new(core_root: rbs_gem_path.join(CORE_PATH)) @environment = RBS::Environment.from_loader(@loader).resolve_type_names @builder = RBS::DefinitionBuilder.new(env: @environment) Setup RBS environment

Slide 80

Slide 80 text

if method_rdoc.type == “instance" @type_repository.signature_for_object_instance_method( object: object_rdoc.name, method: method_doc.name )&.map(&:to_s) elsif method_rdoc.type == “class" @type_repository.signature_for_object_class_method( object: object_rdoc.name, method: method_rdoc.name )&.map(&:to_s) end Fetching a method’s signatures

Slide 81

Slide 81 text

method_type = RBS::TypeName.new( name: “String”, namespace: RBS::Namespace.root ) // instance methods @builder.build_instance(method_type).methods[:to_i] &.method_types // class methods @builder.build_singleton(method_type).methods[:to_i] &.method_types Query RBS for type signature

Slide 82

Slide 82 text

Type Signatures also have their own challenges

Slide 83

Slide 83 text

Managed separately from ruby-src

Slide 84

Slide 84 text

Not optimised for humans

Slide 85

Slide 85 text

String#gsub (::Regexp | ::string pattern, ::string replacement) -> ::String (::Regexp | ::string pattern, ::Hash[::String, ::String] hash) -> ::String (::Regexp | ::string pattern) { (::String match) -> ::_ToS } -> ::String (::Regexp | ::string pattern) -> ::Enumerator[::String, self] Example

Slide 86

Slide 86 text

String#gsub (::Regexp | ::string pattern, ::string replacement) -> ::String (::Regexp | ::string pattern, ::Hash[::String, ::String] hash) -> ::String (::Regexp | ::string pattern) { (::String match) -> ::_ToS } -> ::String (::Regexp | ::string pattern) -> ::Enumerator[::String, self] Example

Slide 87

Slide 87 text

Optimised for computers humans

Slide 88

Slide 88 text

(::int index) -> ::String? (::int start, ::int length) -> ::String? (::Range[::Integer] | ::Range[::Integer?] range) -> ::String? (::Regexp regexp) -> ::String? (::Regexp regexp, ::int | ::String capture) -> ::String? (::String match_str) -> ::String?

Slide 89

Slide 89 text

(int index) -> String? (int start, int length) -> String? (Range[Integer] | Range[Integer?] range) -> String? (Regexp regexp) -> String? (Regexp regexp, int | String capture) -> String? (String match_str) -> String?

Slide 90

Slide 90 text

(int index) -> String? (int start, int length) -> String? (Range[Integer] | Range[Integer?] range) -> String? (Regexp regexp) -> String? (Regexp regexp, int | String capture) -> String? (String match_str) -> String?

Slide 91

Slide 91 text

(Integer index) -> String? (Integer start, Integer length) -> String? (Range[Integer] | Range[Integer?] range) -> String? (Regexp regexp) -> String? (Regexp regexp, Integer | String capture) -> String? (String match_str) -> String?

Slide 92

Slide 92 text

Integer index -> String? Integer start, Integer length -> String? Range[Integer] | Range[Integer?] range -> String? Regexp regexp -> String? Regexp regexp, Integer | String capture -> String? String match_str -> String?

Slide 93

Slide 93 text

Integer index -> String? Integer start, Integer length -> String? Range[Integer] | Range[Integer?] range -> String? Regexp regexp -> String? Regexp regexp, Integer | String capture -> String? String match_str -> String?

Slide 94

Slide 94 text

Range[Integer] | Range[Integer?] range -> String?

Slide 95

Slide 95 text

(::int index) -> ::String? (::int start, ::int length) -> ::String? (::Range[::Integer] | ::Range[::Integer?] range) -> ::String? (::Regexp regexp) -> ::String? (::Regexp regexp, ::int | ::String capture) -> ::String? (::String match_str) -> ::String? Integer index -> String? Integer start, Integer length -> String? Range[Integer] | Range[Integer?] range -> String? Regexp regexp -> String? Regexp regexp, Integer | String capture) -> String? String match_str -> String?

Slide 96

Slide 96 text

Next steps

Slide 97

Slide 97 text

github.com/rubyapi/rubyapi

Slide 98

Slide 98 text

Checkout the other talks about Ruby Types! Types teaches success, what will we do? - @fugakkbn Let's collect type info during Ruby running and automaticall - @pink_bangbi Ruby programming with types in action - @soutaro

Slide 99

Slide 99 text

ありがとう