rbs 1.3.3 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +10 -0
  3. data/CHANGELOG.md +69 -0
  4. data/Gemfile +2 -0
  5. data/Rakefile +4 -0
  6. data/Steepfile +9 -1
  7. data/core/array.rbs +8 -7
  8. data/core/builtin.rbs +1 -1
  9. data/core/enumerable.rbs +11 -10
  10. data/core/enumerator.rbs +2 -2
  11. data/core/exception.rbs +1 -0
  12. data/core/false_class.rbs +4 -4
  13. data/core/file.rbs +3 -1
  14. data/core/float.rbs +1 -1
  15. data/core/global_variables.rbs +180 -0
  16. data/core/hash.rbs +7 -7
  17. data/core/integer.rbs +1 -2
  18. data/core/io/wait.rbs +37 -0
  19. data/core/io.rbs +11 -5
  20. data/core/kernel.rbs +25 -2
  21. data/core/object.rbs +1 -1
  22. data/core/ractor.rbs +779 -0
  23. data/core/range.rbs +11 -9
  24. data/core/string_io.rbs +3 -5
  25. data/core/true_class.rbs +4 -4
  26. data/docs/collection.md +116 -0
  27. data/lib/rbs/builtin_names.rb +1 -0
  28. data/lib/rbs/cli.rb +94 -2
  29. data/lib/rbs/collection/cleaner.rb +29 -0
  30. data/lib/rbs/collection/config/lockfile_generator.rb +95 -0
  31. data/lib/rbs/collection/config.rb +85 -0
  32. data/lib/rbs/collection/installer.rb +27 -0
  33. data/lib/rbs/collection/sources/git.rb +147 -0
  34. data/lib/rbs/collection/sources/rubygems.rb +40 -0
  35. data/lib/rbs/collection/sources/stdlib.rb +38 -0
  36. data/lib/rbs/collection/sources.rb +22 -0
  37. data/lib/rbs/collection.rb +13 -0
  38. data/lib/rbs/environment_loader.rb +12 -0
  39. data/lib/rbs/errors.rb +18 -0
  40. data/lib/rbs/parser.rb +1 -1
  41. data/lib/rbs/parser.y +1 -1
  42. data/lib/rbs/prototype/rb.rb +8 -1
  43. data/lib/rbs/prototype/runtime.rb +1 -1
  44. data/lib/rbs/repository.rb +13 -7
  45. data/lib/rbs/type_alias_dependency.rb +88 -0
  46. data/lib/rbs/validator.rb +8 -0
  47. data/lib/rbs/version.rb +1 -1
  48. data/lib/rbs.rb +2 -0
  49. data/sig/builtin_names.rbs +1 -0
  50. data/sig/cli.rbs +5 -0
  51. data/sig/collection/cleaner.rbs +13 -0
  52. data/sig/collection/collections.rbs +112 -0
  53. data/sig/collection/config.rbs +69 -0
  54. data/sig/collection/installer.rbs +15 -0
  55. data/sig/collection.rbs +4 -0
  56. data/sig/environment_loader.rbs +3 -0
  57. data/sig/errors.rbs +9 -0
  58. data/sig/polyfill.rbs +12 -3
  59. data/sig/repository.rbs +4 -0
  60. data/sig/type_alias_dependency.rbs +22 -0
  61. data/sig/validator.rbs +2 -0
  62. data/stdlib/digest/0/digest.rbs +418 -0
  63. data/stdlib/objspace/0/objspace.rbs +406 -0
  64. data/stdlib/openssl/0/openssl.rbs +3711 -0
  65. data/stdlib/pathname/0/pathname.rbs +2 -2
  66. data/stdlib/rubygems/0/rubygems.rbs +1 -1
  67. data/stdlib/securerandom/0/securerandom.rbs +3 -1
  68. data/stdlib/tempfile/0/tempfile.rbs +270 -0
  69. data/stdlib/uri/0/generic.rbs +3 -3
  70. data/steep/Gemfile.lock +10 -10
  71. metadata +28 -3
data/core/hash.rbs CHANGED
@@ -493,8 +493,8 @@ class Hash[unchecked out K, unchecked out V] < Object
493
493
  # h.fetch_values("cow", "bird") # raises KeyError
494
494
  # h.fetch_values("cow", "bird") { |k| k.upcase } #=> ["bovine", "BIRD"]
495
495
  #
496
- def fetch_values: (*K) -> Array[V]
497
- | [X] (*K) { (K) -> X } -> (V | X)
496
+ def fetch_values: (*K) -> ::Array[V]
497
+ | [X] (*K) { (K) -> X } -> ::Array[V | X]
498
498
 
499
499
  # Returns a new hash consisting of entries for which the block returns true.
500
500
  #
@@ -906,8 +906,8 @@ class Hash[unchecked out K, unchecked out V] < Object
906
906
  #
907
907
  # If no block is given, an enumerator is returned instead.
908
908
  #
909
- def transform_keys!: () -> Enumerator[K, Hash[untyped, V]]
910
- | () { (K) -> K } -> Hash[K, V]
909
+ def transform_keys!: () -> Enumerator[K, self]
910
+ | () { (K) -> K } -> self
911
911
 
912
912
  # Returns a new hash with the results of running the block once for every value.
913
913
  # This method does not change the keys.
@@ -920,7 +920,7 @@ class Hash[unchecked out K, unchecked out V] < Object
920
920
  #
921
921
  # If no block is given, an enumerator is returned instead.
922
922
  #
923
- def transform_values: () -> Enumerator[K, Hash[K, untyped]]
923
+ def transform_values: () -> Enumerator[V, Hash[K, untyped]]
924
924
  | [A] () { (V) -> A } -> Hash[K, A]
925
925
 
926
926
  # Invokes the given block once for each value in *hsh*, replacing it with the
@@ -935,8 +935,8 @@ class Hash[unchecked out K, unchecked out V] < Object
935
935
  #
936
936
  # If no block is given, an enumerator is returned instead.
937
937
  #
