ml.ruby-lang.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

ruby-core

Thread Start a new thread
Download
Threads by month
  • ----- 2026 -----
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2025 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2022 -----
  • December
  • November
ruby-core@ml.ruby-lang.org

July 2026

  • 3 participants
  • 117 discussions
[ruby-core:125924] [Ruby Bug#22176] [Feature] TracePoint#defined_box: get the Ruby::Box where a method is defined
by udzura (Uchio KONDO) 04 Jul '26

04 Jul '26
Issue #22176 has been reported by udzura (Uchio KONDO). ---------------------------------------- Bug #22176: [Feature] TracePoint#defined_box: get the Ruby::Box where a method is defined https://bugs.ruby-lang.org/issues/22176 * Author: udzura (Uchio KONDO) * Status: Open * ruby -v: ruby 4.1.0dev (2026-07-04T01:14:12Z master 870c8d6a50) +PRISM [arm64-darwin25] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- ## Summary In `TracePoint` events such as `:call` / `:c_call` / `:return` / `:c_return`, I'd like to propose adding an attribute `TracePoint#defined_box`, which returns the `Ruby::Box` in which the called method was defined. Currently, `tp.defined_class` correctly returns a distinct class/module object per Box, but there is no direct way to know which Box that object belongs to. ## Background / Motivation With `Ruby::Box`, the same class name and the same source file can be loaded independently in multiple Boxes. In this situation, tools that observe and record method calls via TracePoint (profilers, security auditing tools, test support tools, etc.) run into a practical problem: they cannot distinguish "which Box the called method was defined in." ### Reproduction sample1.rb: ```ruby b = Ruby::Box.new b.require_relative 'sample2' require_relative 'sample2' tp = TracePoint.new(:call) do |tp| if tp.method_id == :method1 puts "Calling #{tp.defined_class}##{tp.method_id} from #{tp.path}:#{tp.lineno}" end end tp.enable ins1 = Sample2.new ins1.method1 ins2 = b::Sample2.new ins2.method1 ``` sample2.rb: ```ruby class Sample2 def method1 puts "This is method 1" end end ``` Output: ``` $ RUBY_BOX=1 ./miniruby ./sample1.rb ./miniruby: warning: Ruby::Box is experimental, and the behavior may change in the future! See https://docs.ruby-lang.org/en/master/Ruby/Box.html for known issues, etc. Calling Sample2#method1 from /path/to/sample2.rb:2 This is method 1 Calling Sample2#method1 from /path/to/sample2.rb:2 This is method 1 ``` There is no way to distinguish the two calls using `tp.defined_class`, `tp.method_id`, `tp.path`, or `tp.lineno`. I have confirmed that the class returned by `tp.defined_class` does in fact have a different object id in each case, but there is no way to check which Box it is associated with. ### Known workarounds and their issues #### 1. `tp.binding.eval "Ruby::Box.current.inspect"` It's possible to obtain the Box indirectly by evaluating `Ruby::Box.current` through `tp.binding`. However, `:c_call` / `:c_return` (calls into methods implemented in C) have no corresponding Ruby-level binding, so `tp.binding` itself is unavailable, and this workaround cannot be used there. #### 2. `tp.self.method(tp.method_id).box.inspect` Re-obtaining an `Object#method` reference does work, and this also works for C methods. However, it has the downside of re-constructing a `Method` object inside the TracePoint block every time, which adds overhead, and it isn't very intuitive. ## Proposed API Add `TracePoint#defined_box`, paired with `tp.defined_class`, returning the `Ruby::Box` instance in which the method/class was defined (or `nil` when `RUBY_BOX` is disabled). ```ruby tp = TracePoint.new(:call, :c_call) do |tp| puts "#{tp.defined_class}##{tp.method_id} defined in #{tp.defined_box.inspect}" end ``` ### On naming | Candidate | Verdict | Reason | |---|---|---| | `defined_box` | Proposed | Symmetric with `defined_class` | | `box` | Under consideration | Matches `Method#box`, but less specific | ## Possible implementation I'm considering two directions and would appreciate feedback: - Go through `rb_trace_arg_t *` and use `rb_callable_method_entry_without_refinements()` to look up the method definition from the klass and called_id, then read the box off of it. This may add some overhead. - Add an argument carrying the defining-Box information to `rb_exec_event_hook_orig()`. Since this function is used very broadly, this would come with a higher cost of change. ## Open questions 1. For singleton methods, `defined_class` returns the singleton class per spec. Should `defined_box` likewise return the Box that singleton class belongs to? 2. Is similar information also needed for the `:script_compiled` event, etc.? ## Environment ``` $ ./miniruby --version ruby 4.1.0dev (2026-07-04T01:14:12Z master 870c8d6a50) +PRISM [arm64-darwin25] ``` ## Use cases ### Security auditing / debugging When multiple Boxes coexist and an unexpected side effect occurs, there is likely demand for being able to trace "which Box the code that actually ran was defined in." In [vivarium](https://github.com/udzura/vivarium), an auditing tool I'm developing, I'm also considering per-Box auditing, but currently the only options are going through `tp.binding` or re-fetching `Method#box` each time, both of which feel redundant. ### Verifying dependency updates One of the use cases for Box is running an old and a new version of a dependency in parallel, each in its own Box, and comparing the resulting responses. For this, a mechanism to visualize which Box's code path was actually executed is needed. ### Application to multi-tenant systems, etc. TracePoint would make it possible to tally how much code from which Box (i.e., which plugin or tenant) was invoked. For example, an APM/profiling tool could use this to visualize per-Box usage, which may be a real demand. Since this seems like a generally useful attribute to trace with respect to Box in the first place, I expect more ideas for use cases to surface as well. ## Related - [Feature #21311] Namespace on read (revised) -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:125922] [Ruby Bug#18995] IO#set_encoding sometimes set an IO's internal encoding to the default external encoding
by javanthropus (Jeremy Bopp) 04 Jul '26

04 Jul '26
Issue #18995 has been updated by javanthropus (Jeremy Bopp). @nobu, may I ask you to take a look at this? ---------------------------------------- Bug #18995: IO#set_encoding sometimes set an IO's internal encoding to the default external encoding https://bugs.ruby-lang.org/issues/18995#change-117891 * Author: javanthropus (Jeremy Bopp) * Status: Open * ruby -v: ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux] * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- This script demonstrates the behavior: ```ruby def show(io) printf( "external encoding: %-25p internal encoding: %-25p\n", io.external_encoding, io.internal_encoding ) end Encoding.default_external = 'iso-8859-1' Encoding.default_internal = 'iso-8859-2' File.open('/dev/null') do |f| f.set_encoding('utf-8', nil) show(f) # f.internal_encoding is iso-8859-2, as expected f.set_encoding('utf-8', 'invalid') show(f) # f.internal_encoding is now iso-8859-1! Encoding.default_external = 'iso-8859-3' Encoding.default_internal = 'iso-8859-4' show(f) # f.internal_encoding is now iso-8859-3! end ``` In the 1st case, we see that the IO's internal encoding is set to the current setting of Encoding.default_internal. In the 2nd case, the IO's internal encoding is set to Encoding.default_external instead. The 3rd case is more interesting because it shows that the IO's internal encoding is actually following the current setting of Encoding.default_external. It didn't just copy it when #set_encoding was called. It changes whenever Encoding.default_external changes. What should the correct behavior be? -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:125919] [Ruby Bug#22174] Set operations (&, ^, collect!, flatten, classify, divide) do not preserve compare_by_identity
by gil.desmarais (Gil Desmarais) 03 Jul '26

03 Jul '26
Issue #22174 has been reported by gil.desmarais (Gil Desmarais). ---------------------------------------- Bug #22174: Set operations (&, ^, collect!, flatten, classify, divide) do not preserve compare_by_identity https://bugs.ruby-lang.org/issues/22174 * Author: gil.desmarais (Gil Desmarais) * Status: Open * ruby -v: ruby 4.0.5 (2026-05-20 revision 64336ffd0e) +PRISM [arm64-darwin25] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- Since `Set` was transitioned to a C-level core class in Ruby 4.0, several operations that allocate new sets or partition subsets (`&`, `^`, `collect!`, `flatten`, `classify`, and `divide`) lost the `compare_by_identity` behavior for the receiver set or its subsets. * For `&`, `collect!`, `flatten`, `classify`, and `divide`, the resulting sets/subsets are allocated without propagating the `compare_by_identity` flag (returning `false` for `#compare_by_identity?`). * For `^` (XOR) with a generic `Enumerable`, the returned set has the flag set (via `dup`), but it allocates a temporary set `tmp` to hold the RHS elements without propagating the `compare_by_identity` flag. This causes the RHS elements to be deduplicated incorrectly using value equality instead of identity equality. A pull request has been opened with the fix and tests: https://github.com/ruby/ruby/pull/17633 ### Reproduction ```ruby require 'set' # 1. Intersection & s1 = Set.new.compare_by_identity s2 = Set.new s1 << "a" s2 << "a" puts "Intersection preserves compare_by_identity: #{(s1 & s2).compare_by_identity?}" # Expected: true # Actual: false # 2. XOR ^ (with duplicate objects by value on RHS) s_xor = Set.new.compare_by_identity x1 = +"x" x2 = +"x" result = s_xor ^ [x1, x2] puts "XOR with Enumerable preserves identity comparison: #{result.size == 2}" # Expected: true (size should be 2, because x1 and x2 are distinct objects) # Actual: false (size is 1) # 3. collect! / map! s_collect = Set.new(["a", "b"]).compare_by_identity s_collect.collect! { |x| x } puts "collect! preserves compare_by_identity: #{s_collect.compare_by_identity?}" # Expected: true # Actual: false # 4. flatten s_flat = Set.new([Set.new([1])]).compare_by_identity puts "flatten preserves compare_by_identity: #{s_flat.flatten.compare_by_identity?}" # Expected: true # Actual: false # 5. classify s_classify = Set.new(["a", "b"]).compare_by_identity classified = s_classify.classify { |x| x } puts "classify subsets preserve compare_by_identity: #{classified.values.all?(&:compare_by_identity?)}" # Expected: true # Actual: false # 6. divide s_divide = Set.new(["a", "b"]).compare_by_identity divided = s_divide.divide { |x| x } puts "divide subsets preserve compare_by_identity: #{divided.all?(&:compare_by_identity?)}" # Expected: true # Actual: false -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:125855] [Ruby Feature#22132] Scala-like for comprehensions
by shugo (Shugo Maeda) 03 Jul '26

03 Jul '26
Issue #22132 has been reported by shugo (Shugo Maeda). ---------------------------------------- Feature #22132: Scala-like for comprehensions https://bugs.ruby-lang.org/issues/22132 * Author: shugo (Shugo Maeda) * Status: Open ---------------------------------------- ## Abstract How about adding an expression form of `for` that desugars into nested `flat_map`/`map` and `filter` calls. Here's an example, which computes all pairs of numbers between `0` and `n-1` whose sum is equal to a given value `v`: ```ruby def foo(n, v) for i in 0...n, j in 0...n when i + j == v then [i, j] end end p foo(10, 10) #=> [[1, 9], [2, 8], [3, 7]...] ``` The above code is desugared as follows: ```ruby def foo(n, v) (0...n).flat_map { |i| (0...n).filter { |j| i + j == v }.map { |j| [i, j] } } end p foo(10, 10) ``` ## Background and Motivation Some other languages have syntactic sugar that flattens nested code. For example, Scala has for comprehensions: ```scala def foo(n: Int, v: Int) = for i <- 0 until n j <- 0 until n if i + j == v yield (i, j) ``` Haskell has do notation: ```haskell foo :: Int -> Int -> [(Int, Int)] foo n v = do i <- [0 .. n-1] j <- [0 .. n-1] guard (i + j == v) pure (i, j) ``` Blocks are often nested deeply in Ruby, so such syntactic sugar is useful. ## Use cases For comprehensions can be used to flatten nested blocks. For example, ```ruby (1..).lazy.flat_map { |z| (1..z).lazy.flat_map { |x| (x..z).lazy.filter { |y| x**2 + y**2 == z**2 }.map { |y| [x, y, z] } } }.take(3).force ``` can be flattened as follows: ```ruby for z in (1..).lazy, x in (1..z).lazy, y in (x..z).lazy when x**2 + y**2 == z**2 then [x, y, z] end.take(3).force ``` For comprehensions can be used not only for Enumerable objects, but also for other objects which have `flat_map` and `map` that satisfy [the Monad laws](https://wiki.haskell.org/index.php?title=Monad_laws). ## Why `then` and `when`? Scala's `yield` conflicts with the existing `yield` keyword in Ruby, so I chose `then`. While a bare `for ... then` (no guard) reads a little unnaturally in English, Ruby already gives `then` a value-producing meaning (e.g., `Kernel#then`), so I consider it acceptable. The guard keyword is not `if` but `when`, because `if` after the source would be ambiguous with the modifier `if`. ## Limitations The right operand of `in` is `arg_value`, not `expr_value`, to avoid conflicts, so unparenthesized method calls (command calls) must be parenthesized. ## Backward compatibility * `for x in xs do ... end` (and the newline form) is unchanged: it still iterates via `each` and returns the collection. * All of the new forms (`for ... then`, `for ... ,`, `for ... when`) were `SyntaxError` before, so no existing program changes meaning. ## Implementation PoC: https://github.com/ruby/ruby/pull/17500 It's currently implemented only in `parse.y`, not in Prism yet, so requires `--parser=parse.y`. ## Open questions * Variable scope: the loop variables currently leak, same as `for ... do`. Should a comprehension instead scope them like a block? -- https://bugs.ruby-lang.org/
4 9
0 0
[ruby-core:125830] [Ruby Feature#22128] C API: Expose RB_OBJ_SET_FROZEN_SHAREABLE
by byroot (Jean Boussier) 02 Jul '26

02 Jul '26
Issue #22128 has been reported by byroot (Jean Boussier). ---------------------------------------- Feature #22128: C API: Expose RB_OBJ_SET_FROZEN_SHAREABLE https://bugs.ruby-lang.org/issues/22128 * Author: byroot (Jean Boussier) * Status: Open ---------------------------------------- ### Context I'm trying to experiment with adapting Active Record for a Ractor architecture. Since database connections can't possibly be Ractor shareable, the idea is to warp each connection inside its own ractor, and then send SQL queries and responses through a port. But for this to perform well, I'd like to directly build the query response as a fully shareable object, so that it can be pushed into the port for free, instead of having Ruby need to recursively walk the potentially large response to mark objects as shareable. Here's an example of how it would work in trilogy: https://github.com/byroot/trilogy/commit/5f58200b398995d2fc0d4673a5a6bbccd0… ### Problem Unfortunately, the necessary API isn't currently exposed in the C API: - `RB_OBJ_SET_FROZEN_SHAREABLE` - `RB_OBJ_SET_SHAREABLE` / `rb_obj_set_shareable` I understand that this API could potentially be misused, but given it's a C API, I believe it's acceptable to require care from the caller. -- https://bugs.ruby-lang.org/
3 4
0 0
[ruby-core:125071] [Ruby Feature#21957] Introduce `Enumerable#close` to free internal resources.
by ioquatix (Samuel Williams) 02 Jul '26

02 Jul '26
Issue #21957 has been reported by ioquatix (Samuel Williams). ---------------------------------------- Feature #21957: Introduce `Enumerable#close` to free internal resources. https://bugs.ruby-lang.org/issues/21957 * Author: ioquatix (Samuel Williams) * Status: Open * Assignee: ioquatix (Samuel Williams) ---------------------------------------- In some cases, `Enumerable` has substantial internal state (e.g. Fiber) related to enumeration. There is currently no way to clear up this state besides garbage collection, which means that we can accumulate considerable garbage before cleaning up, even if we know when the enumerable is no longer needed. I'd like to introduce `Enumerable#close` which invalidates the enumerable, freeing internal resources. After which, most usage would result in `Enumerable::ClosedError`. -- https://bugs.ruby-lang.org/
2 3
0 0
[ruby-core:115879] [Ruby master Bug#20081] Transfered Fiber doesn't return to Fiber that started it
by rmosolgo (Robert Mosolgo) 02 Jul '26

02 Jul '26
Issue #20081 has been reported by rmosolgo (Robert Mosolgo). ---------------------------------------- Bug #20081: Transfered Fiber doesn't return to Fiber that started it https://bugs.ruby-lang.org/issues/20081 * Author: rmosolgo (Robert Mosolgo) * Status: Open * Priority: Normal * ruby -v: ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-darwin22] * Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- Hi! I'm trying to figure out how to make sure that Fibers started with `.transfer` end up _terminated_, not just suspended. (If they're suspended, Rails thinks they're still alive, and they continue to hold onto database connections, see: https://github.com/rmosolgo/graphql-ruby/issues/4739#issuecomment-1866930914.) So, I'm looking for way to make sure that any Fiber I start with `.transfer` will be properly terminated. But what I noticed is that when a transfer-based Fiber terminates, it gives control back to the top-most Fiber, not the Fiber which transfered to it. Is this intended? Here's a script to replicate the issue: ```ruby manager = Fiber.new do parent = Fiber.current worker = Fiber.new do puts "2. Begin Worker" parent.transfer puts "4. End Worker" end puts "1. Transfer 1" worker.transfer puts "3. Transfer 2" worker.transfer puts "5. Finished manager" end manager.transfer puts "6. Finished script" ``` I expect the steps to print in order: ``` 1. Transfer 1 2. Begin Worker 3. Transfer 2 4. End Worker 5. Finished manager 6. Finished script ``` But instead, `5. ...` is skipped: ``` 1. Transfer 1 2. Begin Worker 3. Transfer 2 4. End Worker 6. Finished script ``` I think that's because my `worker` fiber terminates and passes control back to the top-level Fiber. Should it have passed control back to the `manager`? Or is there another way to make sure `worker` is terminated, and `manager` gets control? -- https://bugs.ruby-lang.org/
3 9
0 0
  • ← Newer
  • 1
  • ...
  • 9
  • 10
  • 11
  • 12
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.