Unable to extract information from a file - ruby

My program is designed to intake the "Album title", "Album artist", "Album Genre", and "tracks" from a .txt file. It puts these tracks into an array and then prints that array to the terminal.
Everything works fine, except the array that is being printed is empty "[]"
The Ruby Code
module Genre
POP, CLASSIC, JAZZ, ROCK = *1..4
end
$genre_names = ['Null', 'Pop', 'Classic', 'Jazz', 'Rock']
class Album
# NB: you will need to add tracks to the following and the initialize()
attr_accessor :title, :artist, :genre, :tracks
# complete the missing code:
def initialize (title, artist, genre, tracks)
#title = title
#artist = artist
#genre = genre
#tracks = tracks
end
end
class Track
attr_accessor :name, :location
def initialize (name, location)
#name = name
#location = location
end
end
# Returns an array of tracks read from the given file
def read_tracks(music_file)
tracks = Array.new
count = music_file.gets().to_i
index = 0
while index < count
track = read_track(music_file)
tracks << track
index = index + 1
end
tracks
end
# Reads in and returns a single track from the given file
def read_track(music_file)
track_name = music_file.gets
track_location = music_file.gets
track = Track.new(track_name, track_location)
end
# Takes an array of tracks and prints them to the terminal
def print_tracks(tracks)
index = 0
while (index < tracks.length)
puts 'Track Number ' + index.to_s + ' is:'
print_track(tracks[index])
index = index + 1
end
tracks
end
# Reads in and returns a single album from the given file, with all its tracks
def read_album(music_file)
# read in all the Album's fields/attributes including all the tracks
# complete the missing code
album_artist = music_file.gets
album_title = music_file.gets
album_genre = music_file.gets
tracks = read_tracks(music_file)
album = Album.new(album_title, album_artist, album_genre, tracks)
album
end
# Takes a single album and prints it to the terminal along with all its tracks
def print_album(album, tracks)
# print out all the albums fields/attributes
# Complete the missing code.
puts 'Album title is ' + album.title.to_s
puts 'Album artist is ' + album.artist.to_s
puts 'Genre is ' + album.genre.to_s
puts $genre_names[album.genre.to_i]
# print out the tracks
puts 'Tracks are: ' + print_tracks(tracks).to_s
end
# Takes a single track and prints it to the terminal
def print_track(tracks)
tracks.each do |track|
puts('Track title is: ' + track.name.to_s)
puts('Track file location is: ' + track.location.to_s)
end
end
# Reads in an album from a file and then print the album to the terminal
def main
music_file = File.new("album.txt", "r")
if music_file
album = read_album(music_file)
tracks = read_tracks(music_file)
music_file.close
else
puts "unable to read"
end
print_album(album, tracks)
end
main
This is what the .txt file contains
Neil Diamond
Greatest Hits
1
3
Crackling Rose
sounds/01-Cracklin-rose.wav
Soolaimon
sounds/06-Soolaimon.wav
Sweet Caroline
sounds/20-Sweet_Caroline.wav
I receive an ouput of:
Album title is Greatest Hits
Album artist is Neil Diamond
Genre is 1
Pop
Tracks are: []
when the tracks array should print out the different tracks, their name and their file location

