From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS24940 94.130.0.0/16 X-Spam-Status: No, score=-3.7 required=3.0 tests=AWL,BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, SPF_HELO_PASS,SPF_PASS,URIBL_GREY shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 Received: from nue.mailmanlists.eu (nue.mailmanlists.eu [94.130.110.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPS id CF8A01F428 for ; Fri, 17 Mar 2023 01:15:23 +0000 (UTC) Authentication-Results: dcvr.yhbt.net; dkim=pass (1024-bit key; secure) header.d=ml.ruby-lang.org header.i=@ml.ruby-lang.org header.a=rsa-sha256 header.s=mail header.b=is+2p7/s; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=sendgrid.net header.i=@sendgrid.net header.a=rsa-sha256 header.s=smtpapi header.b=wdqt0iay; dkim-atps=neutral Received: from nue.mailmanlists.eu (localhost [127.0.0.1]) by nue.mailmanlists.eu (Postfix) with ESMTP id CF3837F31F; Fri, 17 Mar 2023 01:15:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ml.ruby-lang.org; s=mail; t=1679015713; bh=jaAzEF4fzJuxhmBmUVCGRpQvpvkoUN8qa17tVwTbcpc=; h=Date:References:To:Reply-To:Subject:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Cc:From; b=is+2p7/se7Z8gLFUo+/tsZStVgrqBaMConb+veynyrsJUl3IXCLmVmpNVkACzu8KI P1aAy8IiIWhurWtZDD9nYt1sdtRuWq2tMFF3zFl9nzBdNoeUoZWFBCYaeBf2wvtCl2 CZpbvVrwhkGeFFfXt2JzbmduICUlYCRpvjj1ptgk= Received: from xtrwkhkc.outbound-mail.sendgrid.net (xtrwkhkc.outbound-mail.sendgrid.net [167.89.16.28]) by nue.mailmanlists.eu (Postfix) with ESMTPS id 30A507F310 for ; Fri, 17 Mar 2023 01:15:11 +0000 (UTC) Authentication-Results: nue.mailmanlists.eu; dkim=pass (1024-bit key; unprotected) header.d=sendgrid.net header.i=@sendgrid.net header.a=rsa-sha256 header.s=smtpapi header.b=wdqt0iay; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sendgrid.net; h=from:references:subject:mime-version:content-type: content-transfer-encoding:list-id:to:cc:content-type:from:subject:to; s=smtpapi; bh=Zy0RwGIsuUtGgD0fjQ5FKt/dgOvwqBD3YNu9Bj4wv0Y=; b=wdqt0iaykTOtbfD2KyI/O0Ehv1iFgqRxAKYN4eYRvKAVzY8J4GoYey0n4AHCxmonnm2+ XK3YV2M8/MaH78lETVv2kfaqJcy4p/eA7DW44shXQPvVRgeza/W9s12VjpbFgPmB+E0LRJ o6rKVif5/lr3Kwj7kpmrh+vauRanlBFuA= Received: by filterdrecv-7457b4c9b5-h2p4l with SMTP id filterdrecv-7457b4c9b5-h2p4l-1-6413BF1D-28 2023-03-17 01:15:09.36650736 +0000 UTC m=+1993356.383255173 Received: from herokuapp.com (unknown) by geopod-ismtpd-33 (SG) with ESMTP id tplAbd0JQF-saWKwZHVZjg for ; Fri, 17 Mar 2023 01:15:09.258 +0000 (UTC) Date: Fri, 17 Mar 2023 01:15:09 +0000 (UTC) Message-ID: References: Mime-Version: 1.0 X-Redmine-Project: ruby-master X-Redmine-Issue-Tracker: Feature X-Redmine-Issue-Id: 18951 X-Redmine-Issue-Author: byroot X-Redmine-Sender: dsisnero X-Mailer: Redmine X-Redmine-Host: bugs.ruby-lang.org X-Redmine-Site: Ruby Issue Tracking System X-Auto-Response-Suppress: All Auto-Submitted: auto-generated X-Redmine-MailingListIntegration-Message-Ids: 89333 X-SG-EID: =?us-ascii?Q?YTcC4pXzR8TlXBEvbkWPFxrbHrGC47VouOLAPTnELdlCiO092nq=2FcUCzsaE2wq?= =?us-ascii?Q?udveHhu7FM4fsiYxQouYOUXeNNrSh+uOMoilj3M?= =?us-ascii?Q?AlhhwIkvUrLFJSoahEjUYn6BrWJFEUfgAmLu84d?= =?us-ascii?Q?ZIaWXT1vAbXOKQ1KY1GXvyFkisyT1YvZ68kZgXl?= =?us-ascii?Q?xRU4ChtHZR86HMmHQOxwdxB2XSqwDb5vR4Mgaom?= =?us-ascii?Q?HEKwAt9r2r2qQRdnNDrYC1YhF9hh2sReJlDhuAa?= =?us-ascii?Q?npsRZi=2FYXpH8V2tcCnuLTAUQXo7tnLocegqPJMV?= =?us-ascii?Q?wH0=3D?= To: ruby-core@ml.ruby-lang.org X-Entity-ID: b/2+PoftWZ6GuOu3b0IycA== Message-ID-Hash: 5UB5JBWZLQNQ7OQFZQBA26YCGALQQWFG X-Message-ID-Hash: 5UB5JBWZLQNQ7OQFZQBA26YCGALQQWFG X-MailFrom: bounces+313651-b711-ruby-core=ml.ruby-lang.org@sendgrid.net X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.3 Precedence: list Reply-To: Ruby developers Subject: [ruby-core:112920] [Ruby master Feature#18951] Object#with to set and restore attributes around a block List-Id: Ruby developers Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: "dsisnero (Dominic Sisneros) via ruby-core" Cc: "dsisnero (Dominic Sisneros)" Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Issue #18951 has been updated by dsisnero (Dominic Sisneros). temp_set might be a better method name ---------------------------------------- Feature #18951: Object#with to set and restore attributes around a block https://bugs.ruby-lang.org/issues/18951#change-102440 * Author: byroot (Jean Boussier) * Status: Rejected * 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/23a5c1e2ff688518904f206df8d4a8734275292d/lib/redis_client/ruby_connection/buffered_io.rb#L35-L53 - Lots of tests in Rails's codebase: - Changing `Thread.report_on_exception`: https://github.com/rails/rails/blob/2d2fdc941e7497ca77f99ce5ad404b6e58f043ef/activerecord/test/cases/connection_pool_test.rb#L583-L595 - Changing a class attribute: https://github.com/rails/rails/blob/2d2fdc941e7497ca77f99ce5ad404b6e58f043ef/activerecord/test/cases/associations/belongs_to_associations_test.rb#L136-L150 -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/