[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Iterating through class names using a block

Ge Bro

11/24/2007 4:52:00 AM

Hey all,
this is a total newbie question, i'm just starting to learn Ruby.

here's the thing: i've got a couple of arrays with several items in
each. I'd like to use a block to iterate through each array, and create
objects of the class that is named the same as the array.

This is supposed to populate a database where tables are named after
arrays, and each table contains 1 column called "item_name" with items
from the corresponding array.

allow me to illustrate:

###

some_item = ['foo', 'bar']
other_item = ['alpha', 'bravo', 'foxtrot', 'zebra']

['some_item', 'other_item'].each do |this_array|
array_length = 0
this_object = this_array.to_s.capitalize #<== here's the problem
until array_length == this_array.length
this_object.new(:item_name => this_array[array_length])
array_length += 1
end
end

###

the problem is, this_object becomes a string with the value of
|this_array|. How do I explain to the program that its value is not just
a string, but actually a name of the class, an instance of which i'm
trying to initialize?

Forgive the dumb question, but i'm just starting out. I've been banging
my head against this for hours.... any help would be appreciated...

thanks!
--
Posted via http://www.ruby-....

16 Answers

Phrogz

11/24/2007 5:08:00 AM

0

On Nov 23, 9:52 pm, Ge Bro <booms...@gmail.com> wrote:
> this_object = this_array.to_s.capitalize #<== here's the problem

Search the archives (this is not just a forum on a website, but a
mailing list and newsgroup) for posts like string name to class. The
simple answer involves const_get. This is a FAQ.

Phrogz

11/24/2007 5:08:00 AM

0

On Nov 23, 10:07 pm, Phrogz <phr...@mac.com> wrote:
> On Nov 23, 9:52 pm, Ge Bro <booms...@gmail.com> wrote:
>
> > this_object = this_array.to_s.capitalize #<== here's the problem
>
> Search the archives (this is not just a forum on a website, but a
> mailing list and newsgroup) for posts like string name to class. The
> simple answer involves const_get. This is a FAQ.

Oh, and...welcome to Ruby :)

7stud --

11/24/2007 7:31:00 AM

0

Ge Bro wrote:
> ['some_item', 'other_item'].each do |this_array|
> ..
> this_object = this_array.to_s.capitalize #<== here's the problem

> the problem is, this_object becomes a string with the value of
> |this_array|.

This loop:

['some_item', 'other_item'].each do |this_array|

says to take each element in the array and stuff it in the variable
this_array. The array is that thing surrounded by brackets. Since each
element of the array is a string--that's what the quotes mean--those
strings get stuffed into the variable this_array in turn. For example,
the first time through the loop, the string 'some_item' will get stuffed
into the variable this_array.

Inside the loop, you tell ruby to convert the string stored in
this_array, which is 'some_item', into a string, but the string
'some_item' is already a string, so the to_s method call doesn't do
anything. Then you say to capitalize the string 'some_item', which
gives you: 'Some_item', and then you assign 'Some_item to this_object.

> the problem is, this_object becomes a string with the value of
> |this_array|. How do I explain to the program that its value is not just
> a string, but actually a name of the class

'Some_item' is the name of a class? Where is the 'Some_item' class
defined in your code? For that matter, where is any class defined in
your code?

--
Posted via http://www.ruby-....

dusty

11/24/2007 5:28:00 PM

0