Here your solution))) It was a few little mistakes. Firstly, you had empty tracks because you read the end of the file. You have been called read_tracks the first time in read_album and the second time in main.
Also, you have an exception when you in print_track tried to call the loop on Track object.
module Genre
POP, CLASSIC, JAZZ, ROCK = *1..4
end
$genre_names = ['Null', 'Pop', 'Classic', 'Jazz', 'Rock']
class Album
# NB: you will need to add tracks to the following and the initialize()
attr_accessor :title, :artist, :genre, :tracks
# complete the missing code:
def initialize (title, artist, genre)
#title = title
#artist = artist
#genre = genre
end
end
class Track
attr_accessor :name, :location
def initialize (name, location)
#name = name
#location = location
end
end
# Returns an array of tracks read from the given file
def read_tracks(music_file)
tracks = Array.new
count = music_file.gets.to_i
index = 0
while index < count
tracks << read_track(music_file)
index += 1
end
tracks
end
# Reads in and returns a single track from the given file
def read_track(music_file)
track_name = music_file.gets
track_location = music_file.gets
track = Track.new(track_name, track_location)
end
# Takes an array of tracks and prints them to the terminal
def print_tracks(tracks)
index = 0
while (index < tracks.length)
puts 'Track Number ' + index.to_s + ' is:'
print_track(tracks[index])
index += 1
end
tracks
end
# Reads in and returns a single album from the given file, with all its tracks
def read_album(music_file)
# read in all the Album's fields/attributes including all the tracks
# complete the missing code
album_artist = music_file.gets
album_title = music_file.gets
album_genre = music_file.gets
album = Album.new(album_title, album_artist, album_genre)
album
end
# Takes a single album and prints it to the terminal along with all its tracks
def print_album(album)
# print out all the albums fields/attributes
# Complete the missing code.
puts 'Album title is ' + album.title.to_s
puts 'Album artist is ' + album.artist.to_s
puts 'Genre is ' + album.genre.to_s
puts $genre_names[album.genre.to_i]
# print out the tracks
puts 'Tracks are: ' + print_tracks(album.tracks).to_s
end
# Takes a single track and prints it to the terminal
def print_track(track)
puts('Track title is: ' + track.name.to_s)
puts('Track file location is: ' + track.location.to_s)
end
# Reads in an album from a file and then print the album to the terminal
def main
music_file = File.new("album.txt", "r")
if music_file
album = read_album(music_file)
album.tracks = read_tracks(music_file)
music_file.close
else
puts "unable to read"
end
print_album(album)
end
main

Related

Ruby input failed

here is my code. I've encountered the bug that said: test.rb:101:in <main>': undefined method location' for nil:NilClass (NoMethodError)
although i've defined the method tracks above. I've tried to debug and got the result that nothing was pushed into the tracks variable. Please help me in this stage.
puts albums[0].tracks[0].location
`
class Album
attr_accessor :author, :name, :year, :genre, :cover, :count, :tracks
def initialize (author, name, year, genre, cover, count, tracks)
#author = author
#name = name
#year = year
#genre = genre
#cover = cover
#count = count
#tracks = tracks
end
end
class Track
attr_accessor :name, :location
def initialize (name, location)
#name = name
#location = location
end
end
def read_track(pineapple_music)
song = pineapple_music.gets
file = pineapple_music.gets
t = Track.new(song, file)
return t
end
def read_tracks(pineapple_music, count)
tracks = Array.new
trackcount = count
while trackcount < count
track = read_track(pineapple_music)
tracks << track
trackcount += 1
end
return tracks
end
def read_album(music_file)
album_author = music_file.gets.chomp
album_name = music_file.gets.chomp
album_year = music_file.gets.chomp.to_i
album_genre = music_file.gets.chomp.to_i
cover = music_file.gets.chomp
count = music_file.gets.chomp.to_i
tracks = read_tracks(music_file, count)
album = Album.new(album_author, album_name, album_year, album_genre, cover, count, tracks)
return album
end
def read_albums(pineapple_music)
count = pineapple_music.gets.to_i
albums = Array.new
albumcount = 0
while albumcount < count
album = read_album(pineapple_music)
albums << album
albumcount += 1
end
return albums
end
def read_file(pineapple_music)
music_file = File.new(pineapple_music, "r")
albums = read_albums(music_file)
music_file.close()
return albums
end
def read_number_albums(pineapple_music)
music_file = File.new(pineapple_music, "r")
count = music_file.gets.to_i
music_file.close()
return count
end
`
I've tried some methods like try to output in every function to see what happened. I'm expecting the file name in the library to be loaded in the program, but nothing was loaded and the output was nil afterall.

Undefined method 'name' for []: Array (NoMethodError)

