Forum: Ruby Attributes not populated until called?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
0cb023654f46a6bd171405af6419baf6?d=identicon&s=25 darren kirby (Guest)
on 2006-06-06 22:28
(Received via mailing list)
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
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2006-06-06 22:47
(Received via mailing list)
On 6/6/06, darren kirby <bulliver@badcomputer.org> 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
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2006-06-06 22:50
(Received via mailing list)
On 6/6/06, Robert Dober <robert.dober@gmail.com> 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
Bc6d88907ce09158581fbb9b469a35a3?d=identicon&s=25 James Britt (Guest)
on 2006-06-06 22:56
(Received via mailing list)
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 Britt

"Blanket statements are over-rated"
0cb023654f46a6bd171405af6419baf6?d=identicon&s=25 darren kirby (Guest)
on 2006-06-06 23:14
(Received via mailing list)
quoth the Robert Dober:
> On 6/6/06, Robert Dober <robert.dober@gmail.com> wrote:
<snip>
>
> 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 Britt...

> >
> > --
> > 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
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-06-06 23:21
(Received via mailing list)
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
0cb023654f46a6bd171405af6419baf6?d=identicon&s=25 darren kirby (Guest)
on 2006-06-06 23:30
(Received via mailing list)
quoth the James Britt:
> 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')
=> #<OggMetaTags etc etc
irb> 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
0cb023654f46a6bd171405af6419baf6?d=identicon&s=25 darren kirby (Guest)
on 2006-06-06 23:39
(Received via mailing list)
quoth the ara.t.howard@noaa.gov:
>      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
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2006-06-06 23:42
(Received via mailing list)
On 6/6/06, darren kirby <bulliver@badcomputer.org> 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 Britt...


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 ;)

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
0cb023654f46a6bd171405af6419baf6?d=identicon&s=25 darren kirby (Guest)
on 2006-06-06 23:46
(Received via mailing list)
quoth the Robert Dober:
> > > > 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 Britt...
>
> 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...
1c1e3bdfe006a22214102fcd6434a012?d=identicon&s=25 Daniel Sheppard (Guest)
on 2006-06-07 04:37
(Received via mailing list)
> 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
23172b6630dc631a134c9bad2fec2a39?d=identicon&s=25 Chris (Guest)
on 2006-06-07 18:32
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
0cb023654f46a6bd171405af6419baf6?d=identicon&s=25 darren kirby (Guest)
on 2006-06-07 21:19
(Received via mailing list)
quoth the Daniel Sheppard:
>
> 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
This topic is locked and can not be replied to.