On Nov 23, 11:52 pm, Ge Bro <booms...@gmail.com> wrote:
> Hey all,
> this is a total newbie question, i'm just starting to learn Ruby.
>
> here's the thing: i've got a couple of arrays with several items in
> each. I'd like to use a block to iterate through each array, and create
> objects of the class that is named the same as the array.
>
> This is supposed to populate a database where tables are named after
> arrays, and each table contains 1 column called "item_name" with items
> from the corresponding array.
>
> allow me to illustrate:
>
> ###
>
> some_item = ['foo', 'bar']
> other_item = ['alpha', 'bravo', 'foxtrot', 'zebra']
>
> ['some_item', 'other_item'].each do |this_array|
> array_length = 0
> this_object = this_array.to_s.capitalize #<== here's the problem
> until array_length == this_array.length
> this_object.new(:item_name => this_array[array_length])
> array_length += 1
> end
> end
>
> ###
>
> the problem is, this_object becomes a string with the value of
> |this_array|. How do I explain to the program that its value is not just
> a string, but actually a name of the class, an instance of which i'm
> trying to initialize?
>
> Forgive the dumb question, but i'm just starting out. I've been banging
> my head against this for hours.... any help would be appreciated...
>
> thanks!
> --
> Posted viahttp://www.ruby-....

Something like this will work.

class Foo; end
class Bar; end
arr = ['foo','bar','bad']
out = arr.collect {|a| Object.const_get(a.capitalize) rescue nil }
out.compact!

See http://www.ruby-doc.org/core/classes/...

Look for collect and compact! to see what I'm doing there.

dusty

11/24/2007 5:29:00 PM

0

On Nov 24, 12:27 pm, dusty <dusty.do...@gmail.com> wrote:
> On Nov 23, 11:52 pm, Ge Bro <booms...@gmail.com> wrote:
>
>
>
> > Hey all,
> > this is a total newbie question, i'm just starting to learn Ruby.
>
> > here's the thing: i've got a couple of arrays with several items in
> > each. I'd like to use a block to iterate through each array, and create
> > objects of the class that is named the same as the array.
>
> > This is supposed to populate a database where tables are named after
> > arrays, and each table contains 1 column called "item_name" with items
> > from the corresponding array.
>
> > allow me to illustrate:
>
> > ###
>
> > some_item = ['foo', 'bar']
> > other_item = ['alpha', 'bravo', 'foxtrot', 'zebra']
>
> > ['some_item', 'other_item'].each do |this_array|
> > array_length = 0
> > this_object = this_array.to_s.capitalize #<== here's the problem
> > until array_length == this_array.length
> > this_object.new(:item_name => this_array[array_length])
> > array_length += 1
> > end
> > end
>
> > ###
>
> > the problem is, this_object becomes a string with the value of
> > |this_array|. How do I explain to the program that its value is not just
> > a string, but actually a name of the class, an instance of which i'm
> > trying to initialize?
>
> > Forgive the dumb question, but i'm just starting out. I've been banging
> > my head against this for hours.... any help would be appreciated...
>
> > thanks!
> > --
> > Posted viahttp://www.ruby-....
>
> Something like this will work.
>
> class Foo; end
> class Bar; end
> arr = ['foo','bar','bad']
> out = arr.collect {|a| Object.const_get(a.capitalize) rescue nil }
> out.compact!
>
> Seehttp://www.ruby-doc.org/core/classes/...
>
> Look for collect and compact! to see what I'm doing there.

Sorry, forgot to instantiate the objects. Should have been.

class Foo; end
class Bar; end
arr = ['foo','bar','bad']
out = arr.collect {|a| Object.const_get(a.capitalize).new rescue nil }
out.compact!

Ge Bro

11/24/2007 7:59:00 PM

0

Great, so with everyone's help this is what I ended up doing in the end:

foo = [1foo, 2foo, 3foo]
bar = [abar, bbar]

['foo', 'bar'].each do |this_array|
length = 0
this_obj = this_array.to_s.capitalize.constantize
until length == eval(this_array).length
this_obj.create(:word => eval(this_array)[length])
length += 1
end

the trick was to use the .constantize method to turn 'foo' and 'bar'
into classes Foo and Bar, and then to use eval() to use the array's
names instead of its contents.

I came across .constantize after searching for const_get as phrogz
suggested.

7stud, my classes are defined in their corresponding controllers - this
is a part of a Rails exercise

