Giter Site home page Giter Site logo

ruby-music-library-cli's Introduction

Ruby Music Library

Overview

You're going to be implementing a Music Library domain composed of 3 main models, Song, Artist, and Genre. The models will relate to each other and collaborate heavily. Additionally, you're going to be extracting some common functionality out of those models and into a module, Concerns::Findable, which you'll then mix back into the models. You'll then build a collaborating object, MusicImporter, that can parse a directory of MP3 files and use the extracted filenames to create instances of Song, Artist, and Genre objects. Finally, you'll build a CLI in bin/musiclibrary that is powered by a MusicLibraryController to provide a simple CLI that lets a user browse the library of MP3s imported by song, artist, and genre.

This is a complex lab with many parts, so go slowly. Take time to understand what you're building holistically before starting. Read this entire README before jumping in. As you go from spec to spec, we recommend doing them in numbered order.

Instructions

Song, Artist, and Genre basics

The first thing to do is get the basics of the main models working. Each model has almost the exact same basic requirements, so once you make 001_song_basics_spec.rb pass by building the Song class, the basic Artist and Genre specs will go quickly.

The requirements for each model are that they can accept a name upon initialization and set that property correctly. The name property should be readable and writable by the object.

Song.new("Blank Space").name #=> "Blank Space"`

Additionally, each class should contain a class variable @@all that is set to an empty array and is prepared to store all saved instances of the class. This class variable should be accessible via the class method .all.

Song.all #=> []

Artist.all #=> []

Instances should respond to a #save method that adds the instance itself into the appropriate @@all class variable.

Song.new("Blank Space").save
Song.all #=> [#<Song: @name="Blank Space">]

The class should be able to empty its @@all array via a class method .destroy_all.

Song.new("Kaohsiung Christmas").save
Song.all #=> [#<Song: @name="Blank Space">, #<Song: @name="Kaohsiung Christmas">]
Song.destroy_all
Song.all #=> []

Finally, all classes should implement a custom constructor .create that instantiates an instance using .new but also invokes #save on that instance, forcing it to persist immediately.

Song.new("Blank Space")
Song.all #=> []
Song.create("Blank Space")
Song.all #=> [#<Song: @name="Blank Space">]

Relationships

Songs and Artists

  • Songs belong to an artist and an artist has many songs. Adding a song to an artist is done by calling an #add_song method on an instance of the Artist class.
  • Songs can be initialized with an optional artist argument.

Songs and Genres

  • Genres have many songs and are initialized with an empty list of songs.
  • Songs have one genre.
  • Songs can be initialized with an optional genre argument.

Artists and Genres

  • Artists have many genres through their songs. Implement a #genres method for this association.
  • Genres have many artists through their songs. Implement an #artists method for this association.

Note: there are a few tests concerned with switching the Song#initialize method from setting instance variables for @artist and @genre to using the custom setter methods that you define (e.g., Song#genre=). We want to use the custom setter methods because they keep our associations in sync. For example, when we call our custom Song#artist= method, it sets the song's @artist property and adds the song to the artist's collection of songs. When you reach these tests, make sure those setter methods are only invoked if Song#initialize is called with artist and/or genre arguments. Otherwise, the @artist and/or @genre properties will be initialized as nil, and you'll have some unexpected consequences in both your code and the test suite.

  • If we call Song.new("Song Title", artist_object, genre_object), both Song#artist= and Song#genre= should be invoked.
  • If we call Song.new("This Song Has No Artist or Genre"), neither Song#artist= nor Song#genre= should be invoked.

Finding

Song

First implement the following two methods in your Song class:

  • Songs should have a find_by_name method.
  • Songs should have a find_or_create_by_name method.

Concerns::Findable

Now that you've gotten the methods working in Song, let's adapt them for general reuse by putting them into a module that we can mix into our Artist and Genre classes. It's Ruby convention to put modules in a concerns/ folder nested under lib/, and each module should be namespaced like this:

