- Gain a deeper understanding of object relations.
- Build classes that interact with one another through associations and behavior.
This is a test-driven lab. Use the test file and test output to understand what is being asked of you as you follow the guidelines below.
In this lab, we'll be dealing with an Artist
class, a Song
class, and an
MP3Importer
class.
Because of the relationships between your classes, the Artist
tests, i.e. the
tests in spec/artist_spec.rb
, rely on some code in the Song
class and vice
versa. So, as you proceed through solving this lab, you will go back and forth
between coding in Artist
and Song
.
We recommend starting out by getting some of the initial Artist
tests passing
and switching to write code in the Song
class as directed by the test output.
Because of the complexity, let's walk through a guideline of how to think about
the problem as you're building out the solution.
You will be building an Artist
class, a Song
class, and an MP3Importer
class. At the top level, you can think about what we'll be doing in 3 steps:
- The
MP3Importer
class will parse all the filenames in thespec/fixtures
folder and send the filenames to the Song class - The
Song
class will be responsible for creating songs given each filename and sending the artist's name (a string) to the Artist class - The
Artist
class will be responsible for either creating the artist (if the artist doesn't exist in our program yet) or finding the instance of that artist (if the artist does exist).
Thinking about it this way will get us started. From this breakdown, it looks
like the MP3Importer
relies on Song
in order to do its job of parsing
filenames. Song
relies on the Artist
instances to build associations.
Therefore, we'll start by working on Artist
. Keep in mind though, that you
will need to build out related classes together in order to pass some tests, as
they work in collaboration.
An Artist
should be initialized with a name and should have an attribute
accessor for this name. The class should have an @@all
class variable and
store all Artist
instances in this variable.
This class method should return all Artist
instances.
This instance method exists to tell a passed in Song
instance it belongs to
this Artist
instance.
This instance method returns an Array of all songs that belong to this Artist
instance. This method should get all existing Song
instances from Song
and
select only the ones that are associated with this Artist
instance.
This class method should take the name that is passed in (remember, it will be a string), and do one of two things: find the artist instance that has that name or create one if it doesn't exist. Either way, the return value of the method will be an instance of an artist with the name attribute filled out.
This instance methods outputs the names of all songs associated with this
Artist
instance.
A Song
should be initialized with a name and have attribute accessors for
name and artist. The Song
class should have an @@all
class variable
and store all Song
instances in this variable.
This class method should return all Song
instances.
This method will do four things:
-
It must parse a filename to find the song name and artist name. (Hint: every file separates the song and artist with a
" - "
). -
From here, we will create a new song instance using the song name we gathered from the filename.
-
We'll also want to associate that new song with the appropriate artist. Use the
artist
attribute accessor to assign this -
Return the new song instance.
You may have seen something similar in the past where we have the instance of
the artist. If we had the artist object, we could simply assign the artist to
the song with some code that looks like this: our_song_instance.artist = our_artist_instance
. Since we only have the artist name as a string (not an
instance of the Artist class), we'll create a method that takes in the name and
gets the artist object. Let's call this Song#artist_name=
.
This method will do two things. Both of these things will involve collaboration
with the Artist
class:
-
Turn the artist's name as a string into an
Artist
object.First we need to get the instance of the
Artist
class that represents that artist. There are two possibilities here:- Either we have to create that
Artist
instance, or - it already exists and we have to find that
Artist
instance.
To achieve this, we'll need to collaborate with the
Artist
class. We want to send an artist's name (a string — remember we are getting this from the parsed filename) to theArtist
class and either create or find the artist instance. This sounds like a great place to use methodArtist.find_or_create_by_name
. - Either we have to create that
-
Assign the song to the artist. Now that we have the artist instance, we'll want to again collaborate with the
Artist
class by calling on theArtist#add_song(some_song)
method.
Build an MP3Importer
class that:
-
Has a
path
attribute that gets set on initialization. -
Contains a
#files
method that parses the files in thepath
, returning an array that contains the file names. Make sure you only get.mp3
files. Hint: Google around for how to get a list of files in a directory! You may want to look at the documentation for Ruby's built-in Dir class. -
Contains an
#import
method that sends the song names to theSong
class. Specifically, the method should callSong.new_by_filename
, which will handle the creation ofSong
instances and their associatedArtist
instances.
These are just a few hints and guidelines to help you through this lab. Rely on the guides here, refer to the previous lessons on object relations, and read the test output and test files. Never forget to ask a question if you are stuck. Good luck!