dusty, your method using collect and compact looks interesting, I'll
play with it too.

Thanks everyone for help. I haven't been excited about programming for
the last 15 years, but Ruby is changing that rapidly :)


--
Posted via http://www.ruby-....

Ge Bro

11/24/2007 8:04:00 PM

0

Ge Bro wrote:
> Great, so with everyone's help this is what I ended up doing in the end:
>
> foo = [1foo, 2foo, 3foo]
> bar = [abar, bbar]
>
> ['foo', 'bar'].each do |this_array|
> length = 0
> this_obj = this_array.to_s.capitalize.constantize
> until length == eval(this_array).length
> this_obj.create(:word => eval(this_array)[length])
> length += 1
> end
>
> the trick was to use the .constantize method to turn 'foo' and 'bar'
> into classes Foo and Bar, and then to use eval() to use the array's
> names instead of its contents.
>
> I came across .constantize after searching for const_get as phrogz
> suggested.
>
> 7stud, my classes are defined in their corresponding controllers - this
> is a part of a Rails exercise
>
> dusty, your method using collect and compact looks interesting, I'll
> play with it too.
>
> Thanks everyone for help. I haven't been excited about programming for
> the last 15 years, but Ruby is changing that rapidly :)


btw, i do have to admit that i have no clear idea of why eval() works
here and what it's actually supposed to do. Just reading about it now
and trying to make sense of it.
--
Posted via http://www.ruby-....

7stud --

11/24/2007 9:03:00 PM

0

Ge Bro wrote:
> Great, so with everyone's help this is what I ended up doing in the end:

You didn't learn your lessons very well. Look up what the method to_s
does. Does calling to_s on a string do anything? Experiment.

> 7stud, my classes are defined in their corresponding controllers - this
> is a part of a Rails exercise

This isn't a Rails forum.

> I came across .constantize after searching for const_get as phrogz
> suggested.

If you are discarding the ruby solutions that were proffered in favor of
rails specific solutions, then why not just go to the rails forum?

In any case, if you look at the definition of the constantize method, it
just calls the ruby method module_eval, and module_eval seems to act
like const_get in your situation, although module_eval can also do some
other things.


> btw, i do have to admit that i have no clear idea of why eval() works
> here and what it's actually supposed to do.

You don't seem to understand the difference between a variable name and
a string. The most obvious difference is that a string has quotes
around it. Look at this example:

arr = [1, 2, 3, 4, 5, 6, 7]

puts 'arr'.length
puts arr.length

Can you guess what the output will be? Run the code and see if you are
correct.

Now try this:

puts 'arr'[0, 1]
puts arr[0, 1]

Those lines say get the elements starting at position 0 and stopping at
position 1(which does not include the stopping position). Can you
guess the output?

A string and a variable name are different animals.

The eval method says to treat a string as if it is ruby code. If your
string looks like this:

str = "num = 10; puts num"

and you eval() that string then ruby will treat the string as code and
execute it. Essentially, eval removes the quotes around a string and
then treats what's left as code. As a result, when you ask ruby to eval
a string like:

"name"

that becomes:

name

and to ruby that looks like a variable name or a method call. Try this
program:

eval("name")

--output:---
r5test.rb:1: undefined local variable or method `name' for main:Object
(NameError)

ruby can't find a variable named name nor a method named name, so ruby
produces the error message. Now try this:

def name
puts 'Jon'
end

name

The output should be obvious. Now using eval:

def name
puts 'Jon'
end

eval("name")

Why is that useful? Why not just use the previous example's code?
Because sometimes you have a method name as a string, and you want to
execute the method, e.g.:

puts "What method do you want to execute: "
input = gets
input = input.strip

def hi
puts 'hello'
end

def bye
puts 'goodbye'
end

eval(input)

--output:--
What method do you want to execute:
hi
hello

However, you should avoid using eval whenever possible. First, it's
slow. Second, someone could enter a string that contains a command to
erase your hard drive. When you eval that string, poof!







--
Posted via http://www.ruby-....

7stud --

11/24/2007 9:12:00 PM

0

Ge Bro wrote:
> Hey all,
> this is a total newbie question, i'm just starting to learn Ruby.
>
> here's the thing: i've got a couple of arrays with several items in
> each. I'd like to use a block to iterate through each array, and create
> objects of the class that is named the same as the array.
>
> This is supposed to populate a database where tables are named after
> arrays, and each table contains 1 column called "item_name" with items
> from the corresponding array.
>
> allow me to illustrate:
>
> ###
>
> some_item = ['foo', 'bar']
> other_item = ['alpha', 'bravo', 'foxtrot', 'zebra']
>
> ['some_item', 'other_item'].each do |this_array|
> array_length = 0
> this_object = this_array.to_s.capitalize #<== here's the problem
> until array_length == this_array.length
> this_object.new(:item_name => this_array[array_length])
> array_length += 1
> end
> end
>
> ###
>
> the problem is, this_object becomes a string with the value of
> |this_array|. How do I explain to the program that its value is not just
> a string, but actually a name of the class, an instance of which i'm
> trying to initialize?
>
> Forgive the dumb question, but i'm just starting out. I've been banging
> my head against this for hours.... any help would be appreciated...
>
> thanks!

By the way, are you aware that your arrays can contain the class objects
themselves rather than strings:

class Dog
def id
puts "I'm a Dog"
end
end

class Flower
def id
puts "I'm a Flower"
end
end

class Circle
def id
puts "I'm a Circle"
end
end

arr = [Dog, Flower, Circle]

arr.each do |a_class|
obj = a_class.new
obj.id
end

--output:--
I'm a Dog
I'm a Flower
I'm a Circle



--
Posted via http://www.ruby-....

Giles Bowkett

11/25/2007 12:01:00 AM

0

> > this_object = this_array.to_s.capitalize #<== here's the problem
>
> Search the archives (this is not just a forum on a website, but a
> mailing list and newsgroup) for posts like string name to class. The
> simple answer involves const_get. This is a FAQ.

The gateway link to this list is the top link on ruby-forum.com.
Considering how easy it would be for a newbie to just click the first
link on the list, the newbie questions we get are actually pretty
good. But I e-mailed the Ruby Forum guy anyway to see if I could
persuade him to move the link down the list a bit and maybe put the
Rails list link at the top. No dice so far. No response so far, in
fact.

Also, just a note - the OP says he doesn't know what eval does - and 7stud said:

> However, you should avoid using eval whenever possible. First, it's
> slow. Second, someone could enter a string that contains a command to
> erase your hard drive. When you eval that string, poof!

Just wanted to chime in with my agreement. eval is risky, don't mess
with eval unless you're sure you know what you're doing (or you're
feeling extremely lucky).

Also:

> here's the thing: i've got a couple of arrays with several items in
> each. I'd like to use a block to iterate through each array, and create
> objects of the class that is named the same as the array.

I would say that the real way to fix this would be to fix this part
here. That's really where the bug is. Naming the array after the
class. If you have that data, you can also use it to create a string,
and that's so much easier.

Instead of

arbitrary_class = [item, other_item]

And then a whole bunch of "meta" stuff, including an eval on the class
name - which you have to create an array of strings to do anyway - I
would recommend maybe this:

classes_with_items = {"ArbitraryClass" => ["item", "other_item"]}

and then

classes_with_items.each do |class_name, array|
array.each do |item_name|
class_name.constantize.new(:item_name => item_name)
end
end

It's a lot easier to read and you won't be cursing yourself a month
later when you find the code, read it, and need to remember what it
does and how it does it.

--
Giles Bowkett

Podcast: http://hollywoodgrit.bl...
Blog: http://gilesbowkett.bl...
Portfolio: http://www.gilesg...
Tumblelog: http://giles....