String to Class object

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 = ???

···

--
R. Mark Volkmann
Partner, Object Computing, Inc.

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

R. Mark Volkmann wrote:

···

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 = ???

--
R. Mark Volkmann
Partner, Object Computing, Inc.

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

-austin

···

On 6/23/05, R. Mark Volkmann <mark@ociweb.com> wrote:

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 = ???

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

clazz = Object.const_get(name)

Hope that helps.

James Edward Gray II

···

On Jun 23, 2005, at 8:26 PM, R. Mark Volkmann wrote:

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 = ???

checkout

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

-g.

Well, slightly less:

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

Devin

Devin Mullins wrote:

···

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

Austin Ziegler wrote:

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

Quoting George Moschovitis <george.moschovitis@gmail.com>:

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.

···

--
R. Mark Volkmann
Partner, Object Computing, Inc.

RCR?

-austin

···

On 6/23/05, Hal Fulton <hal9000@hypermetrics.com> wrote:

Austin Ziegler wrote:
> 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.

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

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 :slight_smile:

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

···

On 6/23/05, Hal Fulton <hal9000@hypermetrics.com> wrote:

Austin Ziegler wrote:
>
> 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.

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.

···

--

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));

--
Nobu Nakada

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

···

On 6/24/05, nobuyoshi nakada <nobuyoshi.nakada@ge.com> wrote:

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