To_s mixin/reflection/newbie stuff


#1

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

Thanks


#2

On 4/18/07, Kris H. removed_email_address@domain.invalid wrote:

ToStringBuilder.)
And that fails out on me (@first_name is nil!)
how would i write a method that gets all instance variables (and then
concatenates them, although i can figure that out. hopefully :wink:

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

Have a look:

small_user = User.find(:first)
small_user.methods.grep(/instance/)
=> [“copy_instance_variables_from”, “instance_variables”,
“instance_variable_get”, “instance_variable_set”, “instance_values”,
“instance_exec”, “instance_of?”, “instance_eval”]

small_user.instance_variables
["@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.


#3

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.


#4

On 4/19/07, hemant removed_email_address@domain.invalid wrote:

  str += attribute[0].to_s + ": " + attribute[1].to_s + ", "

@attributes.each {|x| str << "#{attribute[0]} : #{attribute[1]} , " }

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.


#5

On 4/19/07, hemant removed_email_address@domain.invalid wrote:

for attribute in self.attributes

str = “#{self.class} [”

conventions yet and I haven’t seen anything about utility classes yet…

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


#6

On 4/19/07, Kris H. removed_email_address@domain.invalid wrote:

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.


#7

Hi,

Doan, Alex wrote:

Can someone explain to me why the following raise an error? (is this
something with File::Tail::Logfile.open not closing the file upon exit?)

Yes. Check out version 1.0.1 of File::Tail, where it is fixed. (Though
it may take some time to propagate the gem to the rubygems mirrors.)

BTW.: You’re lucky, that I have found your email here, if one considers
that you didn’t send it to me, and you reply-hijacked someone else’s
thread in order to write to ruby-talk.


#8

Thanks Frank,
This is my time using mailing group so I’m pretty new to how thing work.
I’ll create a new thread/send email to the creator next time.

Thanks Again for the reply.


#9

Can someone explain to me why the following raise an error? (is this
something with File::Tail::Logfile.open not closing the file upon exit?)
require ‘file/tail’
File::Tail::Logfile.open(‘output.txt’, :backward => 10, :return_if_eof
=> true ) do |log|
log.tail { |line| puts line}
end
File.rename(‘output.txt’, ‘output2.txt’)

Output:
C:/alex/work/build_process/scripts/test.rb:19:in `rename’: Permission
denied - output.txt or output2
.txt (Errno::EACCES)
from C:/alex/work/build_process/scripts/test.rb:19

============================================
While this work:
require ‘file/tail’

fh = File::Tail::Logfile.open(‘output.txt’, :backward => 10,
:return_if_eof => true )
fh.tail { |line| puts line }
fh.close
File.rename(‘output.txt’, ‘output2.txt’)


#10

On 4/18/07, hemant removed_email_address@domain.invalid wrote:

str = self.class.name + "["

def to_s

module? In the helper or model folder? lib? A new folder? Don’t know the
Oops, replace “attribute” with x in the iterator, please.
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