string to Class object

R

R. Mark Volkmann

How can I create a Class object from a String that contains the name of a class?
For example, after

clazz = Array

the variable clazz holds a Class object representing the class Array.
I want to start with a string like this.

name = 'Array'
clazz = ???
 
D

Devin Mullins

Good question. A crappy way:

ObjectSpace.each_object {|obj|
break obj if Class === obj and obj.name == clazz
}

But I'm still young. There might be a non-crappy way.

Devin
 
A

Austin Ziegler

How can I create a Class object from a String that contains the name of a= class?
For example, after
=20
clazz =3D Array
=20
the variable clazz holds a Class object representing the class Array.
I want to start with a string like this.
=20
name =3D 'Array'
clazz =3D ???

For top-level objects, this is as simple as:

name =3D 'Array'
klass =3D Object.const_get(name)
p klass # =3D> Array

For nested values, it's:

name =3D 'Transaction::Simple::Group'
klass =3D name.split(/::/).inject(Object) { |k, n| k.const_get(n) }
p klass # =3D> Transaction::Simple::Group

-austin
--=20
Austin Ziegler * (e-mail address removed)
* Alternate: (e-mail address removed)
 
J

James Edward Gray II

How can I create a Class object from a String that contains the
name of a class?
For example, after

clazz = Array

the variable clazz holds a Class object representing the class Array.
I want to start with a string like this.

name = 'Array'
clazz = ???

clazz = Object.const_get(name)

Hope that helps.

James Edward Gray II
 
D

Devin Mullins

Well, slightly less:

ObjectSpace.each_object(Class) {|obj|
break obj if obj.name == clazz
}

Devin
 
H

Hal Fulton

Austin said:
For top-level objects, this is as simple as:

name = 'Array'
klass = Object.const_get(name)
p klass # => Array

For nested values, it's:

name = 'Transaction::Simple::Group'
klass = name.split(/::/).inject(Object) { |k, n| k.const_get(n) }
p klass # => Transaction::Simple::Group

Quite right. There is also the eval (evil?) method:

clazz = eval(name)


BTW, I've wondered why Object.const_get doesn't honor
the X::Y::Z form. It's unambiguous and I can't see a
good reason not to decipher it.


Hal
 
A

Austin Ziegler

Quite right. There is also the eval (evil?) method:
=20
clazz =3D eval(name)
BTW, I've wondered why Object.const_get doesn't honor
the X::Y::Z form. It's unambiguous and I can't see a
good reason not to decipher it.

RCR?

-austin
--=20
Austin Ziegler * (e-mail address removed)
* Alternate: (e-mail address removed)
 
M

Mark Hubbart

=20
Quite right. There is also the eval (evil?) method:
=20
clazz =3D eval(name)
=20
=20
BTW, I've wondered why Object.const_get doesn't honor
the X::Y::Z form. It's unambiguous and I can't see a
good reason not to decipher it.

One possible reason: when you call Object#const_get, you're asking
Object to fetch a constant in its namespace. It might not be logical
to allow recursive decent. (I'm not convinced of this, though... it's
just a thought :)

What about adding a Kernel#const_get that does this? That might
separate things a little better; a global method that decodes any
class name, nested or not, based on current context.

cheers,
Mark
 
N

nobuyoshi nakada

Hi,

At Fri, 24 Jun 2005 16:07:18 +0900,
Mark Hubbart wrote in [ruby-talk:146353]:
What about adding a Kernel#const_get that does this? That might
separate things a little better; a global method that decodes any
class name, nested or not, based on current context.

Kernel also is one of modules.

$ ruby -e 'p Kernel.const_get("Object")'
Object


Index: object.c
===================================================================
RCS file: /cvs/ruby/src/ruby/object.c,v
retrieving revision 1.169
diff -U2 -p -r1.169 object.c
--- object.c 11 May 2005 07:00:32 -0000 1.169
+++ object.c 24 Jun 2005 04:37:33 -0000
@@ -1715,9 +1715,46 @@ rb_mod_attr_accessor(argc, argv, klass)
}

