зеркало из 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,
|
The class use equality semantic to lookup keys like a regular hash,
|
||||||
but it doesn't hold strong references on the keys. [[Feature #18498]]
|
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
|
## Stdlib updates
|
||||||
|
|
||||||
The following default gems are updated.
|
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 #19347]: https://bugs.ruby-lang.org/issues/19347
|
||||||
[Feature #19538]: https://bugs.ruby-lang.org/issues/19538
|
[Feature #19538]: https://bugs.ruby-lang.org/issues/19538
|
||||||
[Feature #19591]: https://bugs.ruby-lang.org/issues/19591
|
[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_SET(VALUE obj);
|
||||||
static inline void ROBJ_TRANSIENT_UNSET(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;
|
struct gen_ivtbl;
|
||||||
int rb_gen_ivtbl_get(VALUE obj, ID id, struct gen_ivtbl **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);
|
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, "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, "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, "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, "ancestors", rb_mod_ancestors, 0); /* in class.c */
|
||||||
|
|
||||||
rb_define_method(rb_cModule, "attr", rb_mod_attr, -1);
|
rb_define_method(rb_cModule, "attr", rb_mod_attr, -1);
|
||||||
|
|
|
@ -6,6 +6,55 @@ describe "Module#name" do
|
||||||
Module.new.name.should be_nil
|
Module.new.name.should be_nil
|
||||||
end
|
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
|
ruby_version_is ""..."3.0" do
|
||||||
it "is nil when assigned to a constant in an anonymous module" do
|
it "is nil when assigned to a constant in an anonymous module" do
|
||||||
m = Module.new
|
m = Module.new
|
||||||
|
|
73
variable.c
73
variable.c
|
@ -134,6 +134,79 @@ rb_mod_name(VALUE mod)
|
||||||
return classname(mod, &permanent);
|
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
|
static VALUE
|
||||||
make_temporary_path(VALUE obj, VALUE klass)
|
make_temporary_path(VALUE obj, VALUE klass)
|
||||||
{
|
{
|
||||||
|
|
Загрузка…
Ссылка в новой задаче