[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

string to Class object

R. Mark Volkmann

6/24/2005 1:27:00 AM

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.


12 Answers

Devin Mullins

6/24/2005 1:39:00 AM

0

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.
>
>
>
>



Austin Ziegler

6/24/2005 1:42:00 AM

0

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

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
--
Austin Ziegler * halostatue@gmail.com
* Alternate: austin@halostatue.ca


James Gray

6/24/2005 1:45:00 AM

0

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

clazz = Object.const_get(name)

Hope that helps.

James Edward Gray II


Devin Mullins

6/24/2005 1:46:00 AM

0

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




Hal E. Fulton

6/24/2005 1:51:00 AM

0

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




Austin Ziegler

6/24/2005 3:20:00 AM

0

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.

RCR?

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


Mark Hubbart

6/24/2005 7:07:00 AM

0

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.

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


Nakada, Nobuyoshi

6/24/2005 9:22:00 AM

0

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


Mark Hubbart

6/24/2005 4:51:00 PM

0

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

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


George Moschovitis

6/24/2005 6:40:00 PM

0

checkout

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

-g.