+static ID
+mod_const_id(namep)
+ volatile VALUE *namep;
+{
+ VALUE tmp, name = *namep;
+ ID id;
+
+ switch (TYPE(name)) {
+ case T_STRING:
+ break;
+ case T_FIXNUM:
+ rb_warn("do not use Fixnums as Symbols");
+ id = FIX2LONG(name);
+ if (!rb_id2name(id)) {
+ rb_raise(rb_eArgError, "%ld is not a symbol", id);
+ }
+ return id;
+ case T_SYMBOL:
+ return SYM2ID(name);
+ default:
+ tmp = rb_check_string_type(name);
+ if (NIL_P(tmp)) {
+ rb_raise(rb_eTypeError, "%s is not a symbol", RSTRING(rb_inspect(name))->ptr);
+ }
+ *namep = name = tmp;
+ }
+ if (RSTRING(name)->ptr && strchr(RSTRING(name)->ptr, ':'))
+ return 0;
+
+ id = str_to_id(name);
+ if (!rb_is_const_id(id)) {
+ rb_name_error(id, "wrong constant name %s", rb_id2name(id));
+ }
+
+ return id;
+}
+
/*
* call-seq:
* mod.const_get(sym) => obj
*
- * Returns the value of the named constant in <i>mod</i>.
+ * Returns the value of the named constant under <i>mod</i>.
*
* Math.const_get:)PI) #=> 3.14159265358979
@@ -1728,9 +1765,10 @@ rb_mod_const_get(mod, name)
VALUE mod, name;
{
- ID id = rb_to_id(name);
+ ID id = mod_const_id(&name);

- if (!rb_is_const_id(id)) {
- rb_name_error(id, "wrong constant name %s", rb_id2name(id));
+ if (!id) {
+ return rb_path2const(RSTRING(name)->ptr, mod, Qtrue);
}
+
return rb_const_get(mod, id);
}
Index: variable.c
===================================================================
RCS file: /cvs/ruby/src/ruby/variable.c,v
retrieving revision 1.121
diff -U2 -p -r1.121 variable.c
--- variable.c 13 May 2005 08:56:14 -0000 1.121
+++ variable.c 24 Jun 2005 04:17:48 -0000
@@ -234,11 +234,21 @@ rb_set_class_path(klass, under, name)

VALUE
-rb_path2class(path)
+rb_path2const(path, cbase, idcheck)
const char *path;
+ VALUE cbase;
+ int idcheck;
{
const char *pbeg, *p;
ID id;
- VALUE c = rb_cObject;
+ VALUE c = cbase;

+ switch (TYPE(cbase)) {
+ case T_MODULE:
+ case T_CLASS:
+ break;
+ default:
+ rb_raise(rb_eTypeError, "class or module required but %s given",
+ rb_obj_classname(cbase));
+ }
if (path[0] == '#') {
rb_raise(rb_eArgError, "can't retrieve anonymous class %s", path);
@@ -249,6 +259,9 @@ rb_path2class(path)

while (*p && *p != ':') p++;
- str = rb_str_new(pbeg, p-pbeg);
+ *(volatile VALUE *)&str = rb_str_new(pbeg, p-pbeg);
id = rb_intern(RSTRING(str)->ptr);
+ if (idcheck && !rb_is_const_id(id)) {
+ rb_name_error(id, "wrong constant name %s", RSTRING(str)->ptr);
+ }
if (p[0] == ':') {
if (p[1] != ':') goto undefined_class;
@@ -258,7 +271,15 @@ rb_path2class(path)
if (!rb_const_defined(c, id)) {
undefined_class:
- rb_raise(rb_eArgError, "undefined class/module %.*s", p-path, path);
+ if (cbase == rb_cObject) {
+ rb_raise(rb_eArgError, "undefined class/module %s",
+ RSTRING(str)->ptr);
+ }
+ else {
+ rb_raise(rb_eArgError, "undefined class/module %s under %s",
+ RSTRING(str)->ptr, rb_class2name(c));
+ }
}
c = rb_const_get_at(c, id);
+ if (!*p) break;
switch (TYPE(c)) {
case T_MODULE:
@@ -273,4 +294,21 @@ rb_path2class(path)
}

+VALUE
+rb_path2class(path)
+ const char *path;
+{
+ VALUE c = rb_path2const(path, rb_cObject, Qfalse);
+
+ switch (TYPE(c)) {
+ case T_MODULE:
+ case T_CLASS:
+ break;
+ default:
+ rb_raise(rb_eTypeError, "%s does not refer class/module", path);
+ }
+
+ return c;
+}
+
void
rb_name_class(klass, id)
Index: intern.h
===================================================================
RCS file: /cvs/ruby/src/ruby/intern.h,v
retrieving revision 1.170
diff -U2 -p -r1.170 intern.h
--- intern.h 12 Jun 2005 16:56:05 -0000 1.170
+++ intern.h 24 Jun 2005 03:57:26 -0000
@@ -504,6 +504,7 @@
VALUE rb_mod_name _((VALUE));
VALUE rb_class_path _((VALUE));
void rb_set_class_path _((VALUE, VALUE, const char*));
+VALUE rb_path2const _((const char*, VALUE, int));
VALUE rb_path2class _((const char*));
void rb_name_class _((VALUE, ID));
VALUE rb_class_name _((VALUE));
 
M

Mark Hubbart

Hi,
=20
At Fri, 24 Jun 2005 16:07:18 +0900,
Mark Hubbart wrote in [ruby-talk:146353]:
What about adding a Kernel#const_get that does this? That might
separate things a little better; a global method that decodes any
class name, nested or not, based on current context.
=20
Kernel also is one of modules.
=20
$ ruby -e 'p Kernel.const_get("Object")'
Object

I actually meant a private instance method defined in Kernel, like
Kernel#warn et al. I was thinking that it might make sense to call
either
SomeNamespace.const_get("SomeConst")
or a plain
const_get("Nifty::Nested::Namespaces")
which could be done from anywhere, and would use the current scope.

But differentiating between the two looks like it may be be more
confusing than it's worth (especially since the worth was dubious
anyway). I un-submit the idea.

cheers,
Mark
 
R

R. Mark Volkmann

Quoting George Moschovitis said:
checkout

lib/facet/object/constant.rb in the Facets gem.

Thanks! It looks like this will do what I want.

After running "gem install facets" I found that the file constant.rb was placed
in C:\Ruby\Ruby1.8.2-15\lib\ruby\gems\1.8\gems\facets-0.7.2\lib\facet\object.
Just thought I'd share this in case anyone had trouble finding it.
 
G

George Moschovitis

lib/facet/object/constant.rb in the Facets gem.
Thanks! It looks like this will do what I want.

there is an even better alternative too:

lib/facet/class/by_name.rb
or
lib/facet/module/by_name.rb

(dont't remember exactly)

this allows you to do:

clazz = Class.by_name('Namespace::MyClass')

regards,
George.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
474,175
Messages
2,570,942
Members
47,489
Latest member
BrigidaD91

Latest Threads

Top