From: "ujihisa (Tatsuhiro Ujihisa)" Date: 2022-11-03T19:21:47+00:00 Subject: [ruby-core:110592] [Ruby master Feature#19099] Support `private_constant` for an undefined constant Issue #19099 has been updated by ujihisa (Tatsuhiro Ujihisa). I tried both the `private_const_set` and the idiom with a local variable versions locally, and found that the `private_const_set` worked better. The fact that the whole statement begins with "private" really helped. Didn't like the idea to change the internal mode by calling `private_constant` by itself, because it consumes more human memory, like `private` by itself. The below is my draft to introduce `private_const_set`. If this is the right direction I can send a pull request on GitHub as well, including `public_const_set` implementation. ```patch diff --git prelude.rb prelude.rb index 8fd6e6cb772..d17908758a5 100644 --- prelude.rb +++ prelude.rb @@ -29,3 +29,11 @@ def to_set(klass = Set, *args, &block) klass.new(self, *args, &block) end end + +class Module + def private_const_set(sym_or_str, obj) + const_set(sym_or_str, obj) + private_constant(sym_or_str) + sym_or_str + end +end diff --git spec/ruby/core/module/private_const_set_spec.rb spec/ruby/core/module/private_const_set_spec.rb new file mode 100644 index 00000000000..6c8b157e374 --- /dev/null +++ spec/ruby/core/module/private_const_set_spec.rb @@ -0,0 +1,26 @@ + +require_relative '../../spec_helper' +require_relative '../../fixtures/constants' + +describe "Module#private_const_set" do + it "sets a private constant specified by a String or Symbol to the given value" do + ConstantSpecs.private_const_set(:CS_PCONST401, :const401) + -> { + ConstantSpecs::CS_PCONST401 + }.should raise_error(NameError) + ConstantSpecs.public_constant(:CS_PCONST401) + ConstantSpecs::CS_PCONST401.should == :const401 + + ConstantSpecs.private_const_set('CS_PCONST402', :const402) + -> { + ConstantSpecs::CS_PCONST402 + }.should raise_error(NameError) + ConstantSpecs.public_constant(:CS_PCONST402) + ConstantSpecs::CS_PCONST402.should == :const402 + end + + it "returns the value set" do + ConstantSpecs.private_const_set(:CS_PCONST403, :const403).should == :CS_PCONST403 + ConstantSpecs.private_const_set('CS_PCONST404', :const404).should == 'CS_PCONST404' + end +end ``` ---------------------------------------- Feature #19099: Support `private_constant` for an undefined constant https://bugs.ruby-lang.org/issues/19099#change-99924 * Author: ujihisa (Tatsuhiro Ujihisa) * Status: Open * Priority: Normal ---------------------------------------- All the following discussion applies to `public_constant` too. Maybe `deprecate_constant` as well. ## Problem ```ruby class C X = ... private_constant :X end ``` The above idiom usually works fine, but when `...` part is long, like a 30-line Ruby Hash, it's very easy to miss the following `private_constant :X` part. ## Impossible solution ```ruby class C private_constant X = ... end ``` Like `private`, if the above notation could work, it would be awesome, but it breaks so many backward compatibility. The constant assignment returns its value but not the name of the constant, and we should keep the current behaviour. ## Proposed solution Allow the following new notation for `private_constant` by making constant private by name without actually resolving itself and raises an error. ``` ruby class C private_constant :X X = ... end ``` The current behaviour is to raise NameError. ``` /tmp/v8svpb4/95:2:in `private_constant': constant C::X1 not defined (NameError) private_constant :X1 ^^^^^^^^^^^^^^^^ from /tmp/v8svpb4/95:2:in `' from /tmp/v8svpb4/95:1:in `
' ``` This proposal breaks this backward compatibility. Also I'm concerned about potential typos. It may be hard to find typos. ```ruby class C private_constant :BEHAVIOUR BEHAVIOR = 123 # Remains public unintentionally end ``` Maybe we need some sort of foolproof somewhere in this way. -- https://bugs.ruby-lang.org/ Unsubscribe: