Attributes not populated until called?


#1

Hello all,

Pretty much new to Ruby, and I think I am not grasping something
fundamental
here. I’ve written some classes that grab meta tags from various audio
file
formats, and overrode 'to_s" to dump the tags to the console, and “to_a”
to
dump the tags to an array for use elsewhere in my program.

I am not sure how to explain my problem, so I will just paste in a
session
from irb that shows it:

irb> require ‘sneetchalizer’ # my class definitions…
irb> x =OggMetaTags.new(’/home/music/e/elliottSmith-shootingStar.ogg’)
=> #<OggMetaTags:0xa7cc1ac8 […rest omitted…]
irb> x.to_a
=> ["", “”, “”, “”, “”, “”, “”]
irb> x.title
=> “Shooting Star”
irb> x.to_a
=> [“Shooting Star”, “”, “”, “”, “”, “”, “”]
irb> x.artist
=> “Elliott Smith”
irb> x.to_a
=> [“Shooting Star”, “Elliott Smith”, “”, “”, “”, “”, “”]

So you can see I need to explicitly call each attribute before my array
gets
the value. How can I change this so the array is populated automatically
when
called?

Here is the class:

class OggMetaTags < MetaTags
def initialize(filename)
@filename = filename
require ‘ogginfo’
@tags = OggInfo.new(@filename)
end
def title
@title = @tags.tag[‘title’]
end
def artist
@artist = @tags.tag[‘artist’]
end
def album
@album = @tags.tag[‘album’]
end
def genre
@genre = @tags.tag[‘genre’]
end
def year
@year = @tags.tag[‘date’]
end
def comment
@comment = super
end
def tracknum
@tracknum = @tags.tag[‘tracknumber’]
end

def to_a
["#@title", “#@artist”, “#@album”, “#@genre”, “#@year”, “#@comment”,
“#@tracknum”]
end
end

If you need more info or want to see all the code please just ask.
Thanks for
consideration,
-d


#2

On 6/6/06, darren kirby removed_email_address@domain.invalid wrote:

I am not sure how to explain my problem, so I will just paste in a session
from irb that shows it:

irb> require ‘sneetchalizer’ # my class definitions…
irb> x =OggMetaTags.new(’/home/music/e/elliottSmith-shootingStar.ogg’)

All instance variables are defaulting to nil, none of the instance
variables
set in the initializer, are used in to_a, so to_a converts nil to a
string
which happens to be an empty string.

=> #<OggMetaTags:0xa7cc1ac8 […rest omitted…]

irb> x.to_a
=> ["", “”, “”, “”, “”, “”, “”]
irb> x.title

=> “Shooting Star”

The title method sets the instance variable @title, therefore to_a
yields

irb> x.to_a

=> [“Shooting Star”, “”, “”, “”, “”, “”, “”]

idem

irb> x.artist

=> “Elliott Smith”
irb> x.to_a
=> [“Shooting Star”, “Elliott Smith”, “”, “”, “”, “”, “”]

So you can see I need to explicitly call each attribute before my array
gets
the value. How can I change this so the array is populated automatically
when
called?

Here is the class:

def artist
end
end
end

If you need more info or want to see all the code please just ask. Thanks
for
consideration,

No the programmatic behavior is clear, I fail to see the reason though,
but
there might be one.
As a matter of fact there is a good reason to cache the value of the
attribute in the reader but that would be done like this
@year = @tags.tag[‘date’]

-d


darren kirby :: Part of the problem since 1976 :: http://badcomputer.org
“…the number of UNIX installations has grown to 10, with more
expected…”

  • Dennis Ritchie and Ken Thompson, June 1972


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

#3

darren kirby wrote:

Hello all,

So you can see I need to explicitly call each attribute before my array gets
the value. How can I change this so the array is populated automatically when
called?

Add code to to_a to populate the instance variables if they are not et
assigned, or have the initializer assign the instance variables when an
object is created.

Calling a method will only do what the method code is set up to do.


James B.

“Blanket statements are over-rated”


#4

quoth the Robert D.:

On 6/6/06, Robert D. removed_email_address@domain.invalid wrote:

which will access the hash only once.

I tried this and it is giving me the default tags (from the super class)
rather than using ‘ogginfo’?! So is putting them in initialize() (see my
response to James B…


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

-d


#5

On 6/6/06, Robert D. removed_email_address@domain.invalid wrote:

file

when
@tags = OggInfo.new(@filename)
def genre
end
consideration,

No the programmatic behavior is clear, I fail to see the reason though,
but there might be one.
As a matter of fact there is a good reason to cache the value of the
attribute in the reader but that would be done like this

Sorry slipped from a key, continuing…

@year ||= @tags.tag[‘date’]

which will access the hash only once.

Hope that helped
Robert

-d


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

#6

On Wed, 7 Jun 2006, darren kirby wrote:

def to_a
["#@title", “#@artist”, “#@album”, “#@genre”, “#@year”, “#@comment”,
“#@tracknum”]
end
end

ATTRIBUTES = %w( title artist album genre year comment )

def to_a
ATTRIBUTES.map{|a| send a}
end

you are using un-initialized instance vars - your methods are lazy
initializers, you need to use them here.

-a


#7

quoth the James B.:

darren kirby wrote:

Hello all,

So you can see I need to explicitly call each attribute before my array
gets the value. How can I change this so the array is populated
automatically when called?

Add code to to_a to populate the instance variables if they are not et
assigned, or have the initializer assign the instance variables when an
object is created.

Ok, so I changed the code so the assignments are in initialize(). Now I
am
getting some broken behavior. First of all, most of the tags are getting
set
by the super class which is only supposed to happen if the tag is
‘comment’:

irb> x = OggMetaTags.new(’/home/music/e/elliottSmith-shootingStar.ogg’)
=> # x.to_a
=> ["/home/music/e/elliottSmith-shootingStar",
“/home/music/e/elliottSmith-shootingStar”, “unknown”, “unknown”,
“unknown”,
“unknown”, “12”]

So here the only tag set correctly is the tracknumber “12”. The rest
were set
by the super class except for ‘comment’ which is supposed to be
‘Starbellied
using sneetchalizer’ but is instead ‘unknown’?! I am completely lost as
to
what is going on here.

I suppose I better list the super class as well:

class MetaTags
attr_reader :title, :artist, :album, :genre, :year, :comment,
:tracknum
def initialize(filename)
@filename = filename
@title = @filename[0…-5]
@artist = @title
@album = ‘unknown’
@genre = ‘unknown’
@year = ‘unknown’
@comment = ‘Starbellied using sneetchalizer’
@tracknum = ‘unknown’
end
def to_s
puts “Title: #@title
puts “Artist: #@artist
puts “Album: #@album
puts “Genre: #@genre
puts “Year: #@year
puts “Comment: #@comment
puts “Tracknum: #@tracknum
end
def to_a
["#@title", “#@artist”, “#@album”, “#@genre”, “#@year”, “#@comment”,
“#@tracknum”]
end
end

class OggMetaTags < MetaTags
attr_reader :title, :artist, :album, :genre, :year, :comment,
:tracknum
def initialize(filename)
@filename = filename
require ‘ogginfo’
@tags = OggInfo.new(@filename)
@title = @tags.tag[‘title’]
@artist = @tags.tag[‘artist’]
@album = @tags.tag[‘album’]
@genre = @tags.tag[‘genre’]
@year = @tags.tag[‘date’]
@comment = super
@tracknum ||= @tags.tag[‘tracknumber’]
end
end

Calling a method will only do what the method code is set up to do.
Understood! About the only thing I understand now…

Thanks for the responses guys,
-d


#8

quoth the removed_email_address@domain.invalid:

 ATTRIBUTES.map{|a| send a}

end

Thank you!
This worked perfectly…

you are using un-initialized instance vars - your methods are lazy
initializers, you need to use them here.

-a

Thanks again,
-d


#9

quoth the Robert D.:

but there might be one.
I tried this and it is giving me the default tags (from the super class)
rather than using ‘ogginfo’?! So is putting them in initialize() (see my
response to James B…

That is strange, you mean @year is not initialized when calling to_a, but
it is when
calling the accessor method first?

Well, that is what I was seeing before. After trying your code it is
getting
set to ‘unknown’ which is what the super class assigns to @year


#10

On 6/6/06, darren kirby removed_email_address@domain.invalid wrote:

Sorry slipped from a key, continuing…

@year ||= @tags.tag[‘date’]

which will access the hash only once.

I tried this and it is giving me the default tags (from the super class)
rather than using ‘ogginfo’?! So is putting them in initialize() (see my
response to James B…

That is strange, you mean @year is not initialized when calling to_a,
but it
is when
calling the accessor method first?

Anyway that might not be what you are interested in, the caching
mechanism
is maybe not
what you wanted either, I was just guessing about semantics :wink:

Cheers
Robert

  • Dennis Ritchie and Ken Thompson, June 1972
    “…the number of UNIX installations has grown to 10, with more
    expected…”
  • Dennis Ritchie and Ken Thompson, June 1972


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

#11

Ok, so I changed the code so the assignments are in
initialize(). Now I am
getting some broken behavior. First of all, most of the tags
are getting set
by the super class which is only supposed to happen if the
tag is ‘comment’:

@comment = super

That line calls the initialize() method from your superclass and assigns
the result of that method (coming from the last line “@tracknum =
‘unknown’”) to the @comment variable.

What you probably want is:

module TagDisplayer
ATTRIBUTES = %w{title artist album genre year comment tracknum}
attr_reader *ATTRIBUTES
def to_a
ATTRIBUTES.map {|x| send(x)}
end
def to_s
ATTRIBUTES.map {|x| “#{x.capitalize}: #{send(x)}”
}.join("\n")
end
end

class MetaTags
include TagDisplayer
def initialize(filename)
@filename = filename
@title = @filename[0…-5]
@artist = @title
@album = ‘unknown’
@genre = ‘unknown’
@year = ‘unknown’
@comment = ‘Starbellied using sneetchalizer’
@tracknum = ‘unknown’
end
end

class OggMetaTags < MetaTags
include TagDisplayer
def initialize(filename)
@filename = filename
require ‘ogginfo’
@tags = OggInfo.new(@filename)
@title = @tags.tag[‘title’]
@artist = @tags.tag[‘artist’]
@album = @tags.tag[‘album’]
@genre = @tags.tag[‘genre’]
@year = @tags.tag[‘date’]
@comment = ‘Starbellied using sneetchalizer’
@tracknum = @tags.tag[‘tracknumber’]
end
end


#12

quoth the Daniel S.:

That line calls the initialize() method from your superclass and assigns
the result of that method (coming from the last line “@tracknum =
‘unknown’”) to the @comment variable.

Why does it do this? I thought it would run the ‘comment’ method in the
superclass. That is what I was trying to do anyway. In the bigger
picture, I
have this:

MetaTags
OggMetaTags
Mp3MetaTags
M4aMetaTags

I have a little wrapper function that checks the filetype you pass to
it, then
dispatches the appropriate tag handling class to return the tags for
use. If
the file is not an ogg, m4a, or mp3 then the defaults should be created
using
MetaTags. I am trying to make this as transparent as possible. ie: you
pass
it a filename, and it returns an array of tags, doing the right thing
internally.

What you probably want is:

Can you define a module in the same file as where you call it? I am not
sure
why you would do it this way instead of simply adding to_a and to_s in
MetaTags? Can you explain why you suggest to do it this way? Please
remember
I am just learning Ruby and I do appreciate a bit of explainaition so I
might
learn some good style…

end
@comment = ‘Starbellied using sneetchalizer’
@title = @tags.tag[‘title’]
@artist = @tags.tag[‘artist’]
@album = @tags.tag[‘album’]
@genre = @tags.tag[‘genre’]
@year = @tags.tag[‘date’]
@comment = ‘Starbellied using sneetchalizer’
@tracknum = @tags.tag[‘tracknumber’]
end
end

Thanks for the help Daniel, and everybody else. It is appreciated.
-d


#13

add a call to super in your subclass initialize to get the defaults from
the superclass

Then only assign to attributes where the values differ from the defaults

Cheers
Chris