module Concerns::ModuleName
  # Module code here
end

Once the basic module structure is good to go, it's time to code our two class methods again:

  • Implement a generic #find_by_name method that uses the .all method defined by the class to find an instance of the class by name.
  • Implement a generic #find_or_create_by_name method that uses the .all method defined by the class to find an instance of the class by name and to create a new instance if a match is not found.
  • Add this module to your Genre and Artist class.

MusicImporter

Create a MusicImporter class that works with your Song, Genre, and Artist objects to import a directory of MP3 files. This class will have the following methods:

  • #initialize accepts a file path to a directory of MP3 files.
  • #files returns all of the imported filenames.
  • .import imports all of the files from the library, instantiating a new Song object for each file.

In addition, add the following pair of methods to your Song class:

  • .new_from_filename, which instantiates a new Song object based on a provided filename.
  • .create_from_filename, which does the same thing as .new_from_filename but also saves the newly-created song to the @@all class variable.

It's CLI time!

Congrats! You've done the heavy lifting. Now let's wrap it all up in a simple CLI so that users can actually interact with our code. Create a MusicLibraryController class that:

  • Upon initialization, accepts an optional path to the library of MP3 files, defaulting to ./db/mp3s. It should then instantiate a MusicImporter object, which it will use to import songs from the specified library.
  • Has a #call method that starts the CLI and prompts the user for input (try using gets.chomp). Read the tests carefully for specifics.

When you've passed all of the tests, you should be able to type ./bin/musiclibrary into your terminal to play around with your CLI and see how it works.

Have fun!

ruby-music-library-cli's People

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ruby-music-library-cli's Issues

Weak tests

The tests for this lab only create 1 song. This creates the opportunity for LOTS of false positives and should be changed to include at least like ..... 3 times as many.... ;)

Instrc

Guidance for MusicImporter asks us to create a ".import" method on the MusicImporter class but the test suite sends "#import" to a class instance.

Module Findable in ruby music library is not getting Loaded

I followed the instructions outlined in the lessons/labs and the ones in the books available and online documentations but apparently my Songs/ Artist/Genre class cannot access the methods defined in findable.rb

"2.6.1 :007 > Song.find_by_name("Larry Csonka")
Traceback (most recent call last):
        3: from bin/console:5:in `<main>'
        2: from (irb):7
        1: from (irb):7:in `rescue in irb_binding'
