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 -----
  • September
  • August
  • 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

  • 2 participants
  • 4151 discussions
[ruby-core:111122] [Ruby master Feature#19107] Allow trailing comma in method signature
by byroot (Jean Boussier) 01 Dec '22

01 Dec '22
Issue #19107 has been updated by byroot (Jean Boussier). > Is there an actual case where this proposal is convenient? Yes, when replacing old APIs that took an "option hash" by explicit keyword arguments, it tend to create very large signature. The last example I have in mind is `redis-client`: https://github.com/redis-rb/redis-client/blob/dcfe43abb83597bee129537464e20… ```ruby def initialize( username: nil, password: nil, db: nil, id: nil, timeout: DEFAULT_TIMEOUT, read_timeout: timeout, write_timeout: timeout, connect_timeout: timeout, ssl: nil, custom: {}, ssl_params: nil, driver: nil, protocol: 3, client_implementation: RedisClient, command_builder: CommandBuilder, inherit_socket: false, reconnect_attempts: false, middlewares: false, circuit_breaker: nil ) ``` When adding a new argument, it cause these annoying diffs: ```diff diff --git a/lib/redis_client/config.rb b/lib/redis_client/config.rb index fc74367..6412171 100644 --- a/lib/redis_client/config.rb +++ b/lib/redis_client/config.rb @@ -36,7 +36,8 @@ class RedisClient command_builder: CommandBuilder, inherit_socket: false, reconnect_attempts: false, - middlewares: false + middlewares: false, + circuit_breaker: nil ) @username = username @password = password ``` Also this inconsistency is the reason why some popular styleguides reverted back to not using trailing comma for multi-line enumerations: - https://github.com/testdouble/standard/pull/453#issuecomment-1234208705 - https://github.com/fables-tales/rubyfmt/issues/154 ---------------------------------------- Feature #19107: Allow trailing comma in method signature https://bugs.ruby-lang.org/issues/19107#change-100396 * Author: byroot (Jean Boussier) * Status: Open * Priority: Normal ---------------------------------------- A popular style for multiline arrays, hashes or method calls, is to use trailing commas: ```ruby array = [ 1, 2, 3, ] hash = { foo: 1, bar: 2, baz: 3, } Some.method( 1, 2, foo: 3, ) ``` The main reason to do this is to avoid unnecessary noise when adding one extra element: ```diff diff --git a/foo.rb b/foo.rb index b2689a7e4f..ddb7dc3552 100644 --- a/foo.rb +++ b/foo.rb @@ -1,4 +1,5 @@ Foo.bar( foo: 1, - bar: 2 + bar: 2, + baz: 3 ) ``` However, this pattern doesn't work with method declarations: ```ruby def foo(bar:,) # syntax error, unexpected ')' ``` ### Proposal For consistency and convenience I propose to allow trailing commas in method declarations. -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111120] [Ruby master Feature#19078] Introduce `Fiber#storage` for inheritable fiber-scoped variables.
by ioquatix (Samuel Williams) 01 Dec '22

01 Dec '22
Issue #19078 has been updated by ioquatix (Samuel Williams). Thanks so much for your time and discussion @matz et al. ---------------------------------------- Feature #19078: Introduce `Fiber#storage` for inheritable fiber-scoped variables. https://bugs.ruby-lang.org/issues/19078#change-100395 * Author: ioquatix (Samuel Williams) * Status: Open * Priority: Normal * Assignee: ioquatix (Samuel Williams) ---------------------------------------- Pull Request: https://github.com/ruby/ruby/pull/6612 This is an evolution of the previous ideas: - https://bugs.ruby-lang.org/issues/19058 - https://bugs.ruby-lang.org/issues/19062 This PR introduces fiber scoped variables, and is a solution for problems like <https://github.com/ioquatix/ioquatix/discussions/17>. The main interface is: ```ruby Fiber[key] = value Fiber[key] # => value ``` The variables are scoped (local to) a fiber and inherited into child fibers and threads. ```ruby Fiber[:request_id] = SecureRandom.hex(16) Fiber.new do p Fiber[:request_id] # prints the above request id end ``` The fiber scoped variables are stored and can be accessed: ```ruby Fiber.current.storage # => returns a Hash (copy) of the internal storage. Fiber.current.storage= # => assigns a Hash (copy) to the internal storage. ``` Fiber itself has one new keyword argument: ``` Fiber.new(..., storage: hash, false, undef, nil) ``` This can control how the fiber variables are setup in a child context. To minimise the performance overhead of some of the implementation choices, we are also simultaneously implementing <https://bugs.ruby-lang.org/issues/19077>. ## Examples ### Request loop ```ruby Thread.new do while request = queue.pop Fiber.new(storage: {id: SecureRandom.hex(16)}) do handle_request.call(request) end end end ``` OR ```ruby Thread.new do while request = queue.pop Fiber.current.storage = {id: SecureRandom.hex(16)} handle_request.call(request) end end ``` -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111119] [Ruby master Feature#19036] Provide a way to set path for File instances created with for_fd
by matz (Yukihiro Matsumoto) 01 Dec '22

01 Dec '22
Issue #19036 has been updated by matz (Yukihiro Matsumoto). LGTM. Matz. ---------------------------------------- Feature #19036: Provide a way to set path for File instances created with for_fd https://bugs.ruby-lang.org/issues/19036#change-100393 * Author: headius (Charles Nutter) * Status: Open * Priority: Normal ---------------------------------------- Ruby provides `IO.for_fd` to instantiate an IO object from an existing file descriptor value. The logic for this simply calls the base `IO.new` logic, which for all IO and subtypes simply wraps the given file descriptor. When called against File, or other subtypes of IO, this has the side effect of creating an IO instance with that type, e.g. `File.for_fd` will behave identically to `IO.for_fd` except that the class of the resulting object will be File. Unfortunately, this results in a File object that does not have any `path` associated with it: ``` 3.1.2 :001 > f = File.open('README.md') => #<File:README.md> 3.1.2 :002 > f.path => "README.md" 3.1.2 :003 > f2 = File.for_fd(f.fileno) => #<File:fd 5> 3.1.2 :004 > f2.path (irb):4:in `path': File is unnamed (TMPFILE?) (IOError) from (irb):4:in `<main>' from /home/headius/.rvm/rubies/ruby-3.1.2/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>' from /home/headius/.rvm/rubies/ruby-3.1.2/bin/irb:25:in `load' from /home/headius/.rvm/rubies/ruby-3.1.2/bin/irb:25:in `<main>' ``` I propose that there should be a way, via an extra parameter or a keyword argument, to provide a path when constructing a new File via `for_fd`. Possible forms: * `File.for_fd(fileno, "my/path")` * `File.for_fd(fileno, path: "my/path")` This would necessitate a separate implementation for `File.for_fd` unless we want to make it possible to set a path for all `for_fd` calls (which may not make sense for many of them). This came up while trying to implement a pure-Ruby (plus FFI) version of the "pty" library. Without overriding the `path` function, it is not possible for the File object returned by `PTY.open` to gain the "masterpty:<slavename>" filename, and therefore it does not clearly indicate it is from a PTY. See https://github.com/jruby/jruby/pull/7391, an attempt to match inspect output for these return values using `define_singleton_method`. Providing a way to set the path would make this automatic without the singleton definition. -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111117] [Ruby master Bug#19108] Format routines like pack blindly treat a string as ASCII-encoded
by matz (Yukihiro Matsumoto) 01 Dec '22

01 Dec '22
Issue #19108 has been updated by matz (Yukihiro Matsumoto). Template strings should be ASCII compatible, exceptions otherwise. Matz. ---------------------------------------- Bug #19108: Format routines like pack blindly treat a string as ASCII-encoded https://bugs.ruby-lang.org/issues/19108#change-100391 * Author: chrisseaton (Chris Seaton) * Status: Open * Priority: Normal * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- Format routines like pack and unpack blindly treat a string as ASCII-encoded, even if they aren't ASCII or ASCII-compatible. I tried to construct code that was misleading using ASCII-incompatible-encodings but couldn't do it in practice (no ASCII-incompatible encodings have a pack directive ASCII byte that is encoded as a printable character.) But I could demonstrate at least some strange behaviour: ``` p ['foo'].pack('u').encoding # => #<Encoding:US-ASCII> p ['foo'].pack('u'.encode('UTF-32BE')).encoding # => #<Encoding:ASCII-8BIT> ``` This is because the NUL characters in the second one (which aren't really NUL characters - they're part of the directive characters) explicitly trigger the encoding to change to binary. There is a warning, but the warning is only for unexpected directives. How about disallowing or warning for non-ascii compatible format strings? -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111116] [Ruby master Bug#19164] [3.2.0dev] Freezing an object can prevent removing methods on its class
by byroot (Jean Boussier) 01 Dec '22

01 Dec '22
Issue #19164 has been reported by byroot (Jean Boussier). ---------------------------------------- Bug #19164: [3.2.0dev] Freezing an object can prevent removing methods on its class https://bugs.ruby-lang.org/issues/19164 * Author: byroot (Jean Boussier) * Status: Open * Priority: Normal * Target version: 3.2 * ruby -v: ruby 3.2.0dev (2022-11-30T08:20:38Z master c8bfbbc25e) [arm64-darwin22] * Backport: 2.7: DONTNEED, 3.0: DONTNEED, 3.1: DONTNEED ---------------------------------------- The following snippet showcase the bug. It passes on 3.1 and older, but fails on 3.2.0 ```ruby klass = Class.new klass.prepend(Module.new) klass.new.freeze klass.class_eval do define_method(:bar) {} # works remove_method(:bar) # raise FrozenError end ``` Bisecting with this script points me to [e7b1ff984fde859a7778dec564731eb79392406f](https://github.com/ruby/ruby/comm…, which suggest some part of the codebase might be misusing some user flags. I have a patch that fix this script but I doubt it's the right fix, I'll be working with @jemmai and @tenderlovemaking to find a proper fix. I'm opening this ticket to make sure it's addressed before the 3.2.0 final release. -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111115] [Ruby master Bug#19003] TracePoint behavior inconsistency in 3.2.0-preview2
by ko1 (Koichi Sasada) 01 Dec '22

01 Dec '22
Issue #19003 has been updated by ko1 (Koichi Sasada). Sorry for late. We left this issue as an implementation details and on Ruby 3.2 the current behavior will be remain. Sorry for inconvenient for your purpose. The reason is to implement the proposed behavior strictly needs a much work and we can't do that now. In my opinion the proposal behavior is preferable, so it can be fixed in future. BTW on the debug.gem we avoid duplicated TracePoint manually. ---------------------------------------- Bug #19003: TracePoint behavior inconsistency in 3.2.0-preview2 https://bugs.ruby-lang.org/issues/19003#change-100388 * Author: hurricup (Alexandr Evstigneev) * Status: Open * Priority: Normal * ruby -v: ruby 3.2.0preview2 (2022-09-09 master 35cfc9a3bb) [x86_64-linux] * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- This is kind of continuation of my previous report about global/local TP processing (#18730). Sample script: ```rb def foo return 1 end puts RubyVM::InstructionSequence.of(method :foo).disasm def step_over TracePoint.new(:line, :return, :b_return) do |tp| puts "Step over hits by #{tp.event} at #{tp.lineno}" step_over tp.disable end.enable(target: RubyVM::InstructionSequence.of(method :foo), target_thread: Thread.current) end TracePoint.new(:line, :return, :b_return) do |tp| if tp.lineno == 2 puts "Step into hits by #{tp.event} at #{tp.lineno}" step_over tp.disable end end.enable(target_thread: Thread.current) a = foo ``` In ruby 3.1.2 we have expected behavior. Output: ``` == disasm: #<ISeq:foo@/home/hurricup/Projects/ruby-debugger/jb-debase-30/test_sample.rb:1 (1,0)-(3,3)> (catch: FALSE) 0000 putobject_INT2FIX_1_ ( 2)[LiCa] 0001 leave ( 3)[Re] Step into hits by line at 2 Step over hits by return at 3 ``` In ruby 3.2.0-preview2 - not so much. Output: ``` == disasm: #<ISeq:foo@/home/hurricup/Projects/ruby-debugger/jb-debase-30/test_sample.rb:1 (1,0)-(3,3)> (catch: false) 0000 putobject_INT2FIX_1_ ( 2)[LiCa] 0001 leave ( 3)[Re] Step into hits by line at 2 Step over hits by line at 2 Step over hits by return at 3 ``` -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111114] [Ruby master Feature#19138] `SyntaxError#path` for syntax_suggest
by matz (Yukihiro Matsumoto) 01 Dec '22

01 Dec '22
Issue #19138 has been updated by matz (Yukihiro Matsumoto). Sounds reasonable. Matz. ---------------------------------------- Feature #19138: `SyntaxError#path` for syntax_suggest https://bugs.ruby-lang.org/issues/19138#change-100386 * Author: nobu (Nobuyoshi Nakada) * Status: Open * Priority: Normal * Target version: 3.2 ---------------------------------------- Currently syntax_suggest searches the path name from the exception message. But extracting the info from messages for humans is fragile, I think. So proposing a new method `SyntaxError#path`, similar to `LoadError#path`. ```patch commit 986da132002af1cdb75c0c89ca2831fe51e6ce69 Author: Nobuyoshi Nakada <nobu(a)ruby-lang.org> AuthorDate: 2022-11-20 22:59:52 +0900 Commit: Nobuyoshi Nakada <nobu(a)ruby-lang.org> CommitDate: 2022-11-20 23:44:27 +0900 Add `SyntaxError#path` diff --git a/error.c b/error.c index 0ff4b8d6d8e..ad1bc6ee8dc 100644 --- a/error.c +++ b/error.c @@ -125,6 +125,8 @@ err_vcatf(VALUE str, const char *pre, const char *file, int line, return str; } +static VALUE syntax_error_with_path(VALUE, VALUE, VALUE*, rb_encoding*); + VALUE rb_syntax_error_append(VALUE exc, VALUE file, int line, int column, rb_encoding *enc, const char *fmt, va_list args) @@ -138,15 +140,7 @@ rb_syntax_error_append(VALUE exc, VALUE file, int line, int column, } else { VALUE mesg; - if (NIL_P(exc)) { - mesg = rb_enc_str_new(0, 0, enc); - exc = rb_class_new_instance(1, &mesg, rb_eSyntaxError); - } - else { - mesg = rb_attr_get(exc, idMesg); - if (RSTRING_LEN(mesg) > 0 && *(RSTRING_END(mesg)-1) != '\n') - rb_str_cat_cstr(mesg, "\n"); - } + exc = syntax_error_with_path(exc, file, &mesg, enc); err_vcatf(mesg, NULL, fn, line, fmt, args); } @@ -2353,6 +2347,25 @@ syntax_error_initialize(int argc, VALUE *argv, VALUE self) return rb_call_super(argc, argv); } +static VALUE +syntax_error_with_path(VALUE exc, VALUE path, VALUE *mesg, rb_encoding *enc) +{ + if (NIL_P(exc)) { + *mesg = rb_enc_str_new(0, 0, enc); + exc = rb_class_new_instance(1, mesg, rb_eSyntaxError); + rb_ivar_set(exc, id_i_path, path); + } + else { + if (rb_attr_get(exc, id_i_path) != path) { + rb_raise(rb_eArgError, "SyntaxError#path changed"); + } + VALUE s = *mesg = rb_attr_get(exc, idMesg); + if (RSTRING_LEN(s) > 0 && *(RSTRING_END(s)-1) != '\n') + rb_str_cat_cstr(s, "\n"); + } + return exc; +} + /* * Document-module: Errno * @@ -3011,9 +3024,14 @@ Init_Exception(void) rb_eSyntaxError = rb_define_class("SyntaxError", rb_eScriptError); rb_define_method(rb_eSyntaxError, "initialize", syntax_error_initialize, -1); + ID id_path = rb_intern_const("path"); + + /* the path failed to parse */ + rb_attr(rb_eSyntaxError, id_path, TRUE, FALSE, FALSE); + rb_eLoadError = rb_define_class("LoadError", rb_eScriptError); /* the path failed to load */ - rb_attr(rb_eLoadError, rb_intern_const("path"), TRUE, FALSE, FALSE); + rb_attr(rb_eLoadError, id_path, TRUE, FALSE, FALSE); rb_eNotImpError = rb_define_class("NotImplementedError", rb_eScriptError); ``` With this method, syntax_suggest/core_ext.rb will no longer need `PathnameFromMessage`. ```patch diff --git i/lib/syntax_suggest/core_ext.rb w/lib/syntax_suggest/core_ext.rb index 40f5fe13759..616a6ed9839 100644 --- i/lib/syntax_suggest/core_ext.rb +++ w/lib/syntax_suggest/core_ext.rb @@ -25,15 +25,12 @@ require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE) message = super - file = if highlight - SyntaxSuggest::PathnameFromMessage.new(super(highlight: false, **kwargs)).call.name - else - SyntaxSuggest::PathnameFromMessage.new(message).call.name - end - - io = SyntaxSuggest::MiniStringIO.new + file = path if file + file = Pathname.new(file) + io = SyntaxSuggest::MiniStringIO.new + SyntaxSuggest.call( io: io, source: file.read, ``` Since we have not released with `SyntaxError#detailed_message` yet, there should not be a compatibility issue. @schneems How do you think? -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111113] [Ruby master Feature#19107] Allow trailing comma in method signature
by matz (Yukihiro Matsumoto) 01 Dec '22

01 Dec '22
Issue #19107 has been updated by matz (Yukihiro Matsumoto). I don't care for consistency here (since formal arguments and actual arguments are different). I am not sure for convenience. Compare to actual arguments, there's less chance to rewrite/update formal arguments. Is there an actual case where this proposal is convenient? Matz. ---------------------------------------- Feature #19107: Allow trailing comma in method signature https://bugs.ruby-lang.org/issues/19107#change-100385 * Author: byroot (Jean Boussier) * Status: Open * Priority: Normal ---------------------------------------- A popular style for multiline arrays, hashes or method calls, is to use trailing commas: ```ruby array = [ 1, 2, 3, ] hash = { foo: 1, bar: 2, baz: 3, } Some.method( 1, 2, foo: 3, ) ``` The main reason to do this is to avoid unnecessary noise when adding one extra element: ```diff diff --git a/foo.rb b/foo.rb index b2689a7e4f..ddb7dc3552 100644 --- a/foo.rb +++ b/foo.rb @@ -1,4 +1,5 @@ Foo.bar( foo: 1, - bar: 2 + bar: 2, + baz: 3 ) ``` However, this pattern doesn't work with method declarations: ```ruby def foo(bar:,) # syntax error, unexpected ')' ``` ### Proposal For consistency and convenience I propose to allow trailing commas in method declarations. -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111112] [Ruby master Feature#18951] Object#with to set and restore attributes around a block
by byroot (Jean Boussier) 01 Dec '22

01 Dec '22
Issue #18951 has been updated by byroot (Jean Boussier). Thank you Matz. If it's not desired in ruby-core, I can add it to Active Support, that's no problem. ---------------------------------------- Feature #18951: Object#with to set and restore attributes around a block https://bugs.ruby-lang.org/issues/18951#change-100384 * Author: byroot (Jean Boussier) * Status: Open * Priority: Normal ---------------------------------------- ### Use case A very common pattern in Ruby, especially in testing is to save the value of an attribute, set a new value, and then restore the old value in an `ensure` clause. e.g. in unit tests ```ruby def test_something_when_enabled enabled_was, SomeLibrary.enabled = SomeLibrary.enabled, true # test things ensure SomeLibrary.enabled = enabled_was end ``` Or sometime in actual APIs: ```ruby def with_something_enabled enabled_was = @enabled @enabled = true yield ensure @enabled = enabled_was end ``` There is no inherent problem with this pattern, but it can be easy to make a mistake, for instance the unit test example: ```ruby def test_something_when_enabled some_call_that_may_raise enabled_was, SomeLibrary.enabled = SomeLibrary.enabled, true # test things ensure SomeLibrary.enabled = enabled_was end ``` In the above if `some_call_that_may_raise` actually raises, `SomeLibrary.enabled` is set back to `nil` rather than its original value. I've seen this mistake quite frequently. ### Proposal I think it would be very useful to have a method on Object to implement this pattern in a correct and easy to use way. The naive Ruby implementation would be: ```ruby class Object def with(**attributes) old_values = {} attributes.each_key do |key| old_values[key] = public_send(key) end begin attributes.each do |key, value| public_send("#{key}=", value) end yield ensure old_values.each do |key, old_value| public_send("#{key}=", old_value) end end end end ``` NB: `public_send` is used because I don't think such method should be usable if the accessors are private. With usage: ```ruby def test_something_when_enabled SomeLibrary.with(enabled: true) do # test things end end ``` ```ruby GC.with(measure_total_time: true, auto_compact: false) do # do something end ``` ### Alternate names and signatures If `#with` isn't good, I can also think of: - `Object#set` - `Object#apply` But the `with_` prefix is by far the most used one when implementing methods that follow this pattern. Also if accepting a Hash is dimmed too much, alternative signatures could be: - `Object#set(attr_name, value)` - `Object#set(attr1, value1, [attr2, value2], ...)` # Some real world code example that could be simplified with method - `redis-client` `with_timeout` https://github.com/redis-rb/redis-client/blob/23a5c1e2ff688518904f206df8d4a… - Lots of tests in Rails's codebase: - Changing `Thread.report_on_exception`: https://github.com/rails/rails/blob/2d2fdc941e7497ca77f99ce5ad404b6e58f043e… - Changing a class attribute: https://github.com/rails/rails/blob/2d2fdc941e7497ca77f99ce5ad404b6e58f043e… -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111111] [Ruby master Feature#18951] Object#with to set and restore attributes around a block
by matz (Yukihiro Matsumoto) 01 Dec '22

01 Dec '22
Issue #18951 has been updated by matz (Yukihiro Matsumoto). * `with_attr` is better than plain `with` * this method can be useful for some cases, but I am not sure if it should be a method of Object class * maybe it should be a utility method in a gem (e.g. `save_current_attr(obj, **kw) {....}`) Matz. ---------------------------------------- Feature #18951: Object#with to set and restore attributes around a block https://bugs.ruby-lang.org/issues/18951#change-100383 * Author: byroot (Jean Boussier) * Status: Open * Priority: Normal ---------------------------------------- ### Use case A very common pattern in Ruby, especially in testing is to save the value of an attribute, set a new value, and then restore the old value in an `ensure` clause. e.g. in unit tests ```ruby def test_something_when_enabled enabled_was, SomeLibrary.enabled = SomeLibrary.enabled, true # test things ensure SomeLibrary.enabled = enabled_was end ``` Or sometime in actual APIs: ```ruby def with_something_enabled enabled_was = @enabled @enabled = true yield ensure @enabled = enabled_was end ``` There is no inherent problem with this pattern, but it can be easy to make a mistake, for instance the unit test example: ```ruby def test_something_when_enabled some_call_that_may_raise enabled_was, SomeLibrary.enabled = SomeLibrary.enabled, true # test things ensure SomeLibrary.enabled = enabled_was end ``` In the above if `some_call_that_may_raise` actually raises, `SomeLibrary.enabled` is set back to `nil` rather than its original value. I've seen this mistake quite frequently. ### Proposal I think it would be very useful to have a method on Object to implement this pattern in a correct and easy to use way. The naive Ruby implementation would be: ```ruby class Object def with(**attributes) old_values = {} attributes.each_key do |key| old_values[key] = public_send(key) end begin attributes.each do |key, value| public_send("#{key}=", value) end yield ensure old_values.each do |key, old_value| public_send("#{key}=", old_value) end end end end ``` NB: `public_send` is used because I don't think such method should be usable if the accessors are private. With usage: ```ruby def test_something_when_enabled SomeLibrary.with(enabled: true) do # test things end end ``` ```ruby GC.with(measure_total_time: true, auto_compact: false) do # do something end ``` ### Alternate names and signatures If `#with` isn't good, I can also think of: - `Object#set` - `Object#apply` But the `with_` prefix is by far the most used one when implementing methods that follow this pattern. Also if accepting a Hash is dimmed too much, alternative signatures could be: - `Object#set(attr_name, value)` - `Object#set(attr1, value1, [attr2, value2], ...)` # Some real world code example that could be simplified with method - `redis-client` `with_timeout` https://github.com/redis-rb/redis-client/blob/23a5c1e2ff688518904f206df8d4a… - Lots of tests in Rails's codebase: - Changing `Thread.report_on_exception`: https://github.com/rails/rails/blob/2d2fdc941e7497ca77f99ce5ad404b6e58f043e… - Changing a class attribute: https://github.com/rails/rails/blob/2d2fdc941e7497ca77f99ce5ad404b6e58f043e… -- https://bugs.ruby-lang.org/
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • ...
  • 416
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.