938
- def transform_values!: () -> Enumerator[K, Hash[K, untyped]]
939
- | () { (V) -> V } -> Hash[K, V]
938
+ def transform_values!: () -> Enumerator[V, self]
939
+ | () { (V) -> V } -> self
940
940
 
941
941
  # Adds the contents of the given hashes to the receiver.
942
942
  #
data/core/integer.rbs CHANGED
@@ -361,8 +361,7 @@ class Integer < Numeric
361
361
  # 18.floor(-1) #=> 10
362
362
  # (-18).floor(-1) #=> -20
363
363
  #
364
- def floor: () -> Integer
365
- | (int digits) -> (Integer | Float)
364
+ def floor: (?int digits) -> Integer
366
365
 
367
366
  # Returns the greatest common divisor of the two integers. The result is always
368
367
  # positive. 0.gcd(x) and x.gcd(0) return x.abs.
data/core/io/wait.rbs ADDED
@@ -0,0 +1,37 @@
1
+ class IO
2
+ # Returns number of bytes that can be read without blocking. Returns zero if no
3
+ # information available.
4
+ #
5
+ def nread: () -> Integer
6
+
7
+ # Returns `true` if input available without blocking, or `false`.
8
+ #
9
+ def ready?: () -> boolish
10
+
11
+ # Waits until the IO becomes ready for the specified events and returns the
12
+ # subset of events that become ready, or `false` when times out.
13
+ #
14
+ # The events can be a bit mask of `IO::READABLE`, `IO::WRITABLE` or
15
+ # `IO::PRIORITY`.
16
+ #
17
+ # Returns `true` immediately when buffered data is available.
18
+ #
19
+ # Optional parameter `mode` is one of `:read`, `:write`, or `:read_write`
20
+ # (deprecated).
21
+ #
22
+ def wait: (Integer events, ?Numeric timeout) -> (self | bool | nil)
23
+ | (?Numeric timeout, *wait_mode mode) -> (self | bool | nil)
24
+
25
+ type wait_mode = :read | :r | :readable
26
+ | :write | :w | :writable
27
+ | :read_write | :rw | :readable_writable
28
+
29
+ # Waits until IO is readable and returns `true`, or `false` when times out.
30
+ # Returns `true` immediately when buffered data is available.
31
+ #
32
+ def wait_readable: (?Numeric timeout) -> (self | bool | nil)?
33
+
34
+ # Waits until IO is writable and returns `true` or `false` when times out.
35
+ #
36
+ def wait_writable: (?Numeric timeout) -> (self | bool | nil)?
37
+ end
data/core/io.rbs CHANGED
@@ -532,10 +532,10 @@ class IO < Object
532
532
  # need the behavior like a single read(2) system call, consider #readpartial,
533
533
  # #read_nonblock, and #sysread.
534
534
  #
535
- def read: (?Integer? length, ?String outbuf) -> String?
535
+ def read: (?int? length, ?string outbuf) -> String?
536
536
 
537
- def read_nonblock: (Integer len) -> String
538
- | (Integer len, ?String buf) -> String
537
+ def read_nonblock: (int len, ?string buf, ?exception: true) -> String
538
+ | (int len, ?string buf, exception: false) -> (String | :wait_readable | nil)
539
539
 
540
540
  # Reads a byte as with `IO#getbyte`, but raises an `EOFError` on end of
541
541
  # file.
@@ -611,7 +611,7 @@ class IO < Object
611
611
  # on the situation IO#sysread causes Errno::EWOULDBLOCK as if the fd is blocking
612
612
  # mode.
613
613
  #
614
- def readpartial: (Integer maxlen, ?String outbuf) -> String
614
+ def readpartial: (int maxlen, ?string outbuf) -> String
615
615
 
616
616
  def reopen: (IO other_IO_or_path) -> IO
617
617
  | (String other_IO_or_path, ?String mode_str) -> IO
@@ -635,6 +635,8 @@ class IO < Object
635
635
  def set_encoding: (?String | Encoding ext_or_ext_int_enc) -> self
636
636
  | (?String | Encoding ext_or_ext_int_enc, ?String | Encoding int_enc) -> self
637
637
 
638
+ def set_encoding_by_bom: () -> Encoding?
639
+
638
640
  # Returns status information for *ios* as an object of type `File::Stat` .
639
641
  #
640
642
  # ```ruby
@@ -704,6 +706,9 @@ class IO < Object
704
706
  #
705
707
  def write: (*_ToS string) -> Integer
706
708
 
709
+ def write_nonblock: (_ToS s, ?exception: true) -> Integer
710
+ | (_ToS s, exception: false) -> (Integer | :wait_writable | nil)
711
+
707
712
  # Opens the file, optionally seeks to the given *offset*, then returns *length*
708
713
  # bytes (defaulting to the rest of the file). #binread ensures the file is
709
714
  # closed before returning. The open mode would be `"rb:ASCII-8BIT"`.
@@ -746,7 +751,8 @@ class IO < Object
746
751
 
747
752
  def self.readlines: (String name, ?String sep, ?Integer limit, ?external_encoding: String external_encoding, ?internal_encoding: String internal_encoding, ?encoding: String encoding, ?textmode: untyped textmode, ?binmode: untyped binmode, ?autoclose: untyped autoclose, ?mode: String mode) -> ::Array[String]
748
753
 
749
- def self.select: (::Array[io]? read_array, ?::Array[io]? write_array, ?::Array[io]? error_array, ?Numeric? timeout) -> ::Array[::Array[io]]?
754
+ def self.select: [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array) -> [Array[X], Array[Y], Array[Z]]
755
+ | [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array, Numeric? timeout) -> [Array[X], Array[Y], Array[Z]]?
750
756
 
751
757
  def self.sysopen: (String path, ?String mode, ?String perm) -> Integer
752
758
 
data/core/kernel.rbs CHANGED
@@ -113,6 +113,7 @@ module Kernel : BasicObject
113
113
  def self?.Array: (NilClass x) -> [ ]
