зеркало из https://github.com/github/ruby.git
Allow setting the name of a class or module. (#7483)
Introduce `Module#set_temporary_name` for setting identifiers for otherwise anonymous modules/classes.
This commit is contained in:
Родитель
e25403d0d9
Коммит
a87bce86bb
5
NEWS.md
5
NEWS.md
|
@ -45,6 +45,10 @@ Note: We're only listing outstanding class updates.
|
|||
The class use equality semantic to lookup keys like a regular hash,
|
||||
but it doesn't hold strong references on the keys. [[Feature #18498]]
|
||||
|
||||
* Module
|
||||
|
||||
* `Module#set_temporary_name` added for setting a temporary name for a module. [[Feature #19521]]
|
||||
|
||||
## Stdlib updates
|
||||
|
||||
The following default gems are updated.
|
||||
|
@ -134,3 +138,4 @@ changelog for details of the default gems or bundled gems.
|
|||
[Feature #19347]: https://bugs.ruby-lang.org/issues/19347
|
||||
[Feature #19538]: https://bugs.ruby-lang.org/issues/19538
|
||||
[Feature #19591]: https://bugs.ruby-lang.org/issues/19591
|
||||
[Feature #19521]: https://bugs.ruby-lang.org/issues/19521
|
||||
|
|
|
@ -36,6 +36,22 @@ static inline bool ROBJ_TRANSIENT_P(VALUE obj);
|
|||
static inline void ROBJ_TRANSIENT_SET(VALUE obj);
|
||||
static inline void ROBJ_TRANSIENT_UNSET(VALUE obj);
|
||||
|
||||
/**
|
||||
* Sets the name of a module.
|
||||
*
|
||||
* Non-permanently named classes can have a temporary name assigned (or
|
||||
* cleared). In that case the name will be used for `#inspect` and `#to_s`, and
|
||||
* nested classes/modules will be named with the temporary name as a prefix.
|
||||
*
|
||||
* After the module is assigned to a constant, the temporary name will be
|
||||
* discarded, and the name will be computed based on the nesting.
|
||||
*
|
||||
* @param[in] mod An instance of ::rb_cModule.
|
||||
* @param[in] name An instance of ::rb_cString.
|
||||
* @retval mod
|
||||
*/
|
||||
VALUE rb_mod_set_temporary_name(VALUE, VALUE);
|
||||
|
||||
struct gen_ivtbl;
|
||||
int rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **ivtbl);
|
||||
int rb_obj_evacuate_ivs_to_hash_table(ID key, VALUE val, st_data_t arg);
|
||||
|
|
1
object.c
1
object.c
|
@ -4508,6 +4508,7 @@ InitVM_Object(void)
|
|||
rb_define_method(rb_cModule, "included_modules", rb_mod_included_modules, 0); /* in class.c */
|
||||
rb_define_method(rb_cModule, "include?", rb_mod_include_p, 1); /* in class.c */
|
||||
rb_define_method(rb_cModule, "name", rb_mod_name, 0); /* in variable.c */
|
||||
rb_define_method(rb_cModule, "set_temporary_name", rb_mod_set_temporary_name, 1); /* in variable.c */
|
||||
rb_define_method(rb_cModule, "ancestors", rb_mod_ancestors, 0); /* in class.c */
|
||||
|
||||
rb_define_method(rb_cModule, "attr", rb_mod_attr, -1);
|
||||
|
|
|
@ -6,6 +6,55 @@ describe "Module#name" do
|
|||
Module.new.name.should be_nil
|
||||
end
|
||||
|
||||
ruby_version_is "3.3" do
|
||||
it "can assign a temporary name" do
|
||||
m = Module.new
|
||||
m.name.should be_nil
|
||||
|
||||
m.set_temporary_name("fake_name")
|
||||
m.name.should == "fake_name"
|
||||
|
||||
m.set_temporary_name(nil)
|
||||
m.name.should be_nil
|
||||
end
|
||||
|
||||
it "can't assign empty string as name" do
|
||||
m = Module.new
|
||||
-> { m.set_temporary_name("") }.should raise_error(ArgumentError, "empty class/module name")
|
||||
end
|
||||
|
||||
it "can't assign a constant name as a temporary name" do
|
||||
m = Module.new
|
||||
-> { m.set_temporary_name("Object") }.should raise_error(ArgumentError, "name must not be valid constant name")
|
||||
end
|
||||
|
||||
it "can't assign name to permanent module" do
|
||||
-> { Object.set_temporary_name("fake_name") }.should raise_error(RuntimeError, "can't change permanent name")
|
||||
end
|
||||
|
||||
it "can assign a temporary name to a nested module" do
|
||||
m = Module.new
|
||||
module m::N; end
|
||||
m::N.name.should =~ /\A#<Module:0x\h+>::N\z/
|
||||
|
||||
m::N.set_temporary_name("fake_name")
|
||||
m::N.name.should == "fake_name"
|
||||
|
||||
m::N.set_temporary_name(nil)
|
||||
m::N.name.should be_nil
|
||||
end
|
||||
|
||||
it "can update the name when assigned to a constant" do
|
||||
m = Module.new
|
||||
m::N = Module.new
|
||||
m::N.name.should =~ /\A#<Module:0x\h+>::N\z/
|
||||
m::N.set_temporary_name(nil)
|
||||
|
||||
m::M = m::N
|
||||
m::M.name.should =~ /\A#<Module:0x\h+>::M\z/m
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is ""..."3.0" do
|
||||
it "is nil when assigned to a constant in an anonymous module" do
|
||||
m = Module.new
|
||||
|
|
73
variable.c
73
variable.c
|
@ -134,6 +134,79 @@ rb_mod_name(VALUE mod)
|
|||
return classname(mod, &permanent);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* mod.set_temporary_name(string) -> self
|
||||
* mod.set_temporary_name(nil) -> self
|
||||
*
|
||||
* Sets the temporary name of the module +mod+. This name is used as a prefix
|
||||
* for the names of constants declared in +mod+. If the module is assigned a
|
||||
* permanent name, the temporary name is discarded.
|
||||
*
|
||||
* After a permanent name is assigned, a temporary name can no longer be set,
|
||||
* and this method raises a RuntimeError.
|
||||
*
|
||||
* If the name given is not a string or is a zero length string, this method
|
||||
* raises an ArgumentError.
|
||||
*
|
||||
* The temporary name must not be a valid constant name, to avoid confusion
|
||||
* with actual constants. If you attempt to set a temporary name that is a
|
||||
* a valid constant name, this method raises an ArgumentError.
|
||||
*
|
||||
* If the given name is +nil+, the module becomes anonymous.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* m = Module.new # => #<Module:0x0000000102c68f38>
|
||||
* m.name #=> nil
|
||||
*
|
||||
* m.set_temporary_name("fake_name") # => fake_name
|
||||
* m.name #=> "fake_name"
|
||||
*
|
||||
* m.set_temporary_name(nil) # => #<Module:0x0000000102c68f38>
|
||||
* m.name #=> nil
|
||||
*
|
||||
* n = Module.new
|
||||
* n.set_temporary_name("fake_name")
|
||||
*
|
||||
* n::M = m
|
||||
* n::M.name #=> "fake_name::M"
|
||||
* N = n
|
||||
*
|
||||
* N.name #=> "nested_fake_name"
|
||||
* N::M.name #=> "N::M"
|
||||
*/
|
||||
|
||||
VALUE
|
||||
rb_mod_set_temporary_name(VALUE mod, VALUE name)
|
||||
{
|
||||
// We don't allow setting the name if the classpath is already permanent:
|
||||
if (RCLASS_EXT(mod)->permanent_classpath) {
|
||||
rb_raise(rb_eRuntimeError, "can't change permanent name");
|
||||
}
|
||||
|
||||
if (NIL_P(name)) {
|
||||
// Set the temporary classpath to NULL (anonymous):
|
||||
RCLASS_SET_CLASSPATH(mod, 0, FALSE);
|
||||
} else {
|
||||
// Ensure the name is a string:
|
||||
StringValue(name);
|
||||
|
||||
if (RSTRING_LEN(name) == 0) {
|
||||
rb_raise(rb_eArgError, "empty class/module name");
|
||||
}
|
||||
|
||||
if (rb_is_const_name(name)) {
|
||||
rb_raise(rb_eArgError, "name must not be valid constant name");
|
||||
}
|
||||
|
||||
// Set the temporary classpath to the given name:
|
||||
RCLASS_SET_CLASSPATH(mod, name, FALSE);
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
make_temporary_path(VALUE obj, VALUE klass)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче