to_s mixin/reflection/newbie stuff

K

Kris Helenek

I'm very new to ruby, coming from a long j2ee background. I hope these
questions make sense and aren't covered elsewhere (i looked, really.)

I'm creating a project in rails and i have several model objects that I
want to override the to_s method on. It looks like using a "mixin" or
module is the way to go since i don't want to write a new to_s for every
model class that i have to update every time i make a change to the db.

In java my model classes would use reflection to discover all the
instance variables and concatenate them together (the apache commons
ToStringBuilder.)

I went to accomplish that for my ruby model class but honestly could not
figure out how. I was first stumped trying to override the to_s in the
first place by putting

Class Member
def to_s
"Member[" + @first_name + ", " + @last_name + "]"
end

And that fails out on me (@first_name is nil!)

Then i switched it to
def to_s
"Member[" + first_name + ", " + last_name + "]"
end

And that worked. I don't get it, i thought instance variables always
started with the @?

So i guess my questions are, why no @ before my instance variables, and
how would i write a method that gets all instance variables (and then
concatenates them, although i can figure that out. hopefully ;)

Thanks
 
H

hemant

I'm very new to ruby, coming from a long j2ee background. I hope these
questions make sense and aren't covered elsewhere (i looked, really.)

I'm creating a project in rails and i have several model objects that I
want to override the to_s method on. It looks like using a "mixin" or
module is the way to go since i don't want to write a new to_s for every
model class that i have to update every time i make a change to the db.

In java my model classes would use reflection to discover all the
instance variables and concatenate them together (the apache commons
ToStringBuilder.)

I went to accomplish that for my ruby model class but honestly could not
figure out how. I was first stumped trying to override the to_s in the
first place by putting

Class Member
def to_s
"Member[" + @first_name + ", " + @last_name + "]"
end

And that fails out on me (@first_name is nil!)

Then i switched it to
def to_s
"Member[" + first_name + ", " + last_name + "]"
end

And that worked. I don't get it, i thought instance variables always
started with the @?

So i guess my questions are, why no @ before my instance variables, and
how would i write a method that gets all instance variables (and then
concatenates them, although i can figure that out. hopefully ;)

Because @first_name is apparently not a instance variable in rails models:

Have a look:
=> ["copy_instance_variables_from", "instance_variables",
"instance_variable_get", "instance_variable_set", "instance_values",
"instance_exec", "instance_of?", "instance_eval"]
["@attributes"]

So, your second attempt worked, because every column is an attribute
and rails creates accessor methods for them.

If you want to override #to_s method for all your models, then an easy
solution would be

module Foobar
def to_s
#put your foobar here
end
end

And mix this module, in all your models where you want to override
behaviour of #to_s. I haven't tested this approach although.
 
K

Kris Helenek

Thanks Hemant! I still have trouble seeing things as methods without
the parentheses(); but it makes perfect sense now. And i wrote a nice
little module to duplicate what is often done to create good debug
output in hibernate (j2ee world):

module ReflectionToString
def to_s
str = self.class.name + "["
for attribute in self.attributes
str += attribute[0].to_s + ": " + attribute[1].to_s + ", "
end
str += "]"
str
end
end

Someone could probably clean it up a bit but its just a first pass...

I have a Rails specific question (i know this is kinda the wrong forum,
but I'm already here..) Where is the appropriate place to store this
module? In the helper or model folder? lib? A new folder? Don't know the
conventions yet and I haven't seen anything about utility classes yet...
thanks again.
 
H

hemant

Thanks Hemant! I still have trouble seeing things as methods without
the parentheses(); but it makes perfect sense now. And i wrote a nice
little module to duplicate what is often done to create good debug
output in hibernate (j2ee world):

module ReflectionToString
def to_s
str = self.class.name + "["
for attribute in self.attributes
str += attribute[0].to_s + ": " + attribute[1].to_s + ", "
end
str += "]"
str
end
end

Someone could probably clean it up a bit but its just a first pass...

def to_s
str = "#{self.class} ["
@attributes.each {|x| str << "#{attribute[0]} : #{attribute[1]} , " }
str << "]"
end

No need to return str, because last statement evaluated is returned.
I have a Rails specific question (i know this is kinda the wrong forum,
but I'm already here..) Where is the appropriate place to store this
module? In the helper or model folder? lib? A new folder? Don't know the
conventions yet and I haven't seen anything about utility classes yet...
thanks again.

#{RAILS_ROOT}/lib is the appropriate place, although when it grows a
little more bigger and may be reusable, its good idea to create a
plugin.
 
H

hemant

Thanks Hemant! I still have trouble seeing things as methods without
the parentheses(); but it makes perfect sense now. And i wrote a nice
little module to duplicate what is often done to create good debug
output in hibernate (j2ee world):

module ReflectionToString
def to_s
str = self.class.name + "["
for attribute in self.attributes
str += attribute[0].to_s + ": " + attribute[1].to_s + ", "
end
str += "]"
str
end
end

Someone could probably clean it up a bit but its just a first pass...

def to_s
str = "#{self.class} ["
@attributes.each {|x| str << "#{attribute[0]} : #{attribute[1]} , " }
str << "]"
end

No need to return str, because last statement evaluated is returned.
I have a Rails specific question (i know this is kinda the wrong forum,
but I'm already here..) Where is the appropriate place to store this
module? In the helper or model folder? lib? A new folder? Don't know the
conventions yet and I haven't seen anything about utility classes yet...
thanks again.

#{RAILS_ROOT}/lib is the appropriate place, although when it grows a
little more bigger and may be reusable, its good idea to create a
plugin.


Oops, replace "attribute" with x in the iterator, please.
 
H

hemant

Thanks Hemant! I still have trouble seeing things as methods without
the parentheses(); but it makes perfect sense now. And i wrote a nice
little module to duplicate what is often done to create good debug
output in hibernate (j2ee world):

module ReflectionToString
def to_s
str = self.class.name + "["
for attribute in self.attributes
str += attribute[0].to_s + ": " + attribute[1].to_s + ", "
end
str += "]"
str
end
end

Someone could probably clean it up a bit but its just a first pass...

def to_s
str = "#{self.class} ["
@attributes.each {|x| str << "#{attribute[0]} : #{attribute[1]} , " }
str << "]"
end

No need to return str, because last statement evaluated is returned.
I have a Rails specific question (i know this is kinda the wrong forum,
but I'm already here..) Where is the appropriate place to store this
module? In the helper or model folder? lib? A new folder? Don't know the
conventions yet and I haven't seen anything about utility classes yet...
thanks again.

#{RAILS_ROOT}/lib is the appropriate place, although when it grows a
little more bigger and may be reusable, its good idea to create a
plugin.


Oops, replace "attribute" with x in the iterator, please.


Also, your method is a bit buggy and hence mine is too, it value
returned by to_s will have a "," at the end.

so a better approach would be:

def to_s
str = "#{self.class} ["
str << @attributes.map {|key,value| "#{key} : #{value}"}.join(',')
str << "]"
end
 
R

Robert Dober

Thanks Hemant! I still have trouble seeing things as methods without
the parentheses(); but it makes perfect sense now. And i wrote a nice
little module to duplicate what is often done to create good debug
output in hibernate (j2ee world):

module ReflectionToString
def to_s
str = self.class.name + "["
for attribute in self.attributes
str += attribute[0].to_s + ": " + attribute[1].to_s + ", "
end
str += "]"
str
end
end

Someone could probably clean it up a bit but its just a first pass...

def to_s
str = "#{self.class} ["
@attributes.each {|x| str << "#{attribute[0]} : #{attribute[1]} , " }
str << "]"
end

No need to return str, because last statement evaluated is returned.


I have a Rails specific question (i know this is kinda the wrong forum,
but I'm already here..) Where is the appropriate place to store this
module? In the helper or model folder? lib? A new folder? Don't know the
conventions yet and I haven't seen anything about utility classes yet...
thanks again.


#{RAILS_ROOT}/lib is the appropriate place, although when it grows a
little more bigger and may be reusable, its good idea to create a
plugin.


Oops, replace "attribute" with x in the iterator, please.


Also, your method is a bit buggy and hence mine is too, it value
returned by to_s will have a "," at the end.

so a better approach would be:

def to_s
str = "#{self.class} ["
str << @attributes.map {|key,value| "#{key} : #{value}"}.join(',')
str << "]"
end
Is inspect not a better choice?
Maybe the following code snippet proves helpfull:

430/6 > irb
irb(main):001:0> class A
irb(main):002:1> attr_accessor :a
irb(main):003:1> end
=> nil
irb(main):004:0> class B
irb(main):005:1> attr_accessor :b
irb(main):006:1> end
=> nil
irb(main):007:0> a = A.new
=> #<A:0xb7d786f8>
irb(main):008:0> b = B.new
=> #<B:0xb7d73310>
irb(main):009:0> b.b = 0x2a
=> 42
irb(main):010:0> a.a=b
=> #<B:0xb7d73310 @b=42>
irb(main):011:0> a.inspect
=> "#<A:0xb7d786f8 @a=#<B:0xb7d73310 @b=42>>"
irb(main):012:0>


HTH
Robert
 

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

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,124
Latest member
JuniorPell
Top