114
114
  | [T] (::Array[T] x) -> ::Array[T]
115
115
  | [T] (::Range[T] x) -> ::Array[T]
116
+ | [T] (_Each[T] x) -> ::Array[T]
116
117
  | [K, V] (::Hash[K, V] x) -> ::Array[[K, V]]
117
118
  | [T] (T x) -> ::Array[T]
118
119
 
@@ -127,7 +128,15 @@ module Kernel : BasicObject
127
128
 
128
129
  def self?.Rational: (Numeric | String | Object x, ?Numeric | String y, ?exception: bool exception) -> Rational
129
130
 
130
- def self?.String: (Object x) -> String
131
+ # Returns *arg* as a String.
132
+ #
133
+ # First tries to call its `to_str` method, then its `to_s` method.
134
+ #
135
+ # String(self) #=> "main"
136
+ # String(self.class) #=> "Object"
137
+ # String(123456) #=> "123456"
138
+ #
139
+ def self?.String: (_ToStr | _ToS x) -> String
131
140
 
132
141
  # Returns the called name of the current method as a
133
142
  # [Symbol](https://ruby-doc.org/core-2.6.3/Symbol.html). If called
@@ -463,6 +472,19 @@ module Kernel : BasicObject
463
472
  # ```
464
473
  def self?.exec: (*String args) -> bot
465
474
 
475
+ type redirect_fd = Integer # redirect to the file descriptor in parent process
476
+ | :in | :out | :err # standard input / output / error
477
+ | IO # the file descriptor specified as io.fileno
478
+ | String # redirect to file with open(string, File::RDONLY)
479
+ | [String] # # redirect to file with open(string, File::RDONLY)
480
+ | [String, string | int] # redirect to file with open(string, open_mode, 0644)
481
+ | [String, string | int, int] # redirect to file with open(string, open_mode, perm)
482
+ | [:child, int] # redirect to the redirected file descriptor
483
+ | :close # close the file descriptor in child process
484
+
485
+ def self?.spawn: (String command, *String args, ?unsetenv_others: boolish, ?pgroup?: (true | Integer), ?umask: Integer, ?in: redirect_fd, ?out: redirect_fd, ?err: redirect_fd, ?close_others: boolish, ?chdir: String) -> Integer
486
+ | (Hash[string, string?] env, String command, *String args, ?unsetenv_others: boolish, ?pgroup?: (true | Integer), ?umask: Integer, ?in: redirect_fd, ?out: redirect_fd, ?err: redirect_fd, ?close_others: boolish, ?chdir: String) -> Integer
487
+
466
488
  # Executes *command…* in a subshell. *command…* is one of following forms.
467
489
  #
468
490
  # commandline : command line string which is passed to the standard shell
@@ -488,7 +510,8 @@ module Kernel : BasicObject
488
510
  # *
489
511
  #
490
512
  # See `Kernel.exec` for the standard shell.
491
- def self?.system: (*String args) -> (NilClass | FalseClass | TrueClass)
513
+ def self?.system: (String command, *String args, ?unsetenv_others: boolish, ?pgroup?: (true | Integer), ?umask: Integer, ?in: redirect_fd, ?out: redirect_fd, ?err: redirect_fd, ?close_others: boolish, ?chdir: String) -> (NilClass | FalseClass | TrueClass)
514
+ | (Hash[string, string?] env, String command, *String args, ?unsetenv_others: boolish, ?pgroup?: (true | Integer), ?umask: Integer, ?in: redirect_fd, ?out: redirect_fd, ?err: redirect_fd, ?close_others: boolish, ?chdir: String) -> (NilClass | FalseClass | TrueClass)
492
515
  end
493
516
 
494
517
  Kernel::RUBYGEMS_ACTIVATION_MONITOR: untyped
data/core/object.rbs CHANGED
@@ -206,7 +206,7 @@ class Object < BasicObject
206
206
  # enum.size # => 42
207
207
  #
208
208
  def enum_for: (Symbol method, *untyped args) ?{ (*untyped args) -> Integer } -> Enumerator[untyped, untyped]
209
- | (*untyped args) ?{ (*untyped args) -> Integer } -> Enumerator[untyped, untyped]
209
+ | () ?{ () -> Integer } -> Enumerator[untyped, self]
210
210
 
211
211
  # Creates a new Enumerator which will enumerate by calling `method` on `obj`,
212
212
  # passing `args` if any.