NoMethodError (undefined method `find_by_name' for Song:Class)"

The unit tests that failed are :

// ♥  rspec spec/007_findable_songs_spec.rb:6
Run options: include {:locations=>{"./spec/007_findable_songs_spec.rb"=>[6]}}

Song
  .find_by_name
    finds a song instance in @@all by the name property of the song (FAILED - 1)

Failures:

  1) Song .find_by_name finds a song instance in @@all by the name property of the song
     Failure/Error: expect(Song.find_by_name("In the Aeroplane Over the Sea")).to be(song_two)

     NoMethodError:



Finished in 0.00694 seconds (files took 1.54 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/007_findable_songs_spec.rb:6 # Song .find_by_name finds a song instance in @@all by the name property of the song

[22:37:25]  ruby-music-library-cli-online-web-ft-120919 (master)

Following is my findable .rb

module Concerns::Findable
# to be commented out upon Advice from Charlotte Neff at 11:15 PM 1/12/2019
#
  def find_by_name(name)
    self.all.detect{|s| s.name == name}
  end

  def find_or_create_by_name(name)
    self.find_by_name(name) || self.create(name)
  end
end

Following is my environment.rb:


require 'bundler'
Bundler.require

# Upon Advice from Charlotte Neff at 11:15 PM 1/12/2019 following is inserted into concerns but commented out here
 
module Concerns
=begin
  module Findable
    def find_by_name(name)
      self.all.detect {|object| object.name == name}
    end

    def find_or_create_by_name(name)
      if self.find_by_name(name).nil?
        self.create(name)
      else
        self.find_by_name(name)
      end
    end
  end
=end
end

require_relative '../lib/concerns/findable'

require_all 'lib'

Must create multiple sources of truth to pass tests for Artist#genres and Genres#artist

In order to pass the tests for Artist#genres and Genres#artist in artists_and_genres_spec.rb, we have to create multiple sources of truth -- instead of storing all new instances in the @@ALL array for the Song class.

Huge shoutout to Nick Lunn for helping me with this. Apologies if this isn't best practice for submitting an issue, I just want to ensure other students and technical coaches are aware.

CLI tests expect a loop an input loop `MusicController#call` but doesn't test for it

 Music Library CLI allows a user to list songs
     Failure/Error: expect(music_libray_controller).to receive(:gets).at_least(:once).and_return("list songs", "exit")
       (#<MusicLibraryController:0x007ffcd3c25060>).gets(*(any args))
           expected: at least 2 times with any arguments
           received: 1 time with any arguments

If they don't loop in their call method and only ask for user input once the failure is vague and hard to debug. We should write a test that explicitly asks for an input gets loop within call - something like it 'loops and asks for user input until they type in exit.

Suggestions for improvement - Music Library CLI Lab

Overall I felt that this lab was very good and challenging. It definitely helped reinforce the OOP concepts. However, I have a couple of suggestions that could improve it and bring it "all together" at the end.

  1. After the lab is built and the tests are passing, there is nothing on the Learn page describing about how to interact with the program via the CLI. We spend all of this time building this program, we should be encouraged to play around with it at the end! Something like "once your tests are passing, try executing your program by typing /bin/musiclibrary and enjoy the results of your work!"

  2. In order for the program to respond to commands typed in via the CLI, all of the lines using 'gets' need to be revised to 'gets.chomp'. The lab suggests using 'gets' but it should really suggest 'gets.chomp' so that everything is working once it is built.

Remove solutions

Maybe I got to this too quickly, but just a friendly reminder to remove the solutions. I didn't realize it until I ran rspec to see what the first error would be, but there weren't any, because the file is complete. (and no I won't cheat that would just be cheating myself)

CLI Commands

This material is not covered in the course. We are just expected to magically know this?? Can't even find info on it through a Google search.

CLI Commands
'list songs'
triggers #list_songs (FAILED - 1)
'list artists'
triggers #list_artists (FAILED - 2)
'list genres'
triggers #list_genres (FAILED - 3)
'list artist'
triggers #list_songs_by_artist (FAILED - 4)
'list genre'
triggers #list_songs_by_genre (FAILED - 5)
'play song'
triggers #play_song (FAILED - 6)

"Concerns"

I wasn't aware of this convention -- it feels like a thing that happened in Rails that's a little weird to support.

Unable to past CLI Tests

I've been unable to past the following two test on my end. However I opened a question and my code was cloned from git and passed by an instructor.

Failed examples:

rspec ./spec/012_musiclibrary_cli_spec.rb:3 # Music Library CLI allows a user to list songs
rspec ./spec/012_musiclibrary_cli_spec.rb:44 # Music Library CLI allows a user to play a song

  1. Music Library CLI allows a user to list songs
    Failure/Error: expect(output).to include("1. Action Bronson - Larry Csonka - indie")
    expected "Welcome to Your Music Library!\nWhat would you like to do?\n1. Real Estate - Green Aisles - country\n2. Thundercat - For Love I
    Come - dance\n3. Real Estate - It's Real - hip-hop\n4. Action Bronson - Larry Csonka - indie\nWelcome to Your Music Library!\nWhat would you li
    ke to do?\n" to include "1. Action Bronson - Larry Csonka - indie"
    Diff:
    @@ -1,2 +1,9 @@
    -1. Action Bronson - Larry Csonka - indie
    +Welcome to Your Music Library!
    +What would you like to do?
    +1. Real Estate - Green Aisles - country
    +3. Real Estate - It's Real - hip-hop
    +4. Action Bronson - Larry Csonka - indie
    +Welcome to Your Music Library!
    +What would you like to do?

    ./spec/012_musiclibrary_cli_spec.rb:11:in `block (2 levels) in <top (required)>'

  2. Music Library CLI allows a user to play a song
    Failure/Error: expect(output).to include("Playing Action Bronson - Larry Csonka - indie")
    expected "Welcome to Your Music Library!\nWhat would you like to do?\nWhat song number would you like to play?\nPlaying Real Estate - Gre
    en Aisles - country\nWelcome to Your Music Library!\nWhat would you like to do?\n" to include "Playing Action Bronson - Larry Csonka - indie"
    Diff:
    @@ -1,2 +1,7 @@
    -Playing Action Bronson - Larry Csonka - indie
    +Welcome to Your Music Library!
    +What would you like to do?
    +What song number would you like to play?
    +Playing Real Estate - Green Aisles - country
    +Welcome to Your Music Library!
    +What would you like to do?

class Person

Inside 008_findable_module_spec.rb there's:

class Person
extend Concerns::Findable
end

We haven't worked with a Person class inside this lab. I think it means Genre according to the lesson.

What happened?

Over night my code had a bunch of random numbers inserted and

<<<<<<<<<<<<<<<<<<<<<<<<<<<, HEAD

inserted

one of my three files was deleted

Issue with test suite 012_musiclibrary_cli_spec.rb, spec #1

For 'it allows a user to list songs,' the expected order of songs in the test suite is:

  1. Action Bronson - Larry Csonka - indie
  2. Real Estate - Green Aisles - country
  3. Real Estate - It's Real - hip-hop
  4. Thundercat - For Love I Come - dance

After passing specs in suites 001-011, I failed suite 012 spec 1 because the order of songs in output was:

  1. Thundercat - ...
  2. Action Bronson - ...
  3. Real Estate - Green Aisles -...
  4. Real Estate - It's Real - ..

even though the test files for importing were listed in the spec's order (see screenshot image below: IDE file structure on left shows files in spec order 1, 2, 3, 4; terminal captures rspec failure showing the output order as 4, 1, 2, 3 relative to the file structure). A Learn Expert confirmed (via ask-a-question) that the solution branch fails its own rspec test because of the order of the songs. No suggestions for how to resolve, unfortunately.

image

Suggestion to improve description of conventions on Concerns nomenclature

In this lab, the second paragraph of the README reads:

CONCERNS
A quick note on the placement of Modules. It's Ruby convention to put all Modules in a concerns folder and to be namespaced like this Concerns::ModuleName.

From the Intro to Modules lab (earlier in the OO Ruby sequence), I understood to create a folder under lib called "concerns." What I didn't understand as well were the implications of "namespaced like this Concerns::ModuleName."

Test suite 008 of this lab calls for creating a "findable" module for a couple of methods. Based on my experience in the Intro to Modules lab, I initially had ./lib/concerns/findable.rb with findable.rb's content as
module Findable ( some code here) end

It wasn't easy to figure out that the name of the module needed to be Concerns::Findable (instead of just Findable). My suggestion would be to either include a more explicit example of the nomenclature in the README....or (better?), incorporate this approach to naming in the Intro to Modules lab.

The tests spelling errors and puncutation

The tests in this lesson have several spelling errors. To a lesser extent, punctuation and capitalization also make the errors hard to read in the terminal. Here are two examples:

spec/005_songs_and_genres_spec.rb:5
"initializes with a songs propety set to an empty array"

spec/007_findable_songs_spec.rb:3
"fins a song instance in @@ALL by the"

Discrepancy between readme & test

Readme instructs for an optional path to './db/mp3s/' (line 102)
Test asks for an optional path to './db/mp3s' (line 15, 010_music_library_controller_spec.rb)

Issue with 007_findable_songs_spec.rb

The .find_or_create_by_name spec passes if you define the method but don't have it do anything. The test should be updated so if fails if the method doesn't find or create a song

Modules and CLI Spec

Can someone look into revising the spec expectations for module Findable and the Music Lib CLI spec?

Outstanding issue from 2016: Concerns::Findable does not work to pass the spec test.
This will pass the test:

module Concerns
module Findable

  1. Music Lib CLI Spec requires a list/play song expectation I was able to run the program and get the intended behavior from my methods however the test were "failing".

Song.find_by_name method spec seems broken

The spec file seems to want the class returned instead of the song instance itself. The following code makes the test pass, this is not correct.

   found= all.find do |song| 
     song.name == name
       end
      return found.class
    end

wont load lab

it keeps saying it is having a problem loading the lab try again later

Code expected is not DRY-ing

Hello,

Just noticed that we had to copy the sort code that was already implemented in the #list_songs method. This seems to be a bit redundant if we already have a method that produces the listed number of songs.

def list_songs Song.all.sort {|x,y| x.name <=> y.name}.each_with_index {|val, index| puts "#{index + 1}. #{val.artist.name} - #{val.name} - #{val.genre.name}" } end

def play_song puts "Which song number would you like to play?" num = gets.strip.to_i - 1 song = Song.all.sort {|x,y| x.name <=> y.name}[num] if song && num.between?(0, Song.all.length-1) puts "Playing #{song.name} by #{song.artist.name}" end end

I think that it would've made sense to invoke #list_songs, and then manipulate the produced numbered list instead of sorting the song list twice. The wording of the specs also kind of leads you to believe that we had to invoke #list_songs any way.

`describe "#play_song" do
it "prompts the user to choose a song from the alphabetized list output by #list_songs" do
allow(music_library_controller).to receive(:gets).and_return("Testing for #puts")

  expect($stdout).to receive(:puts).with("Which song number would you like to play?")

  allow($stdout).to receive(:puts)

  music_library_controller.play_song
end`

Namespacing Syntax on Concerns::Findable

Hello,

I was getting an uninitialized constant error using the following convention for the Concerns::Findable module laid out in the instructions:

module Concerns::ModuleName
    # Module code here
end

Here is a sample of the error message:

// ♥ learn --fail-fast
/home/redolent-teacher-2456/ruby-music-library-cli-v-000/lib/artist.rb:9:in `<class:Artist>': uninitialized constant Artist::Concerns (NameError)
        from /home/redolent-teacher-2456/ruby-music-library-cli-v-000/lib/artist.rb:8:in `<top (required)>'

However I looked back to the Intro to Modules lab and the convention for nested modules there was the following:

module Concerns
    module ModuleName
        #Module code here
    end
end

I adjusted my code to reflect the above and I was no longer receiving that error. Are the instructions inaccurate or am I misunderstanding the instructions?

Feel free to take a look at the lib/concerns/findable.rb file in my repo for this project here: https://github.com/tgray017/ruby-music-library-cli-v-000/blob/master/lib/concerns/findable.rb

After completing this lab, I tested it one more time and swapped out this convention:

module Concerns
  module Findable
    def find_by_name(name)
      self.all.detect {|object| object.name == name}
    end
    
    def find_or_create_by_name(name)
      if self.find_by_name(name).nil?
        self.create(name)
      else
        self.find_by_name(name)
      end
    end
  end
end

with this convention:

module Concerns::Findable
    def find_by_name(name)
      self.all.detect {|object| object.name == name}
    end
    
    def find_or_create_by_name(name)
      if self.find_by_name(name).nil?
        self.create(name)
      else
        self.find_by_name(name)
      end
    end
end

and again received the error.

Ask a Question is having errors.

When I pull up what questions I should be receiving, it only gives me the option of "all previous lessons." The toggle on and off for it does not work though. Any ideas?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.