I am creating a program that reads tracks and albums from a given file. So I have created the following code.
# Task 6.1 T - use the code from last week's tasks to complete this:
# eg: 5.1T, 5.2T
module Genre
POP, CLASSIC, JAZZ, ROCK = *1..4
end
$genre_names = ['Null', 'Pop', 'Classic', 'Jazz', 'Rock']
class Album
# NB: you will need to add tracks to the following and the initialize()
attr_accessor :title, :artist, :genre, :tracks
# complete the missing code:
def initialize (title, artist, genre, tracks)
#title = title
#artist = artist
#genre = genre
#tracks = tracks
end
end
class Track
attr_accessor :name, :location
def initialize (name, location)
#name = name
#location = location
end
end
# Returns an array of tracks read from the given file
def read_tracks (music_file)
tracks = Array.new
count = music_file.gets().to_i
index = 0
while index < count
track = read_track(music_file)
tracks << track
index = index + 1
end
tracks
end
# Reads in and returns a single track from the given file
def read_track (music_file)
track_name = music_file.gets
track_location = music_file.gets
track = Track.new(track_name, track_location)
end
# Takes an array of tracks and prints them to the terminal
def print_tracks (tracks)
index = 0
while (index < tracks.length)
puts 'Track Number ' + index.to_s + ' is:'
print_track(tracks[index])
index = index + 1
tracks
end
end
# Reads in and returns a single album from the given file, with all its tracks
def read_album (music_file)
# read in all the Album's fields/attributes including all the tracks
# complete the missing code
album_artist = music_file.gets
album_title = music_file.gets
album_genre = music_file.gets
tracks = music_file.gets
album = Album.new(album_title, album_artist, album_genre, tracks)
album
end
# Takes a single album and prints it to the terminal along with all its tracks
def print_album (album, tracks)
# print out all the albums fields/attributes
# Complete the missing code.
puts 'Album title is ' + album.title.to_s
puts 'Album artist is ' + album.artist.to_s
puts 'Genre is ' + album.genre.to_s
puts $genre_names[album.genre.to_i]
# print out the tracks
puts 'Tracks are ' + print_track(tracks).to_s
end
# Takes a single track and prints it to the terminal
def print_track (track)
# This is the line where the error is directing me
puts('Track title is: ' + track.name.to_s)
puts('Track file location is: ' + track.location.to_s)
end
# Reads in an album from a file and then print the album to the terminal
def main
music_file = File.new("album.txt", "r")
album = read_album(music_file)
tracks = read_tracks(music_file)
read_tracks(music_file)
print_album(album, tracks)
print_tracks(tracks)
end
main
The program is supposed to read the tracks from the given file, yet I am given the error:
C:/Users/Harry/Desktop/6.1T/album_file_handling.rb:104:in `print_track': undefined method `name' for []:Array (NoMethodError)
Why is this happening?
Five lines above you pass an array of tracks to the method print_track:
puts 'Tracks are ' + print_track(tracks).to_s
And inside the method itself, you expect the single track there. You need to iterate over tracks and print each of them. Somewhat like this would do:
def print_track(tracks)
tracks.each do |track|
puts('Track title is: ' + track.name.to_s)
puts('Track file location is: ' + track.location.to_s)
end
end
Sidenote: never ever put a space between a method declaration/call and the parenthesis opening the list of arguments in ruby. It might result in unpredicted errors.
The problem is you are passing an array(tracks) to the print_track in print_album def.
def print_album (album, tracks)
puts 'Album title is ' + album.title.to_s
puts 'Album artist is ' + album.artist.to_s
puts 'Genre is ' + album.genre.to_s
puts $genre_names[album.genre.to_i]
tracks.each do |track|
puts 'Tracks are '
print_track(track).to_s
end
end

Using a variable across classes in Ruby

