rbs 1.4.0 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +10 -0
  3. data/CHANGELOG.md +52 -0
  4. data/Gemfile +2 -0
  5. data/Steepfile +9 -1
  6. data/core/builtin.rbs +1 -1
  7. data/core/file.rbs +3 -1
  8. data/core/global_variables.rbs +3 -3
  9. data/core/io/wait.rbs +37 -0
  10. data/core/io.rbs +6 -4
  11. data/core/ractor.rbs +779 -0
  12. data/core/string_io.rbs +3 -5
  13. data/docs/collection.md +116 -0
  14. data/lib/rbs/builtin_names.rb +1 -0
  15. data/lib/rbs/cli.rb +93 -2
  16. data/lib/rbs/collection/cleaner.rb +29 -0
  17. data/lib/rbs/collection/config/lockfile_generator.rb +95 -0
  18. data/lib/rbs/collection/config.rb +85 -0
  19. data/lib/rbs/collection/installer.rb +27 -0
  20. data/lib/rbs/collection/sources/git.rb +147 -0
  21. data/lib/rbs/collection/sources/rubygems.rb +40 -0
  22. data/lib/rbs/collection/sources/stdlib.rb +38 -0
  23. data/lib/rbs/collection/sources.rb +22 -0
  24. data/lib/rbs/collection.rb +13 -0
  25. data/lib/rbs/environment_loader.rb +12 -0
  26. data/lib/rbs/errors.rb +2 -0
  27. data/lib/rbs/repository.rb +13 -7
  28. data/lib/rbs/validator.rb +4 -1
  29. data/lib/rbs/version.rb +1 -1
  30. data/lib/rbs.rb +1 -0
  31. data/sig/builtin_names.rbs +1 -0
  32. data/sig/cli.rbs +5 -0
  33. data/sig/collection/cleaner.rbs +13 -0
  34. data/sig/collection/collections.rbs +112 -0
  35. data/sig/collection/config.rbs +69 -0
  36. data/sig/collection/installer.rbs +15 -0
  37. data/sig/collection.rbs +4 -0
  38. data/sig/environment_loader.rbs +3 -0
  39. data/sig/polyfill.rbs +12 -3
  40. data/sig/repository.rbs +4 -0
  41. data/stdlib/digest/0/digest.rbs +418 -0
  42. data/stdlib/objspace/0/objspace.rbs +406 -0
  43. data/stdlib/openssl/0/openssl.rbs +1 -1
  44. data/stdlib/tempfile/0/tempfile.rbs +270 -0
  45. data/steep/Gemfile.lock +10 -10
  46. metadata +24 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '019689814b3a1a5d0c1b88e2fc433acb328795f7b71c0a42889ac79bab50eb23'
4
- data.tar.gz: d7669d77ad9fcd0a211586b9f4cfcdeaee51c22d53c98f439da69a0321728021
3
+ metadata.gz: cffc862a61abaf040b4873bca9c7c93bf191400b079763825c7bfa34ff066410
4
+ data.tar.gz: 3771aaa34f52b4eda197ca53f2d09d0a474cd73daa9e11fd71e39ef6d2bbcccf
5
5
  SHA512:
6
- metadata.gz: 01101a55594365a082d5ed34ac6ca58e94ad79f275efb94e22cc381578755f210e14a984f77bc63c7eab118967c3f6774051b5c67a034ded2635a0081f19dc48
7
- data.tar.gz: 4ebe708ff34901816c8d0dfd012e9e68b7fe28e65b4a7c6101ef4f1ef284e9a44c103287f40f0f7582f93bda3f4b8a85a1c834dbff01a4b3758791d3b9388087
6
+ metadata.gz: 713cf7abebe3f03bdd6c1c7be31173db1e210180bb50b33f3587990eba18eb934668ec5d4ad6bf9e89848da29b559a493c2a2636fa319601b8af4c4b91e0d4a8
7
+ data.tar.gz: ec10a533cda84779326b21f5d00e5f9a139f6c5a8bf392640eaa52a8a1dd01b1181e7bb24709dae2fa8bdb35785e0b47fd5ff1ccc88b06fbc3ce6852975bf2a9
@@ -0,0 +1,10 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "bundler"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "daily"
7
+ - package-ecosystem: "bundler"
8
+ directory: "/steep"
9
+ schedule:
10
+ interval: "daily"
data/CHANGELOG.md CHANGED
@@ -2,6 +2,58 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.6.1 (2021-09-05)
6
+
7
+ This is a minor release including test fixes.
8
+
9
+ ## Miscellaneous
10
+
11
+ * Fix stdlib test for `Resolv::Hosts` by removing `/etc/hosts` dependency ([\#779](https://github.com/ruby/rbs/pull/779))
12
+ * Fix bundler related test for test-bundled-gems ([\#778](https://github.com/ruby/rbs/pull/778))
13
+
14
+ ## 1.6.0 (2021-09-05)
15
+
16
+ This release includes a preview of `rbs collection` commands, which is _bundler for RBS_.
17
+ The command helps you manage RBS files from gem_rbs_collection or other repositories.
18
+
19
+ This feature is a preview, and any feedback is welcome!
20
+
21
+ ## Signature updates
22
+
23
+ * objspace ([\#763](https://github.com/ruby/rbs/pull/763), [\#776](https://github.com/ruby/rbs/pull/776))
24
+ * tempfile ([\#767](https://github.com/ruby/rbs/pull/767), [\#775](https://github.com/ruby/rbs/pull/775))
25
+ * `IO#set_encoding_by_bom` ([\#106](https://github.com/ruby/rbs/pull/106))
26
+ * `OpenSSL::PKey::EC#dh_compute_key` ([\#775](https://github.com/ruby/rbs/pull/775))
27
+
28
+ ## Library changes
29
+
30
+ * Add `rbs collection` ([\#589](https://github.com/ruby/rbs/pull/589), [\#772](https://github.com/ruby/rbs/pull/772), [\#773](https://github.com/ruby/rbs/pull/773))
31
+
32
+ ## Miscellaneous
33
+
34
+ * Let `bin/annotate-with-rdoc` process nested constants/classes ([\#766](https://github.com/ruby/rbs/pull/766), [\#768](https://github.com/ruby/rbs/pull/768))
35
+ * Stop printing version mismatch message in CI ([\#777](https://github.com/ruby/rbs/pull/777))
36
+ * Update Steep and fix type errors ([\#770](https://github.com/ruby/rbs/pull/770), [\#774](https://github.com/ruby/rbs/pull/774))
37
+ * Add dependabot configuration ([\#771](https://github.com/ruby/rbs/pull/771))
38
+
39
+ ## 1.5.1 (2021-08-22)
40
+
41
+ ### Miscellaneous
42
+
43
+ * Fix Net_HTTP_test ([\#759](https://github.com/ruby/rbs/pull/759))
44
+
45
+ ## 1.5.0 (2021-08-22)
46
+
47
+ This release includes stdlib signature updates.
48
+
49
+ ### Signature updates
50
+
51
+ * digest ([\#744](https://github.com/ruby/rbs/pull/744), [\#757](https://github.com/ruby/rbs/pull/757))
52
+ * io-wait ([\#756](https://github.com/ruby/rbs/pull/756), [\#758](https://github.com/ruby/rbs/pull/758))
53
+ * `Ractor` ([\#755](https://github.com/ruby/rbs/pull/755))
54
+ * `File::Stat#size?` ([\#754](https://github.com/ruby/rbs/pull/754))
55
+ * `$-i`, `$0`, `$PROGRAM_NAME` ([\#753](https://github.com/ruby/rbs/pull/753))
56
+
5
57
  ## 1.4.0 (2021-08-19)
6
58
 
7
59
  This release includes feature enhancements including recursive `type` definition validation, improved compatibility of global variable names, and various method type updates.
data/Gemfile CHANGED
@@ -15,6 +15,8 @@ gem "json-schema"
15
15
  gem 'stackprof'
16
16
  gem "goodcheck"
17
17
  gem "dbm"
18
+ gem 'digest'
19
+ gem 'tempfile'
18
20
 
19
21
  # Test gems
20
22
  gem "rbs-amber", path: "test/assets/test-gem"
data/Steepfile CHANGED
@@ -1,13 +1,21 @@
1
+ D = Steep::Diagnostic
2
+
1
3
  target :lib do
2
4
  signature "sig"
3
5
  check "lib"
4
6
  ignore "lib/rbs/parser.rb"
5
7
  ignore "lib/rbs/prototype", "lib/rbs/test", "lib/rbs/test.rb"
6
8
 
7
- library "set", "pathname", "json", "logger", "monitor", "tsort"
9
+ library "set", "pathname", "json", "logger", "monitor", "tsort", "uri"
8
10
  signature "stdlib/strscan/0/"
9
11
  signature "stdlib/rubygems/0/"
10
12
  signature "stdlib/optparse/0/"
13
+
14
+ configure_code_diagnostics do |config|
15
+ config[D::Ruby::MethodDefinitionMissing] = :hint
16
+ config[D::Ruby::ElseOnExhaustiveCase] = :hint
17
+ config[D::Ruby::FallbackAny] = :hint
18
+ end
11
19
  end
12
20
 
13
21
  # target :lib do
data/core/builtin.rbs CHANGED
@@ -31,7 +31,7 @@ interface _Each[out A]
31
31
  end
32
32
 
33
33
  interface _Reader
34
- def read: (?int length, ?string outbuf) -> String?
34
+ def read: (?int? length, ?string outbuf) -> String?
35
35
  end
36
36
 
37
37
  interface _ReaderPartial
data/core/file.rbs CHANGED
@@ -1042,7 +1042,7 @@ class File::Stat < Object
1042
1042
 
1043
1043
  def rdev_minor: () -> Integer
1044
1044
 
1045
- def read: (?Integer length, ?String outbuf) -> String
1045
+ def read: (?int? length, ?string outbuf) -> String?
1046
1046
 
1047
1047
  def readable?: () -> bool
1048
1048
 
@@ -1054,6 +1054,8 @@ class File::Stat < Object
1054
1054
 
1055
1055
  def size: () -> Integer
1056
1056
 
1057
+ def size?: () -> Integer?
1058
+
1057
1059
  def socket?: () -> bool
1058
1060
 
1059
1061
  def sticky?: () -> bool
@@ -48,7 +48,7 @@ $-a: bool
48
48
  $-d: bool
49
49
 
50
50
  # In in-place-edit mode, this variable holds the extension, otherwise +nil+.
51
- $-i: bool
51
+ $-i: String?
52
52
 
53
53
  # True if option <tt>-l</tt> is set. Read-only variable.
54
54
  $-l: bool
@@ -74,7 +74,7 @@ $.: Integer
74
74
  # The input record separator, newline by default. Aliased to $-0.
75
75
  $/: String | nil
76
76
 
77
- # The same as $!.backtrace.
77
+ # Contains the name of the script being executed. May be assignable.
78
78
  $0: String
79
79
 
80
80
  # The Nth group of the last successful match. May be > 1.
@@ -149,7 +149,7 @@ $LOADED_FEATURES: Array[String]
149
149
  # the path the original Kernel#require method would load.
150
150
  $LOAD_PATH: Array[String]
151
151
 
152
- # The same as $!.backtrace.
152
+ # Contains the name of the script being executed. May be assignable.
153
153
  $PROGRAM_NAME: String
154
154
 
155
155
  # The verbose flag, which is set by the <tt>-w</tt> or <tt>-v</tt> switch.
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 buf, ?exception: true) -> String
538
- | (Integer len, ?String buf, exception: false) -> (String | :wait_readable | nil)
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
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