data/core/ractor.rbs ADDED
@@ -0,0 +1,779 @@
1
+ # Ractor is a Actor-model abstraction for Ruby that provides thread-safe
2
+ # parallel execution.
3
+ #
4
+ # Ractor.new can make new Ractor and it will run in parallel.
5
+ #
6
+ # # The simplest ractor
7
+ # r = Ractor.new {puts "I am in Ractor!"}
8
+ # r.take # wait it to finish
9
+ # # here "I am in Ractor!" would be printed
10
+ #
11
+ # Ractors do not share usual objects, so the some kind of thread-safety concerns
12
+ # such as data-race, race-conditions are not available on multi-ractor
13
+ # programming.
14
+ #
15
+ # To achieve this, ractors severely limit object sharing between different
16
+ # ractors. For example, unlike threads, ractors can't access each other's
17
+ # objects, nor any objects through variables of the outer scope.
18
+ #
19
+ # a = 1
20
+ # r = Ractor.new {puts "I am in Ractor! a=#{a}"}
21
+ # # fails immediately with
22
+ # # ArgumentError (can not isolate a Proc because it accesses outer variables (a).)
23
+ #
24
+ # On CRuby (the default implementation), Global Virtual Machine Lock (GVL) is
25
+ # held per ractor, so ractors are performed in parallel without locking each
26
+ # other.
27
+ #
28
+ # Instead of accessing the shared state, the objects should be passed to and
29
+ # from ractors via sending and receiving objects as messages.
30
+ #
31
+ # a = 1
32
+ # r = Ractor.new do
33
+ # a_in_ractor = receive # receive blocks till somebody will pass message
34
+ # puts "I am in Ractor! a=#{a_in_ractor}"
35
+ # end
36
+ # r.send(a) # pass it
37
+ # r.take
38
+ # # here "I am in Ractor! a=1" would be printed
39
+ #
40
+ # There are two pairs of methods for sending/receiving messages:
41
+ #
42
+ # * Ractor#send and Ractor.receive for when the *sender* knows the receiver
43
+ # (push);
44
+ # * Ractor.yield and Ractor#take for when the *receiver* knows the sender
45
+ # (pull);
46
+ #
47
+ #
48
+ # In addition to that, an argument to Ractor.new would be passed to block and
49
+ # available there as if received by Ractor.receive, and the last block value
50
+ # would be sent outside of the ractor as if sent by Ractor.yield.
51
+ #
52
+ # A little demonstration on a classic ping-pong:
53
+ #
54
+ # server = Ractor.new do
55
+ # puts "Server starts: #{self.inspect}"
56
+ # puts "Server sends: ping"
57
+ # Ractor.yield 'ping' # The server doesn't know the receiver and sends to whoever interested
58
+ # received = Ractor.receive # The server doesn't know the sender and receives from whoever sent
59
+ # puts "Server received: #{received}"
60
+ # end
61
+ #
62
+ # client = Ractor.new(server) do |srv| # The server is sent inside client, and available as srv
63
+ # puts "Client starts: #{self.inspect}"
64
+ # received = srv.take # The Client takes a message specifically from the server
65
+ # puts "Client received from " \
66
+ # "#{srv.inspect}: #{received}"
67
+ # puts "Client sends to " \
68
+ # "#{srv.inspect}: pong"
69
+ # srv.send 'pong' # The client sends a message specifically to the server
70
+ # end
71
+ #
72
+ # [client, server].each(&:take) # Wait till they both finish
73
+ #
74
+ # This will output:
75
+ #
76
+ # Server starts: #<Ractor:#2 test.rb:1 running>
77
+ # Server sends: ping
78
+ # Client starts: #<Ractor:#3 test.rb:8 running>
79
+ # Client received from #<Ractor:#2 rac.rb:1 blocking>: ping
80
+ # Client sends to #<Ractor:#2 rac.rb:1 blocking>: pong
81
+ # Server received: pong
82
+ #
83
+ # It is said that Ractor receives messages via the *incoming port*, and sends
84
+ # them to the *outgoing port*. Either one can be disabled with
85
+ # Ractor#close_incoming and Ractor#close_outgoing respectively. If a ractor
86
+ # terminated, its ports will be closed automatically.
87
+ #
88
+ # ## Shareable and unshareable objects
89
+ #
90
+ # When the object is sent to and from the ractor, it is important to understand
91
+ # whether the object is shareable or unshareable. Most of objects are
92
+ # unshareable objects.
93
+ #
94
+ # Shareable objects are basically those which can be used by several threads
95
+ # without compromising thread-safety; e.g. immutable ones. Ractor.shareable?
96
+ # allows to check this, and Ractor.make_shareable tries to make object shareable
97
+ # if it is not.
98
+ #
99
+ # Ractor.shareable?(1) #=> true -- numbers and other immutable basic values are
100
+ # Ractor.shareable?('foo') #=> false, unless the string is frozen due to # freeze_string_literals: true
101
+ # Ractor.shareable?('foo'.freeze) #=> true
102
+ #
103
+ # ary = ['hello', 'world']
104
+ # ary.frozen? #=> false
105
+ # ary[0].frozen? #=> false
106
+ # Ractor.make_shareable(ary)
107
+ # ary.frozen? #=> true
108
+ # ary[0].frozen? #=> true
109
+ # ary[1].frozen? #=> true
110
+ #
111
+ # When a shareable object is sent (via #send or Ractor.yield), no additional
112
+ # processing happens, and it just becomes usable by both ractors. When an
113
+ # unshareable object is sent, it can be either *copied* or *moved*. The first is
114
+ # the default, and it makes the object's full copy by deep cloning of
115
+ # non-shareable parts of its structure.
116
+ #
117
+ # data = ['foo', 'bar'.freeze]
118
+ # r = Ractor.new do
119
+ # data2 = Ractor.receive
120
+ # puts "In ractor: #{data2.object_id}, #{data2[0].object_id}, #{data2[1].object_id}"
121
+ # end
122
+ # r.send(data)
123
+ # r.take
124
+ # puts "Outside : #{data.object_id}, #{data[0].object_id}, #{data[1].object_id}"
125
+ #
126
+ # This will output:
127
+ #
128
+ # In ractor: 340, 360, 320
129
+ # Outside : 380, 400, 320
130
+ #
131
+ # (Note that object id of both array and non-frozen string inside array have
132
+ # changed inside the ractor, showing it is different objects. But the second
133
+ # array's element, which is a shareable frozen string, has the same object_id.)
134
+ #
135
+ # Deep cloning of the objects may be slow, and sometimes impossible.
136
+ # Alternatively, `move: true` may be used on sending. This will *move* the
137
+ # object to the receiving ractor, making it inaccessible for a sending ractor.
138
+ #
139
+ # data = ['foo', 'bar']
140
+ # r = Ractor.new do
141
+ # data_in_ractor = Ractor.receive
142
+ # puts "In ractor: #{data_in_ractor.object_id}, #{data_in_ractor[0].object_id}"
143
+ # end
144
+ # r.send(data, move: true)
145
+ # r.take
146
+ # puts "Outside: moved? #{Ractor::MovedObject === data}"
147
+ # puts "Outside: #{data.inspect}"
148
+ #
149
+ # This will output:
150
+ #
151
+ # In ractor: 100, 120
152
+ # Outside: moved? true
153
+ # test.rb:9:in `method_missing': can not send any methods to a moved object (Ractor::MovedError)
154
+ #
155
+ # Notice that even `inspect` (and more basic methods like `__id__`) is
156
+ # inaccessible on a moved object.
157
+ #
158
+ # Besides frozen objects, there are shareable objects. Class and Module objects
159
+ # are shareable so the Class/Module definitons are shared between ractors.
160
+ # Ractor objects are also shareable objects. All operations for the shareable
161
+ # mutable objects are thread-safe, so the thread-safety property will be kept.
162
+ # We can not define mutable shareable objects in Ruby, but C extensions can
163
+ # introduce them.
164
+ #
165
+ # It is prohibited to access instance variables of mutable shareable objects
166
+ # (especially Modules and classes) from ractors other than main:
167
+ #
168
+ # class C
169
+ # class << self
170
+ # attr_accessor :tricky
171
+ # end
172
+ # end
173
+ #
174
+ # C.tricky = 'test'
175
+ #
176
+ # r = Ractor.new(C) do |cls|
177
+ # puts "I see #{cls}"
178
+ # puts "I can't see #{cls.tricky}"
179
+ # end
180
+ # r.take
181
+ # # I see C
182
+ # # can not access instance variables of classes/modules from non-main Ractors (RuntimeError)
183
+ #
184
+ # Ractors can access constants if they are shareable. The main Ractor is the
185
+ # only one that can access non-shareable constants.
186
+ #
187
+ # GOOD = 'good'.freeze
188
+ # BAD = 'bad'
189
+ #
190
+ # r = Ractor.new do
191
+ # puts "GOOD=#{GOOD}"
192
+ # puts "BAD=#{BAD}"
193
+ # end
194
+ # r.take
195
+ # # GOOD=good
196
+ # # can not access non-shareable objects in constant Object::BAD by non-main Ractor. (NameError)
197
+ #
198
+ # # Consider the same C class from above
199
+ #
200
+ # r = Ractor.new do
201
+ # puts "I see #{C}"
202
+ # puts "I can't see #{C.tricky}"
203
+ # end
204
+ # r.take
205
+ # # I see C
206
+ # # can not access instance variables of classes/modules from non-main Ractors (RuntimeError)
207
+ #
208
+ # See also the description of `# shareable_constant_value` pragma in [Comments
209
+ # syntax](rdoc-ref:doc/syntax/comments.rdoc) explanation.
210
+ #
211
+ # ## Ractors vs threads
212
+ #
213
+ # Each ractor creates its own thread. New threads can be created from inside
214
+ # ractor (and, on CRuby, sharing GVL with other threads of this ractor).
215
+ #
216
+ # r = Ractor.new do
217
+ # a = 1
218
+ # Thread.new {puts "Thread in ractor: a=#{a}"}.join
219
+ # end
220
+ # r.take
221
+ # # Here "Thread in ractor: a=1" will be printed
222
+ #
223
+ # ## Note on code examples
224
+ #
225
+ # In examples below, sometimes we use the following method to wait till ractors
226
+ # that are not currently blocked will finish (or process till next blocking)
227
+ # method.
228
+ #
229
+ # def wait
230
+ # sleep(0.1)
231
+ # end
232
+ #
233
+ # It is **only for demonstration purposes** and shouldn't be used in a real
234
+ # code. Most of the times, just #take is used to wait till ractor will finish.
235
+ #
236
+ # ## Reference
237
+ #
238
+ # See [Ractor desgin doc](rdoc-ref:doc/ractor.md) for more details.
239
+ class Ractor
240
+ # Returns total count of Ractors currently running.
241
+ #
242
+ # Ractor.count #=> 1
243
+ # r = Ractor.new(name: 'example') { Ractor.yield(1) }
244
+ # Ractor.count #=> 2 (main + example ractor)
245
+ # r.take # wait for Ractor.yield(1)
246
+ # r.take # wait till r will finish
247
+ # Ractor.count #=> 1
248
+ #
249
+ def self.count: () -> Integer
250
+
251
+ # Returns the currently executing Ractor.
252
+ #
253
+ # Ractor.current #=> #<Ractor:#1 running>
254
+ #
255
+ def self.current: () -> untyped
256
+
257
+ # returns main ractor
258
+ #
259
+ def self.main: () -> untyped
260
+
261
+ # Make `obj` shareable between ractors.
262
+ #
263
+ # `obj` and all the objects it refers to will be frozen, unless they are already
264
+ # shareable.
265
+ #
266
+ # If `copy` keyword is `true`, the method will copy objects before freezing them
267
+ # This is safer option but it can take be slower.
268
+ #
269
+ # Note that the specification and implementation of this method are not mature
270
+ # and may be changed in the future.
271
+ #
272
+ # obj = ['test']
273
+ # Ractor.shareable?(obj) #=> false
274
+ # Ractor.make_shareable(obj) #=> ["test"]
275
+ # Ractor.shareable?(obj) #=> true
276
+ # obj.frozen? #=> true
277
+ # obj[0].frozen? #=> true
278
+ #
279
+ # # Copy vs non-copy versions:
280
+ # obj1 = ['test']
281
+ # obj1s = Ractor.make_shareable(obj1)
282
+ # obj1.frozen? #=> true
283
+ # obj1s.object_id == obj1.object_id #=> true
284
+ # obj2 = ['test']
285
+ # obj2s = Ractor.make_shareable(obj2, copy: true)
286
+ # obj2.frozen? #=> false
287
+ # obj2s.frozen? #=> true
288
+ # obj2s.object_id == obj2.object_id #=> false
289
+ # obj2s[0].object_id == obj2[0].object_id #=> false
290
+ #
291
+ # See also the "Shareable and unshareable objects" section in the Ractor class
292
+ # docs.
293
+ #
294
+ def self.make_shareable: [T] (T obj, ?copy: boolish) -> T
295
+
296
+ # Create a new Ractor with args and a block.
297
+ #
298
+ # A block (Proc) will be isolated (can't access to outer variables). `self`
299
+ # inside the block will refer to the current Ractor.
300
+ #
301
+ # r = Ractor.new { puts "Hi, I am #{self.inspect}" }
302
+ # r.take
303
+ # # Prints "Hi, I am #<Ractor:#2 test.rb:1 running>"
304
+ #
305
+ # `args` passed to the method would be propagated to block args by the same
306
+ # rules as objects passed through #send/Ractor.receive: if `args` are not
307
+ # shareable, they will be copied (via deep cloning, which might be inefficient).
308
+ #
309
+ # arg = [1, 2, 3]
310
+ # puts "Passing: #{arg} (##{arg.object_id})"
311
+ # r = Ractor.new(arg) {|received_arg|
312
+ # puts "Received: #{received_arg} (##{received_arg.object_id})"
313
+ # }
314
+ # r.take
315
+ # # Prints:
316
+ # # Passing: [1, 2, 3] (#280)
317
+ # # Received: [1, 2, 3] (#300)
318
+ #
319
+ # Ractor's `name` can be set for debugging purposes:
320
+ #
321
+ # r = Ractor.new(name: 'my ractor') {}
322
+ # p r
323
+ # #=> #<Ractor:#3 my ractor test.rb:1 terminated>
324
+ #
325
+ def self.new: (*untyped args, ?name: string) { (*untyped) -> untyped } -> Ractor
326
+
327
+ # Receive an incoming message from the current Ractor's incoming port's queue,
328
+ # which was sent there by #send.
329
+ #
330
+ # r = Ractor.new do
331
+ # v1 = Ractor.receive
332
+ # puts "Received: #{v1}"
333
+ # end
334
+ # r.send('message1')
335
+ # r.take
336
+ # # Here will be printed: "Received: message1"
337
+ #
338
+ # Alternatively, private instance method `receive` may be used:
339
+ #
340
+ # r = Ractor.new do
341
+ # v1 = receive
342
+ # puts "Received: #{v1}"
343
+ # end
344
+ # r.send('message1')
345
+ # r.take
346
+ # # Here will be printed: "Received: message1"
347
+ #
348
+ # The method blocks if the queue is empty.
349
+ #
350
+ # r = Ractor.new do
351
+ # puts "Before first receive"
352
+ # v1 = Ractor.receive
353
+ # puts "Received: #{v1}"
354
+ # v2 = Ractor.receive
355
+ # puts "Received: #{v2}"
356
+ # end
357
+ # wait
358
+ # puts "Still not received"
359
+ # r.send('message1')
360
+ # wait
361
+ # puts "Still received only one"
362
+ # r.send('message2')
363
+ # r.take
364
+ #
365
+ # Output:
366
+ #
367
+ # Before first receive
368
+ # Still not received
369
+ # Received: message1
370
+ # Still received only one
371
+ # Received: message2
372
+ #
373
+ # If close_incoming was called on the ractor, the method raises
374
+ # Ractor::ClosedError if there are no more messages in incoming queue:
375
+ #
376
+ # Ractor.new do
377
+ # close_incoming
378
+ # receive
379
+ # end
380
+ # wait
381
+ # # in `receive': The incoming port is already closed => #<Ractor:#2 test.rb:1 running> (Ractor::ClosedError)
382
+ #
383
+ def self.receive: () -> untyped
384
+
385
+ # Receive only a specific message.
386
+ #
387
+ # Instead of Ractor.receive, Ractor.receive_if can provide a pattern by a block
388
+ # and you can choose the receiving message.
389
+ #
390
+ # r = Ractor.new do
391
+ # p Ractor.receive_if{|msg| msg.match?(/foo/)} #=> "foo3"
392
+ # p Ractor.receive_if{|msg| msg.match?(/bar/)} #=> "bar1"
393
+ # p Ractor.receive_if{|msg| msg.match?(/baz/)} #=> "baz2"
394
+ # end
395
+ # r << "bar1"
396
+ # r << "baz2"
397
+ # r << "foo3"
398
+ # r.take
399
+ #
400
+ # This will output:
401
+ #
402
+ # foo3
403
+ # bar1
404
+ # baz2
405
+ #
406
+ # If the block returns a truthy value, the message will be removed from the
407
+ # incoming queue and returned. Otherwise, the messsage remains in the incoming
408
+ # queue and the following received messages are checked by the given block.
409
+ #
410
+ # If there are no messages left in the incoming queue, the method will block
411
+ # until new messages arrive.
412
+ #
413
+ # If the block is escaped by break/return/exception/throw, the message is
414
+ # removed from the incoming queue as if a truthy value had been returned.
415
+ #
416
+ # r = Ractor.new do
417
+ # val = Ractor.receive_if{|msg| msg.is_a?(Array)}
418
+ # puts "Received successfully: #{val}"
419
+ # end
420
+ #
421
+ # r.send(1)
422
+ # r.send('test')
423
+ # wait
424
+ # puts "2 non-matching sent, nothing received"
425
+ # r.send([1, 2, 3])
426
+ # wait
427
+ #
428
+ # Prints:
429
+ #
430
+ # 2 non-matching sent, nothing received
431
+ # Received successfully: [1, 2, 3]
432
+ #
433
+ # Note that you can not call receive/receive_if in the given block recursively.
434
+ # It means that you should not do any tasks in the block.
435
+ #
436
+ # Ractor.current << true
437
+ # Ractor.receive_if{|msg| Ractor.receive}
438
+ # #=> `receive': can not call receive/receive_if recursively (Ractor::Error)
439
+ #
440
+ def self.receive_if: () { (untyped) -> boolish } -> untyped
441
+
442
+ alias self.recv self.receive
443
+
444
+ # Waits for the first ractor to have something in its outgoing port, reads from
445
+ # this ractor, and returns that ractor and the object received.
446
+ #
447
+ # r1 = Ractor.new {Ractor.yield 'from 1'}
448
+ # r2 = Ractor.new {Ractor.yield 'from 2'}
449
+ #
450
+ # r, obj = Ractor.select(r1, r2)
451
+ #
452
+ # puts "received #{obj.inspect} from #{r.inspect}"
453
+ # # Prints: received "from 1" from #<Ractor:#2 test.rb:1 running>
454
+ #
455
+ # If one of the given ractors is the current ractor, and it would be selected,
456
+ # `r` will contain `:receive` symbol instead of the ractor object.
457
+ #
458
+ # r1 = Ractor.new(Ractor.current) do |main|
459
+ # main.send 'to main'
460
+ # Ractor.yield 'from 1'
461
+ # end
462
+ # r2 = Ractor.new do
463
+ # Ractor.yield 'from 2'
464
+ # end
465
+ #
466
+ # r, obj = Ractor.select(r1, r2, Ractor.current)
467
+ # puts "received #{obj.inspect} from #{r.inspect}"
468
+ # # Prints: received "to main" from :receive
469
+ #
470
+ # If `yield_value` is provided, that value may be yielded if another Ractor is
471
+ # calling #take. In this case, the pair `[:yield, nil]` would be returned:
472
+ #
473
+ # r1 = Ractor.new(Ractor.current) do |main|
474
+ # puts "Received from main: #{main.take}"
475
+ # end
476
+ #
477
+ # puts "Trying to select"
478
+ # r, obj = Ractor.select(r1, Ractor.current, yield_value: 123)
479
+ # wait
480
+ # puts "Received #{obj.inspect} from #{r.inspect}"
481
+ #
482
+ # This will print:
483
+ #
484
+ # Trying to select
485
+ # Received from main: 123
486
+ # Received nil from :yield
487
+ #
488
+ # `move` boolean flag defines whether yielded value should be copied (default)
489
+ # or moved.
490
+ #
491
+ def self.select: (*Ractor ractors, ?move: boolish, ?yield_value: untyped) -> [Ractor | Symbol, untyped]
492
+
493
+ # Checks if the object is shareable by ractors.
494
+ #
495
+ # Ractor.shareable?(1) #=> true -- numbers and other immutable basic values are frozen
496
+ # Ractor.shareable?('foo') #=> false, unless the string is frozen due to # freeze_string_literals: true
497
+ # Ractor.shareable?('foo'.freeze) #=> true
498
+ #
499
+ # See also the "Shareable and unshareable objects" section in the Ractor class
500
+ # docs.
501
+ #
502
+ def self.shareable?: (untyped obj) -> bool
503
+
504
+ # Send a message to the current ractor's outgoing port to be consumed by #take.
505
+ #
506
+ # r = Ractor.new {Ractor.yield 'Hello from ractor'}
507
+ # puts r.take
508
+ # # Prints: "Hello from ractor"
509
+ #
510
+ # The method is blocking, and will return only when somebody consumes the sent
511
+ # message.
512
+ #
513
+ # r = Ractor.new do
514
+ # Ractor.yield 'Hello from ractor'
515
+ # puts "Ractor: after yield"
516
+ # end
517
+ # wait
518
+ # puts "Still not taken"
519
+ # puts r.take
520
+ #
521
+ # This will print:
522
+ #
523
+ # Still not taken
524
+ # Hello from ractor
525
+ # Ractor: after yield
526
+ #
527
+ # If the outgoing port was closed with #close_outgoing, the method will raise:
528
+ #
529
+ # r = Ractor.new do
530
+ # close_outgoing
531
+ # Ractor.yield 'Hello from ractor'
532
+ # end
533
+ # wait
534
+ # # `yield': The outgoing-port is already closed (Ractor::ClosedError)
535
+ #
536
+ # The meaning of `move` argument is the same as for #send.
537
+ #
538
+ def self.yield: (untyped obj, ?move: boolish) -> untyped
539
+
540
+ public
541
+
542
+ alias << send
543
+
544
+ # get a value from ractor-local storage
545
+ #
546
+ def []: (Symbol | String sym) -> untyped
547
+
548
+ # set a value in ractor-local storage
549
+ #
550
+ def []=: [T] (Symbol | String sym, T val) -> T
551
+
552
+ # Closes the incoming port and returns its previous state. All further attempts
553
+ # to Ractor.receive in the ractor, and #send to the ractor will fail with
554
+ # Ractor::ClosedError.
555
+ #
556
+ # r = Ractor.new {sleep(500)}
557
+ # r.close_incoming #=> false
558
+ # r.close_incoming #=> true
559
+ # r.send('test')
560
+ # # Ractor::ClosedError (The incoming-port is already closed)
561
+ #
562
+ def close_incoming: () -> bool
563
+
564
+ # Closes the outgoing port and returns its previous state. All further attempts
565
+ # to Ractor.yield in the ractor, and #take from the ractor will fail with
566
+ # Ractor::ClosedError.
567
+ #
568
+ # r = Ractor.new {sleep(500)}
569
+ # r.close_outgoing #=> false
570
+ # r.close_outgoing #=> true
571
+ # r.take
572
+ # # Ractor::ClosedError (The outgoing-port is already closed)
573
+ #
574
+ def close_outgoing: () -> bool
575
+
576
+ def inspect: () -> String
577
+
578
+ # The name set in Ractor.new, or `nil`.
579
+ #
580
+ def name: () -> String?
581
+
582
+ # Send a message to a Ractor's incoming queue to be consumed by Ractor.receive.
583
+ #
584
+ # r = Ractor.new do
585
+ # value = Ractor.receive
586
+ # puts "Received #{value}"
587
+ # end
588
+ # r.send 'message'
589
+ # # Prints: "Received: message"
590
+ #
591
+ # The method is non-blocking (will return immediately even if the ractor is not
592
+ # ready to receive anything):
593
+ #
594
+ # r = Ractor.new {sleep(5)}
595
+ # r.send('test')
596
+ # puts "Sent successfully"
597
+ # # Prints: "Sent successfully" immediately
598
+ #
599
+ # Attempt to send to ractor which already finished its execution will raise
600
+ # Ractor::ClosedError.
601
+ #
602
+ # r = Ractor.new {}
603
+ # r.take
604
+ # p r
605
+ # # "#<Ractor:#6 (irb):23 terminated>"
606
+ # r.send('test')
607
+ # # Ractor::ClosedError (The incoming-port is already closed)
608
+ #
609
+ # If close_incoming was called on the ractor, the method also raises
610
+ # Ractor::ClosedError.
611
+ #
612
+ # r = Ractor.new do
613
+ # sleep(500)
614
+ # receive
615
+ # end
616
+ # r.close_incoming
617
+ # r.send('test')
618
+ # # Ractor::ClosedError (The incoming-port is already closed)
619
+ # # The error would be raised immediately, not when ractor will try to receive
620
+ #
621
+ # If the `obj` is unshareable, by default it would be copied into ractor by deep
622
+ # cloning. If the `move: true` is passed, object is *moved* into ractor and
623
+ # becomes inaccessible to sender.
624
+ #
625
+ # r = Ractor.new {puts "Received: #{receive}"}
626
+ # msg = 'message'
627
+ # r.send(msg, move: true)
628
+ # r.take
629
+ # p msg
630
+ #
631
+ # This prints:
632
+ #
633
+ # Received: message
634
+ # in `p': undefined method `inspect' for #<Ractor::MovedObject:0x000055c99b9b69b8>
635
+ #
636
+ # All references to the object and its parts will become invalid in sender.
637
+ #
638
+ # r = Ractor.new {puts "Received: #{receive}"}
639
+ # s = 'message'
640
+ # ary = [s]
641
+ # copy = ary.dup
642
+ # r.send(ary, move: true)
643
+ #
644
+ # s.inspect
645
+ # # Ractor::MovedError (can not send any methods to a moved object)
646
+ # ary.class
647
+ # # Ractor::MovedError (can not send any methods to a moved object)
648
+ # copy.class
649
+ # # => Array, it is different object
650
+ # copy[0].inspect
651
+ # # Ractor::MovedError (can not send any methods to a moved object)
652
+ # # ...but its item was still a reference to `s`, which was moved
653
+ #
654
+ # If the object was shareable, `move: true` has no effect on it:
655
+ #
656
+ # r = Ractor.new {puts "Received: #{receive}"}
657
+ # s = 'message'.freeze
658
+ # r.send(s, move: true)
659
+ # s.inspect #=> "message", still available
660
+ #
661
+ def send: (untyped obj, ?move: boolish) -> Ractor
662
+
663
+ # Take a message from ractor's outgoing port, which was put there by
664
+ # Ractor.yield or at ractor's finalization.
665
+ #
666
+ # r = Ractor.new do
667
+ # Ractor.yield 'explicit yield'
668
+ # 'last value'
669
+ # end
670
+ # puts r.take #=> 'explicit yield'
671
+ # puts r.take #=> 'last value'
672
+ # puts r.take # Ractor::ClosedError (The outgoing-port is already closed)
673
+ #
674
+ # The fact that the last value is also put to outgoing port means that `take`
675
+ # can be used as some analog of Thread#join ("just wait till ractor finishes"),
676
+ # but don't forget it will raise if somebody had already consumed everything
677
+ # ractor have produced.
678
+ #
679
+ # If the outgoing port was closed with #close_outgoing, the method will raise
680
+ # Ractor::ClosedError.
681
+ #
682
+ # r = Ractor.new do
683
+ # sleep(500)
684
+ # Ractor.yield 'Hello from ractor'
685
+ # end
686
+ # r.close_outgoing
687
+ # r.take
688
+ # # Ractor::ClosedError (The outgoing-port is already closed)
689
+ # # The error would be raised immediately, not when ractor will try to receive
690
+ #
691
+ # If an uncaught exception is raised in the Ractor, it is propagated on take as
692
+ # a Ractor::RemoteError.
693
+ #
694
+ # r = Ractor.new {raise "Something weird happened"}
695
+ #
696
+ # begin
697
+ # r.take
698
+ # rescue => e
699
+ # p e # => #<Ractor::RemoteError: thrown by remote Ractor.>
700
+ # p e.ractor == r # => true
701
+ # p e.cause # => #<RuntimeError: Something weird happened>
702
+ # end
703
+ #
704
+ # Ractor::ClosedError is a descendant of StopIteration, so the closing of the
705
+ # ractor will break the loops without propagating the error:
706
+ #
707
+ # r = Ractor.new do
708
+ # 3.times {|i| Ractor.yield "message #{i}"}
709
+ # "finishing"
710
+ # end
711
+ #
712
+ # loop {puts "Received: " + r.take}
713
+ # puts "Continue successfully"
714
+ #
715
+ # This will print:
716
+ #
717
+ # Received: message 0
718
+ # Received: message 1
719
+ # Received: message 2
720
+ # Received: finishing
721
+ # Continue successfully
722
+ #
723
+ def take: () -> untyped
724
+
725
+ alias to_s inspect
726
+
727
+ private
728
+
729
+ # same as Ractor.receive
730
+ #
731
+ def receive: () -> untyped
732
+
733
+ def receive_if: () { (untyped) -> boolish } -> untyped
734
+
735
+ alias recv receive
736
+
737
+ class ClosedError < StopIteration
738
+ end
739
+
740
+ class Error < RuntimeError
741
+ end
742
+
743
+ class IsolationError < Ractor::Error
744
+ end
745
+
746
+ class MovedError < Ractor::Error
747
+ end
748
+
749
+ class MovedObject < BasicObject
750
+ public
751
+
752
+ def !: (*untyped) -> untyped
753
+
754
+ def !=: (*untyped) -> untyped
755
+
756
+ def ==: (*untyped) -> untyped
757
+
758
+ def __id__: (*untyped) -> untyped
759
+
760
+ def __send__: (*untyped) -> untyped
761
+
762
+ def equal?: (*untyped) -> untyped
763
+
764
+ def instance_eval: (*untyped) -> untyped
765
+
766
+ def instance_exec: (*untyped) -> untyped
767
+
768
+ def method_missing: (*untyped) -> untyped
769
+ end
770
+
771
+ class RemoteError < Ractor::Error
772
+ public
773
+
774
+ def ractor: () -> Ractor
775
+ end
776
+
777
+ class UnsafeError < Ractor::Error
778
+ end
779
+ end