I have this class:
class Player
attr_accessor :card_pile, :name
def initialize
#name = name
#bust = false
#card_pile = []
end
def bust?
return #cards.inject(:+) > 21
end
end
I also have this as the beginning of another class
def playing_game
puts "How many players are playing? "
players_amount = gets.chomp.to_i
(0...players_amount).each do
puts ("What is the players name? ")
#name = gets.chomp
#players.push(#name)
end
#players.each do |each_player|
#name = Player.new
while true
while #name.card_pile.length < 2 do
new_card = Card.new
#name.card_pile.push(new_card.value)
end
puts(#name.card_pile)
print #name, "'s turn" "\n"
At the moment this will print out #<Player:0x007fc14984a4b0>'s turn instead of Rich's turn
Why is this happening? I thought I had made an instance variable in the Player class and then instantiated this class #name = Player.new and then could reference it from here on out??
This will help
def playing_game
puts 'How many players are playing?'
players_amount = gets.chomp.to_i
players_names = (0...players_amount).map do
puts ("What is the players name? ")
gets.chomp
end
players_names.each do |player_name|
player = Player.new(player_name)
while player.card_pile.length < 2 do
new_card = Card.new
player.card_pile.push(new_card.value)
end
puts player.card_pile
puts "#{player.name}'s turn"
end
end
UPD:
You don't need instance variables (like #name and #players inside single method).
In this code you iterate over players names
#players.each do |each_player|
=>
players_names.each do |player_name|
In context of
#name = Player.new
name is a Player instance
to create player with given name pass it to initializer:
player = Player.new(player_name)
then call name on Player instance, that you create earlier
puts "#{player.name}'s turn"

create an adjustable playlist with ruby

I'm not sure why my code will not pass the corresponding test. Each time I try the code, the following error is reported: "rb:60:in <main>': undefined local variable or methodtrack' for main:Object (NameError)." What can I do without editing the tests? Thanks! Open to another approach...thanks!
strong text
class Song
attr_reader :song
def initialize(song, artist)
#song = song
#artist = artist
end
def play
puts "#{#song}by #{#artist}"
end
end
class Playlist
def initialize(player_list)
#player_list = player_list
end
def add(add_song)
add_song.each do |song|
#player_list << song
end
end
def track_number
#player_list.length
end
def remove(remove_song)
remove_song.each do |song|
#player_list.delete(song)
end
end
def includes?(from_list)
i = 0
from_list.each do |song|
if #player_list.include?(song)
i+=1
end
end
if i==from_list.length
true
else
false
end
end
def play_all
#player_list.each do |song|
song.play
end
end
def display
#player_list.each do |song|
puts song.song
end
end
end
one_by_one = Song.new("One by One", "Sirenia")
world_so_cold = Song.new("World So Cold", "Three Days Grace")
going_under = Song.new("Going Under", "Evanescence")
my_playlist = Playlist.new(one_by_one, world_so_cold, going_under)
lying_from_you = Song.new("Lying From You", "Linkin Park")
angels = Song.new("Angels", "Within Temptation")
my_playlist.add(lying_from_you, angels)
p my_playlist.track_number == 5
going_under.play
my_playlist.remove(angels)
p my_playlist.includes?(lying_from_you) == true
my_playlist.play_all
my_playlist.display
When you call the "add" method for PlayList, it is expecting a single Song. However, the "add" method is trying to call .each() which would work for an array of Songs but not for a single song.
The best solution, without changing your test code, would be to remove the ".each" calls in the "add", "remove" and "includes?" methods for PlayList. Have them each add, remove or check for a single song at a time.

instantiation and collections - ruby

I am a newb and struggling with making a test pass. I have 3 classes, artists, songs, and genres. The test I am trying to make pass is below:
test 'A genre has many artists' do
genre = Genre.new.tap{|g| g.name = 'rap'}
[1,2].each do
artist = Artist.new
song = Song.new
song.genre = genre
artist.add_song(song)
end
assert_equal genre.artists.count, 2
end
This is my artist class, the add_song method is the one I need to tweak. When a song is added to an artist I am trying to instantiate a new Genre object and add the artist to that genre as well. Currently not working though, when I call genre.artists it returns an empty array.
class Artist
attr_accessor :name, :songs, :genres, :genre, :artists
##artists = []
def initialize(name = name, genre = genre)
#artists = []
#songs = []
#genre = genre
#genres = []
#name = name
##artists << self
end
def self.all
##artists
end
def self.reset_artists
##artists = []
end
def self.count
self.all.size
end
def songs_count
self.songs.size
end
def count
self.size
end
def add_song(song)
#songs << song
#genres << song.genre
Genre.new(self)
end
end
class Genre
attr_accessor :name, :songs, :artists
##genres = []
def initialize(artists = artists)
#songs = []
#artists = artists
#name = name
##genres << self
end
def count
self.artists.count
end
def self.all
##genres
end
def self.reset_genres
##genre = []
end
end
class Song
attr_accessor :name, :genre, :artist
def initialize(name = name, artist = artist, genre = genre)
#name = name
#artist = artist
#genre = genre
end
end
When you create a new artist, you add it to Artist::artists - a class variable of Artist. The array you test is genre.artists - an object variable of Genre. That's a different variable from Artist::artists, and I don't see you updating genre.artists anywhere in your code - I'm surprised it's even an array, seeing that you don't initialize it to an array...
You are returning a creating a new instance of Genre with the current artist in the add_song method. You could get your test to pass in a couple of ways.
This will add an artist to the genre referenced in the song instance.
def add_song(song)
#songs << song
#genres << song.genre
song.genre.artists << self
end
The second option would be fix your test if you want to return a new Genre instance from your add_song method. This is most likely not what you want, however this would set artist the reference in Genre.
test 'A genre has many artists' do
genre = Genre.new.tap{|g| g.name = 'rap'}
[1,2].each do
art ist = Artist.new
song = Song.new
song.genre = genre
genre = artist.add_song(song)
end
assert_equal genre.artists.count, 2
end

Resources