{"id":15472837,"url":"https://github.com/hackvan/ruby-music-library-cli","last_synced_at":"2025-10-04T18:35:14.949Z","repository":{"id":145341863,"uuid":"141342689","full_name":"hackvan/ruby-music-library-cli","owner":"hackvan","description":null,"archived":false,"fork":false,"pushed_at":"2018-07-18T19:57:24.000Z","size":22,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-05T17:13:23.929Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hackvan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-17T20:55:14.000Z","updated_at":"2018-07-18T19:57:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"5e989d4c-eae0-46ae-a632-a606b38a046e","html_url":"https://github.com/hackvan/ruby-music-library-cli","commit_stats":{"total_commits":5,"total_committers":1,"mean_commits":5.0,"dds":0.0,"last_synced_commit":"973812e31e57d9ac9b364c327bf32bc83b8b5032"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hackvan%2Fruby-music-library-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hackvan%2Fruby-music-library-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hackvan%2Fruby-music-library-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hackvan%2Fruby-music-library-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hackvan","download_url":"https://codeload.github.com/hackvan/ruby-music-library-cli/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240386530,"owners_count":19793193,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-02T02:41:15.715Z","updated_at":"2025-10-04T18:35:14.866Z","avatar_url":"https://github.com/hackvan.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ruby Music Library\n\n## Overview\nYou'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.\n\nThis 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.\n\n## Instructions\n\n## `Song`, `Artist`, and `Genre` basics\nThe 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.\n\nThe 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.\n\n```ruby\nSong.new(\"Blank Space\").name #=\u003e \"Blank Space\"`\n```\n\nAdditionally, *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`.\n\n```ruby\nSong.all #=\u003e []\n\nArtist.all #=\u003e []\n```\n\nInstances should respond to a `#save` method that adds the instance itself into the appropriate `@@all` class variable.\n\n```ruby\nSong.new(\"Blank Space\").save\nSong.all #=\u003e [#\u003cSong: @name=\"Blank Space\"\u003e]\n```\n\nThe class should be able to empty its `@@all` array via a class method `.destroy_all`.\n\n```ruby\nSong.new(\"Kaohsiung Christmas\").save\nSong.all #=\u003e [#\u003cSong: @name=\"Blank Space\"\u003e, #\u003cSong: @name=\"Kaohsiung Christmas\"\u003e]\nSong.destroy_all\nSong.all #=\u003e []\n```\n\nFinally, 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.\n\n```ruby\nSong.new(\"Blank Space\")\nSong.all #=\u003e []\nSong.create(\"Blank Space\")\nSong.all #=\u003e [#\u003cSong: @name=\"Blank Space\"\u003e]\n```\n\n## Relationships\n\n### `Song`s and `Artist`s\n * 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.\n * Songs can be initialized with an optional `artist` argument.\n\n### `Song`s and `Genre`s\n  * Genres have many songs and are initialized with an empty list of songs.\n  * Songs have one genre.\n  * Songs can be initialized with an optional `genre` argument.\n\n### `Artist`s and `Genre`s\n  * Artists have many genres through their songs. Implement a `#genres` method for this association.\n  * Genres have many artists through their songs. Implement an `#artists` method for this association.\n\n***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.\n  * If we call `Song.new(\"Song Title\", artist_object, genre_object)`, both `Song#artist=` and `Song#genre=` should be invoked.\n  * If we call `Song.new(\"This Song Has No Artist or Genre\")`, neither `Song#artist=` nor `Song#genre=` should be invoked.\n\n## Finding\n\n### Song\nFirst implement the following two methods in your `Song` class:\n  * Songs should have a `find_by_name` method.\n  * Songs should have a `find_or_create_by_name` method.\n\n### `Concerns::Findable`\nNow 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:\n```ruby\nmodule Concerns::ModuleName\n  # Module code here\nend\n```\nOnce the basic module structure is good to go, it's time to code our two class methods again:\n  * 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.\n  * 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.\n  * Add this module to your `Genre` and `Artist` class.\n\n## `MusicImporter`\nCreate 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:\n  * `#initialize` accepts a file path to a directory of MP3 files.\n  * `#files` returns all of the imported filenames.\n  * `.import` imports all of the files from the library, instantiating a new `Song` object for each file.\n\nIn addition, add the following pair of methods to your `Song` class:\n  * `.new_from_filename`, which instantiates a new `Song` object based on a provided filename.\n  * `.create_from_filename`, which does the same thing as `.new_from_filename` but also saves the newly-created song to the `@@all` class variable.\n\n## It's CLI time!\nCongrats! 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:\n  * 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.\n  * Has a `#call` method that starts the CLI and prompts the user for input. Read the tests carefully for specifics.\n\nHave fun!\n\n## Resources\n* [QA with Students](https://www.youtube.com/watch?v=kgYP9Yj8OE4\u0026feature=youtu.be)\n  - This Q\u0026A led by Avi Flombaum covers setting up a bin file, setting up a `Gemfile` and installing gems, and identifying objects and their responsibilities. It contains general tips on requirements, gems, and design.\n* [Debugging an Error in Music Library CLI](https://www.youtube.com/watch?v=J_BSGPW37AE)\n  - This walk-through traces an error to its source in the code. In addition it covers how to change file permissions and how to create an executable file to initialize a sandbox environment.\n* [Lab Review](https://www.youtube.com/watch?v=iClea2crypU)\n\n\u003cp class='util--hide'\u003eView \u003ca href='https://learn.co/lessons/ruby-music-library-cli'\u003eMusic Library CLI\u003c/a\u003e on Learn.co and start learning to code for free.\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhackvan%2Fruby-music-library-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhackvan%2Fruby-music-library-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhackvan%2Fruby-music-library-cli/lists"}