Issue #18996 has been updated by st0012 (Stan Lo).
@hasumikin proposed a more powerful and mature design and it has been merged. So I think both this and #19010 can be closed :-)
----------------------------------------
Feature #18996: Proposal: Introduce new APIs to reline for changing dialog UI colours
https://bugs.ruby-lang.org/issues/18996#change-105270
* Author: st0012 (Stan Lo)
* Status: Open
* Priority: Normal
----------------------------------------
### TL;DR
I want to add APIs to `reline` for changing its dialog item's colors.
The APIs I want to add actually have been merged but becaue:
1. This is a design change
2. The maintainer @aycabta is not available to approve nor reject them
I want to raise it here to decide if we should:
1. Drop them
2. Modify them
3. Officiallty accept them
### Background
After version `1.4`, `irb` provides autocompletion support, which is a great feature and has increased many developers' productivity significantly.
But there's an user-experience issue: the completion items' UI colors (set in `reline`) [are not configurable](https://github.com/ruby/reline/blob/9ab5850444b49aff8e360a84e…. So depending on the user's terminal theme, some may find it hard to use because the background and the text having low-contrast colors, like this:
![](https://user-images.githubusercontent.com/3303032/148653612-e3dff786-1a10-4923-a0eb-3975cae10a7f.png)
And if that happens, the user has no way to fix it. This caused users to open issues like:
- https://github.com/ruby/irb/issues/351
- https://github.com/ruby/irb/issues/328
for being able to change it.
Some users even decided to disable it completely because the colors are unreadable to them. I have also seen people sharingtips for disabling this feature: [example](https://twitter.com/sdogruyol/status/1538512030449254400). So I believe it may be bothering many developers.
Personally I really like this feature but the background also bothers me:
![Screenshot 2022-09-07 at 22 55 12](https://user-images.githubusercontent.com/5079556/188990620-5ec7ba0c-97…
And that's why I want to improve it by making the colors configurable and potentially also by providing simple light/dark themes from `irb`.
### Proposal
For the dialog UI, there are 2 element states: `highlighted` and `default`. In `irb`'s case, the selected completion candidate will be `highlighted`, and the rest of options will be `default`. And each state has 2 colors: `foreground (text)` and `background (block)`.
This means the `reline` should allow `irb` and/or users to configure:
- Default items' foreground color
- Default items' background color
- Highlighted items' foreground color
- Highlighted items' background color
That brings us to these APIs:
- `Reline.dialog_default_fg_color`
- `Reline.dialog_default_bg_color`
- `Reline.dialog_highlight_fg_color`
- `Reline.dialog_highlight_bg_color`
And because `reline` only supports coloring through ANSI sequences, these APIs only has 8 available colors if we exclude their bright variants:
- Black
- Red
- Green
- Yellow
- Blue
- Magenta
- Cyan
- White
Given the limited options and also to prevent users from entering non-color ANSI sequences, these APIs only take color names directly:
- :black
- :red
- :green
- :yellow
- :blue
- :magenta
- :cyan
- :white
Example:
```rb
Reline.dialog_default_bg_color = :black
puts Reline.dialog_default_bg_color_sequence #=> 40
Reline.dialog_default_fg_color = :white
puts Reline.dialog_default_fg_color_sequence #=> 37
Reline.dialog_highlight_bg_color = :blue
puts Reline.dialog_highlight_bg_color_sequence #=> 34
Reline.dialog_highlight_fg_color = :black
puts Reline.dialog_highlight_fg_color_sequence #=> 30
```
I have made a [proof of concept PR](https://github.com/ruby/irb/pull/380) on `irb` to show what these APIs can achieve if they or similar ones are adopted.
#### Related PRs
The related changes are made through multiple PRs:
- [Initial APIs PR by @pocari](https://github.com/ruby/reline/pull/413)
- [PR to improve the APIs and make them safer to use](https://github.com/ruby/reline/pull/454)
- [PR to rename the APIs](https://github.com/ruby/reline/pull/456)
#### Other Thoughts
This is more of a concern on the `irb` part, but to make the UI looks comfortable, I think it's better to follow these conditions:
1. An item's foreground and background colors should have high contrast with each other so the texts (foreground) are readable.
2. For the `highlighted` item, its background color should be easily distinguishable from the rest of `default` items.
3. When using dark terminal themes, the `default` items' background is better to be dark as well.
---Files--------------------------------
Screenshot 2022-10-18 at 15.25.36.png (9.7 KB)
--
https://bugs.ruby-lang.org/
Issue #19576 has been reported by jprokop (Jarek Prokop).
----------------------------------------
Bug #19576: Backport request: Gemfile.lock resolving is broken with bundler shipped with Ruby 3.1.4
https://bugs.ruby-lang.org/issues/19576
* Author: jprokop (Jarek Prokop)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
This is a backport request for bundler, that regressed in 2.3.36 in a specific situation. Newer and older bundler versions that ship with Ruby are not problematic, only the version that ships with Ruby version >= 3.1.3.
A few weeks ago, we discovered a bug in resolving in bundler shipped with Ruby 3.1.3 and 3.1.4:
Bundler version:
```
$ bundler --version
Bundler version 2.3.26
```
Affected rubies `ruby -v`:
First:
```
$ ruby -v
ruby 3.1.4p223 (2023-03-30 revision 957bb7cb81) [x86_64-linux]
```
Second:
```
$ruby -v
ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e) [x86_64-linux]
```
Initial bug report with reproducer and more in-depth description can be found here: https://github.com/sclorg/s2i-ruby-container/issues/469
Using the following Gemfile for a rails app:
https://github.com/sclorg/rails-ex/blob/67b7a61eae9efa1088ff3f634ae316e1022…
bundler locks up in trying to resolve Nokogiri for Ruby 3.1, but keeps failing because it keeps using incompatible built binary gem instead of falling back to installing and building the binary extension of Nokogiri locally.
We craft this Gemfile to be usable from Ruby 2.5 up to Ruby 3.1, as the app is used mainly for testing.
I have created a patch to fix the situation, see the attached files. There are 2 of them, one contains the fix and the other one contains the test from the rubygems repo PR#6225.
full commit available here: https://src.fedoraproject.org/fork/jackorp/rpms/ruby/c/5ef600a8f40b76de5636…
The patches are created from the following upstream changes in bundler:
https://github.com/rubygems/rubygems/pull/6225
and adapted:
https://github.com/rubygems/rubygems/commit/7b64c64262a7a980c0eb23b96ea56cf…
for the PR#6225.
With the fix applied I no longer have issues doing `bundle install` with our Gemfile.lock.
---Files--------------------------------
rubygem-bundler-2.3.26-Tests-from-bundler-PR-6225.patch (1.82 KB)
rubygem-bundler-2.3.26-Provide-fix-for-bundler-Gemfile-resolving-regression.patch (5.21 KB)
--
https://bugs.ruby-lang.org/
Issue #19924 has been reported by kddnewton (Kevin Newton).
----------------------------------------
Bug #19924: Character literal escaped \xFF stops parsing
https://bugs.ruby-lang.org/issues/19924
* Author: kddnewton (Kevin Newton)
* Status: Open
* Priority: Normal
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
When you have a character literal, you can have an escaped character. For things that don't actually need escaping, this works out to just returning the character itself. For example:
``` ruby
?\d # => "d"
```
This works for every codepoint in ASCII-8bit _except_ 0xFF. When that byte is hit, the entire parser will stop parsing and just return `nil`.
``` ruby
eval(<<~RUBY)
# encoding: ascii-8bit
p ?\\\xFF
puts "this will never be parsed"
RUBY
```
If you try to parse it with `RubyVM::AbstractSyntaxTree`, it's clear the parser just stops:
``` ruby
RubyVM::AbstractSyntaxTree.parse(<<~RUBY)
# encoding: ascii-8bit
p ?\\\xFF
puts "this will never be parsed"
RUBY
# => (SCOPE@1:0-2:1 tbl: [] args: nil body: (VCALL@2:0-2:1 :p))
```
--
https://bugs.ruby-lang.org/
Issue #19842 has been reported by ko1 (Koichi Sasada).
----------------------------------------
Feature #19842: Intorduce M:N threads
https://bugs.ruby-lang.org/issues/19842
* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
* Assignee: ko1 (Koichi Sasada)
----------------------------------------
This ticket proposes to introduce M:N threads to improve Threads/Ractors performance.
## Background
Ruby threads (RT in short) are implemented from old Ruby versions and they have the following features:
* Can be created with simple notation `Thread.new{}`
* Can be switched to another ready Ruby thread by:
* Time-slice.
* I/O blocking.
* Synchronization such as Mutex features.
* And other blocking reasons.
* Can be interruptible by:
* OS-deliver signals (only for the main thread).
* `Thread#kill`.
* Can be terminated by:
* the end of each Ruby thread.
* the end of the main thread (and other Ruby threads are killed).
Ruby 1.8 and erlier versions uses M:1 threads (green threads, user level threads, .... the word 1:N threads is more popular but to make this explanation consistent I use "M:1" term here) which manages multiple Ruby threads on 1 native thread.
(Native threads are provided by C interfaces such as Pthreads. In many cases, native threads are OS threads, but there are also user-level implementations, such as user-level pthread libraries in theory. Therefore, they are referred to as native threads in this article and NT in short)
If a Ruby thread T1 blocked because of a I/O operation, Ruby interpreter switches to the next ready Ruby thread T2. The I/O operation will be monitors by a `select()` (or similar) functionality and if the I/O is ready, T1 is marked as a ready thread and T1 will be resumed soon. However, when a Ruby thread issues some other blocking operations such as `gethostbyname()`, Ruby interpreter can not swtich to any other Ruby thread while `gethostbyname()` is not finished.
We named two types blocking operations:
* Managed blocking operations
* I/O (most of read/write)
* manage by I/O multiplexing API (select, poll, epoll, kqueue, IOCP, io_uring, ...)
* Sleeping
* Synchronization (Mutex, Queue, ...)
* Unmanaged operations
* All other blocking operations not listed above, written in C
* Huge number calculation like `Bignum#*`
* DNS lookup
* I/O (can not detect block-able or not by multiplexing API)
* open on FIFO, close on NFS, ...
* flock and other locking mechanism
* library call which uses blocking operations
* `libfoo` has `foo_func()` and `foo_func()` waits DNS lookup. A Ruby extension `foo-ruby` can call `foo_func()`.
With these terms we can say that M:1 threads can suport managed blocking operations but can not support unmanaged operations (can not make progress other Ruby threads) without further tricks.
Note that if the `select()`-like system calls say a `fd` is ready, but the I/O opeartion for `fd` can be blocked because of some contention (read by another thread or process, for example).
M:1 threads has another disadvantage that it can not run in parallel because only a native thread is used.
From Ruby 1.9 we had implemented 1:1 thread which means a Ruby thread has a corresponding native thread. To make implementation easy we also introduced a GVL. Only a Ruby thread acquires GVL can run. With 1:1 model, we can support managed blocking oprations and unmanaged blocking operations by releasing GVL. When a Ruby thread want to issue a blocking operation, the Ruby thread releases GVL and another ready Ruby threads continue to run. We don't care the blocking operation is managed or unmanaged.
(We can not make some of unmanaged blocking operations interruptible (stop by Ctrl-C for example)).
Advantages of 1:1 threads to the M:1 threads is:
* Easy to handle blocking operations by releasing GVL.
* We can utilize parallelism with multiple native threads by releasing GVL.
Disadvantages of 1:1 threads to the M:1 threads is:
* Overhead to make many native threads for many Ruby threads
* We can not make huge number of Ruby threads and Ractors on 1:1 threads.
* Thread switching overhead by GVL because inter-core communication is needed.
From Ruby 3.0 we introduced fiber scheduler mechanism to maintain multiple fibers
Differences between Ruby 1.8 M:1 threads are:
* No timeslice (only switch fibers by managed blocking operations)
* Ruby users can make own schedulers for apps with favorite underlying mechanism
Disadvantages are similar to M:1 threads. Another disadvantages is we need to consider about Fiber's behavior.
From Ruby 3.0 we also introduced Ractors. Ractors can run in parallel because of separating most of objects. 1 Ractor creates 1 Ruby thread, so Ractors has same disadvantages of 1:1 threads. For example, we can not make huge number of Ractors.
## Goal
Our goal is making lightweight Ractors on lightweight Ruby threads. To enable this goal we propose to implement M:N threads on MRI.
M:N threads manages M Ruby threads on N native threads, with limited N (~= CPU core numbers for example).
Advantages of M:N threads are:
1. We can run N ractors on N native threads simultaneously if the machine has N cores.
2. We can make huge number of Ruby threads or Ractors because we don't need huge number of native threads
3. We can support unmanaged blocking operations by locking a native thread to a Ruby thread which issues an unmanaged blocking operation.
4. We can make our own Ruby threads or Ractors scheduler instead of the native thread (OS) scheduler.
Disadvantages of M:N threads are:
1. It is complex implmentation and it can be hard.
2. It can introduce incompatibility especaially on TLS (Thread local storage).
3. We need to maitain our own scheduler.
Without using multiple Ractors, it is similar to Ruby 1.8 M:1 threads. The difference with M:1 threads are locking NT mechanism to support unmanaged blocking operations. Another advantage is that it is easy to fallback to 1:1 threads by locking all of corresponding native threads to Ruby threads.
## Proposed design
### User facing changes
If a program only has a main Ractor (i.e., most Ruby programs), the user will not face any changes by default.
On main Ractor, all threads are 1:1 threads by default and there is no compatibility issue.
`RUBY_MN_THREADS=1` envrionment variable is given, main Ractor enables M:N threads.
Note that the main thread locks NT by default because the initial NT is special in some case. I'm not sure we can relax this limitation.
On the multiple Ractors, N (+ alpha) native threads run M ractors. Now there is no way to disable M:N threads on multiple Ractors because there are only a few multi-Ractor programs and no compatibility issues.
Maximum number of N can be specified by `RUBY_MAX_PROC=N`. 8 by default but this value should be specified with the number of CPU processors (cores).
### TLS issue
On M:N threads a Ruby thread (RT1) migrates from a native thread (NT1) to NT2, ... so that TLS on native code can be a problem.
For example, RT1 calls a library function `foo()` and it set TLS1 on NT1. After migrating RT1 to NT2, RT1 calls `foo()` again but there is no TLS1 record because TLS1 is recorded only on NT1.
On this case, RT1 should be run on NT1 while using native library foo. To avoid such prbolem, we need the following features:
* 1:1 threads on main Ractor by default
* functionality to lock the NT for RT, maybe `Thread#lock_native_thread` and `Thread#unlock_native_thread` API is needed. For example, Go language has `runtime.LockOSThread()` and `runtime.UnlockOSThread()` for this purpose.
* Or C-API only for this purpose? (not fixed yet)
Thankfully, the same problem can occur with Fiber scheduler (and of course Ruby 1.8 M:1 threads), but I have not heard of it being much of a problem, so I expect that TLS will not be much of an issue.
### Unmanaged blocking operations
From Ruby 1.9 (1:1 threads), the `nogvl(func)` API is used for most blocking operations to keep the threading system healthy. In other words, `nogvl(func)` represents that the given function is blocking operation. To support unmanaged blocking operations, we lock a native thread for the Ruby thread which issues blocking operation.
If the blocking operations doesn't finish soon, other Ruby threads can not run because a RT locks NT. In this case, another system monitoring thread named "Timer thread" (historical name and TT in short) creates another NT to run ready other Ruby threads.
This TT's behavior is the same as the behavior of "sysmon" in the Go language.
We named locked NT as dedicated native threads (DNT) and other NT as shared native threads (SNT). The upper bound by `RUBY_MAX_PROC` affects the number of SNT. In other words, the number of DNT is not limited (it is same that the number of NT on 1:1 threads are not limited).
### Managed blocking operations
Managed blocking operations are multiplexing by `select()`-like functions on the Timer thread.. Now only `epoll()` is supported.
I/O operation flow (read on fd1) on Ruby thread RT1:
1. check the ready-ness of fd1 by `poll(timeout = 0)`, goto step 4.
2. register fd1 to Timer thread (TT) epoll and resume another ready Ruby thread.
3. If TT detects that the fd1 is ready, make RT1 as ready thread.
4. When RT1 is resumed, then do `read()` by locking corresponding NT1.
`sleep(n)` operation flow on Ruby thread RT1:
1. register timeout of RT1 to TT epoll.
2. If TT detects the timeout of RT1 (n seconds), TT makes RT1 as a ready Ruby thread.
### Internal design
* 2 level scheduling
* Ruby threads of a Ractor is managed by 1:N threads
* Ruby threads of different Ractors are managed by M:N threads
* Timer thread has several duties
1. Monitoring I/O (or other event) ready-ness
2. Monitoring timeout
3. Produce timeslice signals
4. Help OS signal delivering
(On pthread environment) recent Ruby doesn't make timer thread but MaNy implementation makes TT anytime. it can be improved.
## Implementation
The code name is MaNy project, it is from MN threads.
https://github.com/ko1/ruby/tree/many2
The implementation is not matured (debugging now).
## Measurements
See RubyKaigi 2023 slides: https://atdot.net/~ko1/activities/2023_rubykaigi2023.pdf
## Discussion
* Enable/disable
* default behavior
* how to switch the behavior
* Should we lock the NT for main thread anytime?
* Ruby/C API to lock the native threads
## Misc
This description will be improved more later.
--
https://bugs.ruby-lang.org/
Issue #19995 has been reported by kjtsanaktsidis (KJ Tsanaktsidis).
----------------------------------------
Feature #19995: Proposal: Signal._trap as analogue to Process._fork
https://bugs.ruby-lang.org/issues/19995
* Author: kjtsanaktsidis (KJ Tsanaktsidis)
* Status: Open
* Priority: Normal
----------------------------------------
This is a proposal to define a method `Signal#_trap`, which would function as a single place to be wrapped by monitoring libraries for detecting when signal handlers are entered and exited.
## Motivation #1 - stack-based context leaks into signal handlers
When writing monitoring & instrumentation libraries, we often want to add context to emitted logs/metrics/etc based on the current call stack. For example, `ActiveSupport::TaggedLogging` allows you to write code like this:
```
$logger = ActiveSupport::TaggedLogging.new(Logger.new($stderr))
def method_one
$logger.tagged("method_one") do
method_two
end
end
def method_two
$logger.tagged("method_two") do
$logger.info "hello there"
end
end
method_one # logs "[method_one] [method_two] hello there"
```
Signal handlers can complicate this picture though because they can run at arbitrary points in the program. Using a threadlocal variable to store context, as is commonly done, means that the invoked signal handler shares the context of whatever thread it interrupted. For example, this program will log the "method_one" tag from inside the signal handler:
```
# n.b. - need to use the mono_logger gem instead of stdlib ::Logger because ::Logger contains a mutex,
# which cannot be used from a trap handler.
$logger = ActiveSupport::TaggedLogging.new(MonoLogger.new($stderr))
Signal.trap(:TERM) do
$logger.tagged("term_handler") do
$logger.info "goodbye there"
exit
end
end
def method_one
$logger.tagged("method_one") do
Process.kill :TERM, Process.pid
sleep
end
end
method_one # logs "[method_one] [term_handler] goodbye there"
```
This is, in my opinion, undesirable. The fact that the signal handler happened to interrupt at this point and not some other point does not mean that the handler has anything at all to do with `method_one`. I would like a way to, in the logger implementation, detect that we have entered a signal handler and switch to a different context stack, so that the above example printed "[term_handler] goodbye there" instead.
## Motivation #2 - avoiding mutex use in signal handlers
This motivation is hinted at by the use of the `mono_logger` gem in the preceding example. It’s not legal to use a mutex in a trap handler, which means `::Logger` can’t be used. However, the mutex is there for a good reason - it synchronises writes to the log device amongst multiple threads so that messages are written atomically, even if they’re longer than `PIPE_BUF` (the max size write the OS guarantees is atomic in a single write syscall). Ideally, we would like to use the mutex-based implementation in normal code, but avoid the mutex in trap handlers (possibly writing to a different stream e.g. `$stderr` instead of `$stdout`).
For an instrumentation implementation to do this, it needs a way to detect if it’s currently running inside a trap handler.
## Proposed solution
I would like to add a "hook" method `Signal._trap`, which works in a similar way to `Process._fork`. Essentially, when Ruby invokes a signal handler, instead of directly invoking the registered proc, it would invoke `Signal._trap`, and _that_ would invoke the proc. This means that by prepending to `Signal.singleton_class`, you could write code which wraps around all signal handlers. My first example could then be re-written like so:
```
$logger = ActiveSupport::TaggedLogging.new(MonoLogger.new($stderr))
Signal.singleton_class.prepend(Module.new do
def _trap(signo)
old_tags = $logger.pop_tags 1_000_000_000 # ActiveSupport hack; there's no "pop_all" method.
super
$logger.push_tags old_tags
end
end)
Signal.trap(:TERM) do
$logger.tagged("term_handler") do
$logger.info "goodbye there"
exit
end
end
def method_one
$logger.tagged("method_one") do
Process.kill :TERM, Process.pid
sleep
end
end
method_one # logs "[term_handler] goodbye there"
```
## Polyfill for older Rubies
I believe `Signal._trap` can be fully implemented in a gem for older versions of Ruby; something like the following will work: https://gist.github.com/KJTsanaktsidis/0b263c76523a16a049fa5a035e868a68. This is, again, analogous to how monitoring libraries would handle fork hooking before `_fork` was added - possible, but very tricky and ugly.
## Alternative solution
Another option which might help solve the two problems I outlined is to expose the `ec->interrupt_mask |= TRAP_INTERRUPT_MASK flag` as a method on `Signal` or a special `$MAGIC_GLOBAL` variable. This would essentially let code detect whether or not it’s in a trap handler, but not execute specific code around trap handlers.
----
Thanks for your time friends, looking forward to any feedback or discussion!
--
https://bugs.ruby-lang.org/
Issue #14548 has been updated by zverok (Victor Shepelev).
> What about using `dig`
Actually, many codebases are already using `dig` in those cases (and it is _clearer_ without redefinition on `nil`, telling the reader "we are aware there might be `nil` here"):
```ruby
array_or_nil&.dig(index)
```
...but using the proposed solution is shorter and cleaner.
> There is also nothing wrong with `array_or_nil && array_or_nil[index]`.
...until it is part of the message chain:
```ruby
# bad
calculate_some_value(with, some, arguments) && calculate_some_value(with, some, arguments)[index]
# wordy, requires inventing new names:
intermediate = calculate_some_value(with, some, arguments)
intermediate && intermediate[index]
```
Though, even if there is already a variable, `foo && foo[bar]` is already non-DRY and impends reading.
> IMO these `?.[` look too cryptic.
I honestly don't see how it is more cryptic than `&.foo(`. For the codebases that use `&.` where appropriate, an attempt to write `foo&.[bar]` is what less experienced programmers always try to do, and "why it doesn't work" is more cryptic than if it would.
Those are somewhat more confusing, too:
```ruby
# I tried to use foo&.[bar], it failed, I know [] is a method and I write this:
foo&.[](bar)
# I find the above ugly, and I switch to dig:
foo&.dig(bar)
```
...but it is kind of non-standar to use `dig` with one argument, and needs to be remembered as a separate idiom.
----------------------------------------
Feature #14548: Allow some_array&.[1] to be a valid syntax
https://bugs.ruby-lang.org/issues/14548#change-105243
* Author: rosenfeld (Rodrigo Rosenfeld Rosas)
* Status: Open
* Priority: Normal
* Assignee: matz (Yukihiro Matsumoto)
----------------------------------------
Currently, Ruby allows accessing an array's index while testing whether the array is not nil with this syntax: `my_array&.[](1)`. I've always found this awkward but didn't mind about suggesting anything to improve this.
I was just reading about how JavaScript is probably going to support myArray?.[1] and found that it read good enough for me.
So I'd like to propose about the same syntax, replacing ?. with the Ruby equivalent &. instead. How does that look like to you?
--
https://bugs.ruby-lang.org/
Issue #14548 has been updated by Eregon (Benoit Daloze).
What about using `dig` for this like `array_or_nil.dig(index)` and adding `class NilClass; def dig(*) = nil; end`?
There is also nothing wrong with `array_or_nil && array_or_nil[index]`.
IMO these `?.[` look too cryptic.
----------------------------------------
Feature #14548: Allow some_array&.[1] to be a valid syntax
https://bugs.ruby-lang.org/issues/14548#change-105242
* Author: rosenfeld (Rodrigo Rosenfeld Rosas)
* Status: Open
* Priority: Normal
* Assignee: matz (Yukihiro Matsumoto)
----------------------------------------
Currently, Ruby allows accessing an array's index while testing whether the array is not nil with this syntax: `my_array&.[](1)`. I've always found this awkward but didn't mind about suggesting anything to improve this.
I was just reading about how JavaScript is probably going to support myArray?.[1] and found that it read good enough for me.
So I'd like to propose about the same syntax, replacing ?. with the Ruby equivalent &. instead. How does that look like to you?
--
https://bugs.ruby-lang.org/
Issue #19916 has been reported by yawboakye (yaw boakye).
----------------------------------------
Bug #19916: URI#to_s can serialize to a value that doesn't deserialize to the original
https://bugs.ruby-lang.org/issues/19916
* Author: yawboakye (yaw boakye)
* Status: Open
* Priority: Normal
* ruby -v: 3.2.2
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
It appears that when we're serializing a URI to string, we don't check/confirm that it is represented in a form that can be deserialized back into the original. I think it's fair to expect that serialize+deserialize produces an object that is the same as the original, only differing perhaps in terms of the unique object identifier. This isn't the case with URI when they are custom built, which might happen a lot, for example in a Rails app that accepts URL inputs from users. Let me attempt a reproduction, using the generic URI `example.com`.
```ruby
example_url = "example.com"
example_uri = URI(example_url)
```
Given that no scheme is explicitly set in the URI, it is correctly parsed as generic, with the given `example.com` interpreted as the path.
The object returned to is mutable. Since we didn't automatically detect a scheme, let's fix that as well as the hostname.
``` ruby
example_uri.scheme = "https"
example_uri.hostname = example_uri.path
# I've intentionally left path value unchanged, since it helps demonstrate the potential bug.
```
Given that we have a scheme, an authority, and a path, and given that we format URI according to [RFC 3986], one may expect that serializing the URI to string will follow the guidelines of section 3 of the RFC: [Syntax Components], which requires a slash separator between the authority (in our case hostname) and the path. It appears that `URI#to_s` may not do that if path didn't already have a slash prefix. Which would be fine if we were keeping an invariant that ensured that we never produced bad serialized URI. To return to our `example_uri`, serialization produces:
```ruby
serialized_uri = example_uri.to_s
puts serialized_uri # https://example.comexample.com
```
This is obviously bad. One would have expected `https://example.com/example.com` instead. That is, the slash will be automatically and correctly inserted, just as the double slashes were automatically inserted between the scheme and and the authority. `serialized_uri` cannot be deserialized into `example_uri`, in fact. Below is an attempt at deserialization and a comparison of the new value to the original:
```ruby
deserialized_example_uri = URI(serialized_uri)
example_uri.scheme == deserialized_example_uri.scheme # true
example_uri.hostname == deserialized_example_uri.hostname # false (for, example.com =/= example.comexample.com)
example_uri.path == deserialized_example_uri.path # false (for, example.com =/= "")
```
I believe that the ability to serialize and deserialize an object without losing fidelity is a great thing. I believe even more strongly that we should preserve/maintain an invariant that allows us to always serialize a URI to a format that meets the RFC's specification. Therefore I consider this a bug, and I'd be willing to work on a fix, as my first contribution to Ruby, if enough people consider it a bug too.
Regards!
[RFC 3986]: https://www.rfc-editor.org/rfc/rfc3986
[Syntax Components]: https://www.rfc-editor.org/rfc/rfc3986#section-3
---Files--------------------------------
Screenshot 2023-09-29 at 12.19.26.png (180 KB)
--
https://bugs.ruby-lang.org/