├── Gemfile ├── main.rb ├── lib ├── genre.rb ├── label.rb ├── author.rb ├── music_album.rb ├── book.rb ├── game.rb └── item.rb ├── .github └── workflows │ ├── tests.yml │ └── linters.yml ├── spec ├── genre_spec.rb ├── author_spec.rb ├── music_album_spec.rb ├── game_spec.rb ├── book_spec.rb ├── label_spec.rb └── item_spec.rb ├── database ├── queries.sql └── schema.sql ├── .rubocop.yml ├── LICENSE ├── menu.rb ├── module └── storage.rb ├── README.md └── app.rb /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'rubocop', '>= 1.0', '< 2.0' 3 | 4 | # gem "rails" 5 | -------------------------------------------------------------------------------- /main.rb: -------------------------------------------------------------------------------- 1 | require_relative 'menu' 2 | require_relative 'app' 3 | 4 | def main 5 | app = App.new 6 | menu = Menu.new(app) 7 | menu.start_up 8 | end 9 | 10 | main 11 | -------------------------------------------------------------------------------- /lib/genre.rb: -------------------------------------------------------------------------------- 1 | require_relative './music_album' 2 | class Genre 3 | attr_reader :name 4 | 5 | def initialize(name) 6 | @id = Random.rand(1..1000) 7 | @name = name 8 | @items = [] 9 | end 10 | 11 | def add_item(item) 12 | @items << item 13 | item.genre = self 14 | end 15 | 16 | def to_hash 17 | { 18 | id: @id, 19 | name: name 20 | } 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/label.rb: -------------------------------------------------------------------------------- 1 | class Label 2 | attr_accessor :title, :color 3 | 4 | def initialize(title, color) 5 | @id = Random.rand(1..1000) 6 | @title = title 7 | @color = color 8 | @items = [] 9 | end 10 | 11 | def add_item(item) 12 | @items << item unless @items.include?(item) 13 | item.label = self 14 | end 15 | 16 | def to_hash 17 | { 18 | id: @id, 19 | title: title, 20 | color: color 21 | } 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/author.rb: -------------------------------------------------------------------------------- 1 | class Author 2 | attr_reader :first_name, :last_name 3 | 4 | def initialize(first_name, last_name) 5 | @id = Random.rand(1..1000) 6 | @first_name = first_name 7 | @last_name = last_name 8 | @items = [] 9 | end 10 | 11 | def add_item(item) 12 | @items << item 13 | item.author = self 14 | end 15 | 16 | def to_hash 17 | { 18 | id: @id, 19 | first_name: @first_name, 20 | last_name: @last_name 21 | } 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | rspec: 7 | name: RSpec 8 | runs-on: ubuntu-22.04 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-ruby@v1 12 | with: 13 | ruby-version: 3.1.x 14 | - name: Setup RSpec 15 | run: | 16 | [ -f Gemfile ] && bundle 17 | gem install --no-document rspec -v '>=3.0, < 4.0' 18 | - name: RSpec Report 19 | run: rspec --force-color --format documentation 20 | -------------------------------------------------------------------------------- /spec/genre_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../lib/genre' 2 | require_relative '../lib/item' 3 | 4 | describe Genre do 5 | genre = Genre.new('Country') 6 | 7 | context '#new' do 8 | it 'should be instance of Genre' do 9 | expect(genre).to be_instance_of Genre 10 | expect(genre.name).to eq 'Country' 11 | end 12 | end 13 | 14 | context '#add_item' do 15 | it 'should have one item in its @items' do 16 | item = Item.new('2000-2-3') 17 | genre.add_item(item) 18 | expect(genre.instance_eval { @items }.length).to be 1 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/author_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../lib/author' 2 | 3 | describe Author do 4 | before(:each) do 5 | @author = Author.new('James', 'Zerihun') 6 | end 7 | describe '#initialize' do 8 | it 'should create an instance with attributes, ' do 9 | expect(@author).to be_instance_of Author 10 | end 11 | end 12 | describe '#add_item' do 13 | it 'should change items length to 1 when we add one item' do 14 | item = Item.new('2002-12-04') 15 | @author.add_item(item) 16 | expect(@author.instance_eval { @items }.length).to be 1 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/music_album.rb: -------------------------------------------------------------------------------- 1 | require_relative 'item' 2 | 3 | class MusicAlbum < Item 4 | attr_accessor :on_spotify 5 | 6 | def initialize(on_spotify, publish_date) 7 | super(publish_date, label: nil, author: nil, genre: nil) 8 | @id = Random.rand(1..1000) 9 | @on_spotify = on_spotify 10 | end 11 | 12 | def to_hash 13 | hash = { 14 | id: @id, 15 | on_spotify: on_spotify, 16 | genre: @genre.to_hash, 17 | label: @label.to_hash, 18 | author: @author.to_hash 19 | } 20 | super.merge(hash) 21 | end 22 | 23 | private 24 | 25 | def can_be_archived? 26 | super && @on_spotify 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /.github/workflows/linters.yml: -------------------------------------------------------------------------------- 1 | name: Linters 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | rubocop: 7 | name: Rubocop 8 | runs-on: ubuntu-22.04 9 | 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions/setup-ruby@v1 13 | with: 14 | ruby-version: '>=3.1.x' 15 | - name: Setup Rubocop 16 | run: | 17 | gem install --no-document rubocop -v '>= 1.0, < 2.0' # https://docs.rubocop.org/en/stable/installation/ 18 | [ -f .rubocop.yml ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/ruby/.rubocop.yml 19 | - name: Rubocop Report 20 | run: rubocop --color 21 | -------------------------------------------------------------------------------- /database/queries.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | B.pulblisher, 3 | B.cover_state, 4 | I.publish_date, 5 | A.first_name, 6 | G.name, 7 | L.title 8 | FROM 9 | books B 10 | JOIN items I ON B.item_id = I.id 11 | JOIN genres G ON b.genre_id = G.id 12 | JOIN authors A ON b.author_id = A.id 13 | JOIN labels L ON B.label_id = L.id; 14 | 15 | select * from books; 16 | select * from games; 17 | 18 | SELECT 19 | A.id, A.publish_date, A.on_spotify, A.archived 20 | G.name, 21 | L.title, 22 | AU.first_name, AU.last_name 23 | FROM 24 | albums A 25 | JOIN genres G ON A.id = G.id 26 | JOIN authors AU ON A.id = AU.id 27 | JOIN labels L ON A.id = L.id 28 | -------------------------------------------------------------------------------- /lib/book.rb: -------------------------------------------------------------------------------- 1 | require_relative 'item' 2 | 3 | class Book < Item 4 | attr_reader :publisher, :cover_state, :publish_date 5 | 6 | def initialize(publisher, cover_state, publish_date) 7 | super(publish_date, label: nil, author: nil, genre: nil) 8 | @id = Random.rand(1..1000) 9 | @publisher = publisher 10 | @cover_state = cover_state 11 | end 12 | 13 | def to_hash 14 | hash = { 15 | publisher: publisher, 16 | cover_state: cover_state, 17 | label: @label.to_hash, 18 | author: @author.to_hash, 19 | id: @id 20 | } 21 | super.merge(hash) 22 | end 23 | 24 | private 25 | 26 | def can_be_archived? 27 | super || @cover_state.downcase == 'bad' 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/music_album_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../lib/music_album' 2 | 3 | describe MusicAlbum do 4 | album_one = MusicAlbum.new(true, '2000-1-13') 5 | album_two = MusicAlbum.new(false, '2000-1-13') 6 | 7 | context '#new' do 8 | it 'should be an instance of MusicAlbum' do 9 | expect(album_one).to be_instance_of MusicAlbum 10 | end 11 | end 12 | 13 | context '#can_be_archived?' do 14 | it "should return true if parent's method returns true AND if on_spotify equals true" do 15 | expect(album_one.instance_eval('can_be_archived?', __FILE__, __LINE__)).to be_truthy 16 | end 17 | 18 | it 'should return false' do 19 | expect(album_two.instance_eval('can_be_archived?', __FILE__, __LINE__)).to be_falsy 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/game_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../lib/game' 2 | 3 | describe Game do 4 | before(:each) do 5 | @game = Game.new('2012-01-03', 'player1', '2020-12-04') 6 | end 7 | 8 | describe '#initialize' do 9 | it 'should create an instance with attributes, multiplayer and last_played_at,' do 10 | expect(@game).to be_instance_of Game 11 | end 12 | end 13 | 14 | describe '#can_be_archived' do 15 | it 'should return true if parent method returns true and last_played_at is older than 2 years' do 16 | expect(@game.instance_eval('can_be_archived?', __FILE__, __LINE__)).to be_truthy 17 | end 18 | it 'should return false otherwise' do 19 | @game.last_played_at = '2022-01-05' 20 | expect(@game.instance_eval('can_be_archived?', __FILE__, __LINE__)).to be_falsey 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/game.rb: -------------------------------------------------------------------------------- 1 | require_relative './item' 2 | 3 | class Game < Item 4 | attr_accessor :last_played_at 5 | attr_reader :multiplayer 6 | 7 | def initialize(publish_date, multiplayer, last_played_at) 8 | super(publish_date, label: nil, author: nil, genre: nil) 9 | @multiplayer = multiplayer 10 | @last_played_at = last_played_at 11 | end 12 | 13 | def to_hash 14 | hash = { 15 | multiplayer: @multiplayer, 16 | last_played_at: @last_played_at, 17 | label: @label.to_hash, 18 | author: @author.to_hash, 19 | id: @id 20 | } 21 | super.merge(hash) 22 | end 23 | 24 | private 25 | 26 | def can_be_archived? 27 | current_year = Date.today.year 28 | last_played_parsed = Date.parse(@last_played_at).year 29 | super && current_year - last_played_parsed > 2 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/book_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../lib/book' 2 | 3 | describe 'Book' do 4 | before(:each) do 5 | @book_one = Book.new('Sagawa', 'bad', '2022-11-02') 6 | @book_two = Book.new('Sakura', 'good', '2002-12-04') 7 | @book_three = Book.new('Milton', 'good', '2013-11-03') 8 | end 9 | context 'initializing' do 10 | it 'should be instance of Book' do 11 | expect(@book_one).to be_instance_of Book 12 | expect(@book_two).to be_instance_of Book 13 | end 14 | end 15 | 16 | context 'can_be_archived?' do 17 | it 'should return true if parent method return true' do 18 | expect(@book_one.instance_eval('can_be_archived?', __FILE__, __LINE__)).to be_truthy 19 | expect(@book_two.instance_eval('can_be_archived?', __FILE__, __LINE__)).to be_truthy 20 | end 21 | 22 | it 'should return false' do 23 | expect(@book_three.instance_eval('can_be_archived?', __FILE__, __LINE__)).to be_falsy 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/label_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../lib/label' 2 | require_relative '../lib/item' 3 | 4 | describe 'Label' do 5 | before(:each) do 6 | @label_one = Label.new('Gift', 'yellow') 7 | @item_one = Item.new('2020-12-10') 8 | 9 | @label_one.add_item(@item_one) 10 | @label_one.add_item(@item_one) 11 | end 12 | 13 | context 'initializing' do 14 | it 'should be instance of Label' do 15 | expect(@label_one).to be_instance_of Label 16 | end 17 | end 18 | 19 | context 'with Label methods' do 20 | it 'should add the input item to the collection of items' do 21 | expect(@label_one.instance_eval { @items }).to include @item_one 22 | end 23 | 24 | it 'should have the same label in item class' do 25 | expect(@item_one.label).to be @label_one 26 | end 27 | 28 | it 'length should be one if the same item was added' do 29 | expect(@label_one.instance_eval { @items }.length).to eq 1 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | NewCops: enable 3 | Exclude: 4 | - 'Guardfile' 5 | - 'Rakefile' 6 | - 'node_modules/**/*' 7 | 8 | DisplayCopNames: true 9 | 10 | Layout/LineLength: 11 | Max: 120 12 | Metrics/MethodLength: 13 | Max: 20 14 | Metrics/AbcSize: 15 | Max: 50 16 | Metrics/ClassLength: 17 | Max: 150 18 | Metrics/BlockLength: 19 | IgnoredMethods: ['describe'] 20 | Max: 30 21 | 22 | Style/Documentation: 23 | Enabled: false 24 | Style/ClassAndModuleChildren: 25 | Enabled: false 26 | Style/EachForSimpleLoop: 27 | Enabled: false 28 | Style/AndOr: 29 | Enabled: false 30 | Style/DefWithParentheses: 31 | Enabled: false 32 | Style/FrozenStringLiteralComment: 33 | EnforcedStyle: never 34 | 35 | Layout/HashAlignment: 36 | EnforcedColonStyle: key 37 | Layout/ExtraSpacing: 38 | AllowForAlignment: false 39 | Layout/MultilineMethodCallIndentation: 40 | Enabled: true 41 | EnforcedStyle: indented 42 | Lint/RaiseException: 43 | Enabled: false 44 | Lint/StructNewOverride: 45 | Enabled: false 46 | Style/HashEachMethods: 47 | Enabled: false 48 | Style/HashTransformKeys: 49 | Enabled: false 50 | Style/HashTransformValues: 51 | Enabled: false 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Thiri Htet Htet Aung, Damilare Adepoju, Nahom 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/item.rb: -------------------------------------------------------------------------------- 1 | require 'date' 2 | 3 | class Item 4 | attr_accessor :publish_date 5 | attr_reader :author, :label, :genre 6 | 7 | def initialize(publish_date, label: nil, author: nil, genre: nil) 8 | @id = Random.rand(1..1000) 9 | @publish_date = publish_date 10 | @archived = false 11 | @label = label 12 | @author = author 13 | @genre = genre 14 | end 15 | 16 | def genre=(genre) 17 | @genre = genre 18 | genre.instance_eval { @items } << self unless genre.instance_eval { @items }.include?(self) 19 | end 20 | 21 | def author=(author) 22 | @author = author 23 | author.instance_eval { @items } << self unless @author.instance_eval { @items }.include?(self) 24 | end 25 | 26 | def label=(label) 27 | @label = label 28 | label.instance_eval { @items } << self unless label.instance_eval { @items }.include?(self) 29 | end 30 | 31 | def can_be_archived? 32 | cur_year = Date.today.year 33 | publish_year = Date.parse(@publish_date).year 34 | cur_year - publish_year > 10 35 | end 36 | 37 | def move_to_archive 38 | @archived = can_be_archived? 39 | end 40 | 41 | def to_hash 42 | { 43 | publish_date: publish_date, 44 | archived: @archived 45 | } 46 | end 47 | 48 | private :can_be_archived? 49 | end 50 | -------------------------------------------------------------------------------- /menu.rb: -------------------------------------------------------------------------------- 1 | class Menu 2 | def initialize(app) 3 | @app = app 4 | @menus = { 5 | 1 => { label: 'List all books', action: :list_books }, 6 | 2 => { label: 'List all music albums', action: :list_albums }, 7 | 3 => { label: 'List of games', action: :list_games }, 8 | 4 => { label: 'List all genres', action: :list_genres }, 9 | 5 => { label: 'List all labels', action: :list_labels }, 10 | 6 => { label: 'List all authors', action: :list_authors }, 11 | 7 => { label: 'Add a book', action: :add_book }, 12 | 8 => { label: 'Add a music album', action: :add_album }, 13 | 9 => { label: 'Add a game', action: :add_game }, 14 | 10 => { label: 'Exit', action: :exit } 15 | } 16 | end 17 | 18 | def prmopt_menu 19 | puts "\n" 20 | @menus.each do |key, value| 21 | puts "#{key}. #{value[:label]}" 22 | end 23 | gets.chomp.to_i 24 | end 25 | 26 | def choose_menu(num) 27 | menu_size = @menus.size 28 | if (1..menu_size).include?(num) 29 | chosen = @menus[num][:action] 30 | @app.send(chosen) 31 | else 32 | puts 'Type a valid number' 33 | end 34 | end 35 | 36 | def start_up 37 | puts "Welcome to my catalog \n" 38 | loop do 39 | num = prmopt_menu 40 | choose_menu(num) 41 | 42 | return if num == @menus.size 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /database/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE genres ( 2 | id SERIAL PRIMARY KEY, 3 | name VARCHAR(255) NOT NULL 4 | ); 5 | 6 | CREATE TABLE authors ( 7 | id SERIAL PRIMARY KEY, 8 | first_name VARCHAR(255) NOT NULL, 9 | last_name VARCHAR(255) NOT NULL 10 | ); 11 | 12 | CREATE TABLE labels ( 13 | id SERIAL PRIMARY KEY, 14 | title VARCHAR(255) NOT NULL, 15 | color VARCHAR(255) NOT NULL 16 | ); 17 | 18 | CREATE TABLE books ( 19 | id SERIAL PRIMARY KEY, 20 | publish_date DATE NOT NULL, 21 | archived BOOLEAN DEFAULT FALSE, 22 | pulblisher VARCHAR(255) NOT NULL, 23 | cover_state VARCHAR(255) NOT NULL, 24 | genre_id INT REFERENCES genres(id), 25 | author_id INT REFERENCES authors(id), 26 | label_id INT REFERENCES labels(id) 27 | ); 28 | 29 | CREATE TABLE games ( 30 | id SERIAL PRIMARY KEY, 31 | publish_date DATE NOT NULL, 32 | archived BOOLEAN DEFAULT FALSE, 33 | multiplayer VARCHAR(255) NOT NULL, 34 | last_played_at DATE NOT NULL, 35 | genre_id INT REFERENCES genres(id), 36 | author_id INT REFERENCES authors(id), 37 | label_id INT REFERENCES labels(id) 38 | ); 39 | 40 | CREATE TABLE albums ( 41 | id SERIAL PRIMARY KEY, 42 | publish_date DATE NOT NULL, 43 | on_spotify BOOLEAN DEFAULT FALSE, 44 | archived BOOLEAN DEFAULT FALSE, 45 | genre_id INT REFERENCES genres(id), 46 | author_id INT REFERENCES authors(id), 47 | label_id INT REFERENCES labels(id) 48 | ) 49 | 50 | CREATE INDEX books_genre_id ON books (genre_id); 51 | CREATE INDEX books_author_id ON books (author_id); 52 | CREATE INDEX books_label_id ON books (label_id); 53 | CREATE INDEX games_genre_id ON games (genre_id); 54 | CREATE INDEX games_author_id ON games (author_id); 55 | CREATE INDEX games_label_id ON games (label_id); 56 | CREATE INDEX albums_genre_id ON albums (genre_id); 57 | CREATE INDEX albums_author_id ON albums (author_id); 58 | CREATE INDEX albums_label_id ON albums (label_id); 59 | -------------------------------------------------------------------------------- /spec/item_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../lib/item' 2 | require_relative '../lib/genre' 3 | require_relative '../lib/author' 4 | require_relative '../lib/label' 5 | 6 | describe 'Item' do 7 | before(:each) do 8 | @item = Item.new('2002-12-04') 9 | end 10 | 11 | context 'initialization' do 12 | it 'should be instance of Item' do 13 | expect(@item).to be_instance_of Item 14 | end 15 | 16 | it 'should return true if published_date is older than 10 years' do 17 | expect(@item.instance_eval('can_be_archived?', __FILE__, __LINE__)).to be_truthy 18 | end 19 | 20 | it 'should change the archived property to true if move_to_archive method have been called' do 21 | @item.move_to_archive 22 | expect(@item.instance_eval { @archived }).to be_truthy 23 | end 24 | end 25 | 26 | context '#genre' do 27 | before(:each) do 28 | genre = Genre.new('Fiction') 29 | @item.genre = genre 30 | end 31 | 32 | it 'should create instance genre for the item' do 33 | expect(@item.genre).to be_instance_of Genre 34 | end 35 | 36 | it 'the genre instance should have name set' do 37 | name = @item.genre.name 38 | expect(name).to eq 'Fiction' 39 | end 40 | end 41 | 42 | context '#author' do 43 | before(:each) do 44 | author = Author.new('Dare', 'Ade') 45 | @item.author = author 46 | end 47 | 48 | it 'should create instance author for the item' do 49 | expect(@item.author).to be_instance_of Author 50 | end 51 | 52 | it 'the genre instance should have name set' do 53 | first_name = @item.author.first_name 54 | last_name = @item.author.last_name 55 | 56 | expect(first_name).to eq 'Dare' 57 | expect(last_name).to eq 'Ade' 58 | end 59 | end 60 | 61 | context '#label' do 62 | before(:each) do 63 | label = Label.new('Learn Ruby', 'Green') 64 | @item.label = label 65 | end 66 | 67 | it 'should create instance label for the item' do 68 | expect(@item.label).to be_instance_of Label 69 | end 70 | 71 | it 'the genre instance should have name set' do 72 | title = @item.label.title 73 | color = @item.label.color 74 | 75 | expect(title).to eq 'Learn Ruby' 76 | expect(color).to eq 'Green' 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /module/storage.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'fileutils' 3 | require_relative '../lib/book' 4 | require_relative '../lib/label' 5 | require_relative '../lib/genre' 6 | require_relative '../lib/game' 7 | require_relative '../lib/author' 8 | 9 | module Storage 10 | def save_books(books) 11 | save_data('./data/book.json', array_to_hash(books)) 12 | end 13 | 14 | def save_albums(albums) 15 | save_data('./data/album.json', array_to_hash(albums)) 16 | end 17 | 18 | def save_games(games) 19 | save_data('./data/game.json', array_to_hash(games)) 20 | end 21 | 22 | def save_extra_details(label, genre, author) 23 | save_data('./data/genre.json', array_to_hash(genre)) 24 | save_data('./data/author.json', array_to_hash(author)) 25 | save_data('./data/label.json', array_to_hash(label)) 26 | end 27 | 28 | def fetch_books 29 | books = fetch_data('./data/book.json') 30 | book_arr = [] 31 | books&.each do |book| 32 | book_sample = Book.new(book['publisher'], book['cover_state'], book['publish_date']) 33 | extra_to_array(book_sample, book) 34 | book_arr << book_sample 35 | end 36 | book_arr 37 | end 38 | 39 | def fetch_labels 40 | labels = fetch_data('./data/label.json') 41 | labels&.map do |label| 42 | Label.new(label['title'], label['color']) 43 | end 44 | end 45 | 46 | def fetch_albums 47 | albums = fetch_data('./data/album.json') 48 | albums_arr = [] 49 | albums&.each do |album| 50 | album_sample = MusicAlbum.new(album['on_spotify'], album['publish_date']) 51 | extra_to_array(album_sample, album) 52 | albums_arr << album_sample 53 | end 54 | 55 | albums_arr 56 | end 57 | 58 | def fetch_genres 59 | genres = fetch_data('./data/genre.json') 60 | genres&.map do |genre| 61 | Genre.new(genre['name']) 62 | end 63 | end 64 | 65 | def fetch_games 66 | games = fetch_data('./data/game.json') 67 | game_arr = [] 68 | games&.each do |game| 69 | game_sample = Game.new(game['publish_date'], game['multiplayer'], game['last_played_at']) 70 | extra_to_array(game_sample, game) 71 | game_arr << game_sample 72 | end 73 | game_arr 74 | end 75 | 76 | def fetch_author 77 | authors = fetch_data('./data/author.json') 78 | authors&.map do |author| 79 | Author.new(author['first_name'], author['last_name']) 80 | end 81 | end 82 | 83 | private 84 | 85 | def save_data(file_path, data) 86 | json = JSON.generate(data) 87 | File.write(file_path, json) 88 | end 89 | 90 | def fetch_data(file_path) 91 | FileUtils.mkdir_p(File.dirname(file_path)) 92 | File.new(file_path, 'w+') unless File.exist?(file_path) 93 | 94 | return nil if File.empty?(file_path) 95 | 96 | json_data = File.read(file_path) 97 | JSON.parse(json_data) unless json_data.empty? 98 | end 99 | 100 | def array_to_hash(arr) 101 | arr.map(&:to_hash) 102 | end 103 | 104 | def extra_to_array(klass, data) 105 | label = Label.new(data['label']['title'], data['label']['color']) 106 | author = Author.new(data['author']['first_name'], data['author']['last_name']) 107 | genre = Genre.new(data['name']) 108 | klass.send(:label=, label) 109 | klass.send(:author=, author) 110 | klass.send(:genre=, genre) 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

Catalog

5 |
6 | 7 | # 📗 Table of Contents 8 | 9 | - [📖 About the Project](#about-project) 10 | - [🛠 Built With](#built-with) 11 | - [Tech Stack](#tech-stack) 12 | - [Key Features](#key-features) 13 | - [💻 Getting Started](#getting-started) 14 | - [Setup](#setup) 15 | - [Prerequisites](#prerequisites) 16 | - [Install](#install) 17 | - [Usage](#usage) 18 | - [👥 Authors](#authors) 19 | - [🔭 Future Features](#future-features) 20 | - [🤝 Contributing](#contributing) 21 | - [⭐️ Show your support](#support) 22 | - [🙏 Acknowledgements](#acknowledgements) 23 | 24 | - [📝 License](#license) 25 | 26 | # 📖 Catalog 27 | 28 | Catalog is a console app built with Ruby. The app helps to keep a record of different types of things a user own: books, music albums, movies, and games. 29 | 30 | [Project Video](https://drive.google.com/file/d/1fBWwVoO9v3Mb6MrTngdTTsPN7AGVQzD1/view?usp=drive_link) 31 | 32 | ## 🛠 Built With 33 | 34 | ### Tech Stack 35 | 36 |
37 | Client 38 | 39 |
40 | 41 | ### Key Features 42 | 43 | - **Create and list Book items** 44 | - **Create and list Music Album items** 45 | - **Create and list Game items** 46 | - **Create and list Genres** 47 | - **Create and list Authors** 48 | - **Create and list Labels** 49 | 50 |

(back to top)

51 | 52 | ## 💻 Getting Started 53 | 54 | To get a local copy up and running, follow these steps. 55 | 56 | ### Prerequisites 57 | 58 | In order to run this project you need: Ruby 59 | 60 | ### Setup 61 | 62 | Clone the repo, and install the dependencies 63 | 64 | ``` 65 | git clone https://github.com/GraceHtet/catalog.git 66 | ``` 67 | 68 | ### Install 69 | 70 | Install the project's dependencies with: 71 | 72 | ```sh 73 | bundle install 74 | ``` 75 | 76 | ### Usage 77 | 78 | To run the project, execute the following command: 79 | 80 | ```sh 81 | ruby main.rb 82 | ``` 83 | 84 |

(back to top)

85 | 86 | ## 👥 Authors 87 | 88 | 👤 **Grace Htet** 89 | 90 | - GitHub: [Grace Htet](https://github.com/GraceHtet) 91 | - LinkedIn: [Grace Htet](https://linkedin.com/in/thirihtethtetaung) 92 | - Twitter: [Grace Htet](https://twitter.com/Grace_Htet4) 93 | - Angelist: [Grace Htet](https://wellfound.com/u/thiri-htet) 94 | 95 | 👤 **Damilare Adepoju** 96 | 97 | - GitHub: [@githubhandle](https://github.com/adamilare) 98 | - Twitter: [@twitterhandle](https://twitter.com/mailtodare) 99 | - LinkedIn: [LinkedIn](https://linkedin.com/in/damilareadepoju) 100 | - GitUp Page: [My Page](https://adamilare.github.io/) 101 | 102 | 👤 **Nahom Zerihun Demissie** 103 | 104 | - GitHub: [@zdnahom](https://github.com/zdnahom) 105 | - LinkedIn: [LinkedIn](https://linkedin.com/in/nahomzd) 106 | - Twitter: [@nahom11](https://twitter.com/Nahomzerihun11) 107 | 108 |

(back to top)

109 | 110 | ## 🔭 Future Features 111 | 112 | - [ ] **Implement Movie features** 113 | - [ ] **Implement Source features** 114 | 115 |

(back to top)

116 | 117 | ## 🤝 Contributing 118 | 119 | Contributions, issues, and feature requests are welcome! 120 | 121 | Feel free to check the [issues page](../../issues/). 122 | 123 |

(back to top)

124 | 125 | ## ⭐️ Show your support 126 | 127 | If you like this project or find it useful, please consider giving it a ⭐️. Thanks! 128 | 129 |

(back to top)

130 | 131 | ## 🙏 Acknowledgments 132 | 133 | My coding partners and Microverse 134 | 135 |

(back to top)

136 | 137 | ## 📝 License 138 | 139 | This project is [MIT](./LICENSE) licensed. 140 | 141 |

(back to top)

142 | -------------------------------------------------------------------------------- /app.rb: -------------------------------------------------------------------------------- 1 | require_relative './lib/book' 2 | require_relative './lib/game' 3 | require_relative './module/storage' 4 | 5 | require_relative './lib/label' 6 | require_relative './lib/author' 7 | require_relative './lib/music_album' 8 | 9 | class App 10 | include Storage 11 | 12 | def initialize 13 | @books = fetch_books || [] 14 | @albums = fetch_albums || [] 15 | @games = fetch_games || [] 16 | @genres = fetch_genres || [] 17 | @authors = fetch_author || [] 18 | @labels = fetch_labels || [] 19 | end 20 | 21 | def list_books 22 | return puts 'There is no book in the list yet!' if @books.empty? 23 | 24 | @books.each do |book| 25 | puts "Published at: #{book.publish_date} by #{book.publisher.capitalize}, Cover State: #{book.cover_state}" 26 | end 27 | end 28 | 29 | def list_albums 30 | return puts 'There is no album created yet!' if @albums.empty? 31 | 32 | puts 'List of all Albums in the collection:' 33 | @albums.each do |album| 34 | on_spotify = album.on_spotify == 'y' ? 'Yes' : 'No' 35 | puts "Label: #{album.label.title}, Date Published: #{album.publish_date}, On spotify: #{on_spotify}" 36 | end 37 | end 38 | 39 | def list_games 40 | puts 'List of games' 41 | @games.each do |game| 42 | published_date = game.publish_date 43 | multiplayer = game.multiplayer 44 | last_played_at = game.last_played_at 45 | puts "Published at: #{published_date}, Multiplayer: #{multiplayer}, Last played at: #{last_played_at}" 46 | end 47 | end 48 | 49 | def list_genres 50 | return puts 'No genre created yet!' if @genres.empty? 51 | 52 | puts 'List of all Genres in the collection' 53 | @genres.each_with_index do |genre, index| 54 | puts "#{index}) #{genre.name}" 55 | end 56 | end 57 | 58 | def list_labels 59 | return puts 'There is no label in the list yet!' if @labels.empty? 60 | 61 | puts 'The labels in the list:' 62 | @labels.each_with_index do |label, idx| 63 | puts "#{idx}) #{label.title}" 64 | end 65 | end 66 | 67 | def list_authors 68 | return puts 'There is no author in the list yet!' if @authors.empty? 69 | 70 | puts 'The authors in the list:' 71 | @authors.each_with_index do |author, idx| 72 | puts "#{idx}) #{author.first_name} #{author.last_name}" 73 | end 74 | end 75 | 76 | def add_book 77 | publisher = prompt_data('Publisher Name: ') 78 | cover_state = prompt_data('Choose cover state(good/normal/bad): ') 79 | publish_date = prompt_data('Published Date(YYYY/MM/DD): ') 80 | 81 | book = Book.new(publisher, cover_state, publish_date) 82 | add_extra_details(book) 83 | @books << book 84 | puts 'A book created successfully :)' 85 | end 86 | 87 | def add_album 88 | puts 'Add a music album' 89 | publish_date = prompt_data('Published Date(YYYY/MM/DD): ') 90 | on_spotify = prompt_data('Available on spotify (Y or N)?: ').downcase 91 | 92 | on_spotify = prompt_data('Please input Y or N: ').downcase while on_spotify != 'y' && on_spotify != 'n' 93 | 94 | album = MusicAlbum.new(on_spotify, publish_date) 95 | add_extra_details(album) 96 | @albums << album 97 | 98 | puts 'Music album created sucessfully' 99 | end 100 | 101 | def add_game 102 | puts 'Add a game' 103 | game_publish_date = prompt_data('Published date: ') 104 | multiplayer = prompt_data('Multiplayer: ') 105 | last_played = prompt_data('Last played at: ') 106 | game = Game.new(game_publish_date, multiplayer, last_played) 107 | add_extra_details(game) 108 | @games << game 109 | puts 'A game created successfully :)' 110 | end 111 | 112 | def exit 113 | save_books(@books) 114 | save_albums(@albums) 115 | save_games(@games) 116 | save_extra_details(@labels, @genres, @authors) 117 | puts 'Thanks for using our app' 118 | end 119 | 120 | private 121 | 122 | def prompt_data(text) 123 | puts text 124 | gets.chomp 125 | end 126 | 127 | def add_extra_details(klass) 128 | first_name = prompt_data("Add Author's First Name: ") 129 | last_name = prompt_data("Add Author's Last Name: ") 130 | title = prompt_data('Add Title: ') 131 | color = prompt_data('Choose Color: ') 132 | genre_name = prompt_data('What is its Genre name: ') 133 | 134 | label = Label.new(title, color) 135 | author = Author.new(first_name, last_name) 136 | genre = Genre.new(genre_name) 137 | 138 | store_extra_details(label, @labels) 139 | store_extra_details(author, @authors) 140 | store_extra_details(genre, @genres) 141 | 142 | klass.send(:label=, label) 143 | klass.send(:author=, author) 144 | klass.send(:genre=, genre) 145 | end 146 | 147 | def store_extra_details(klass, array) 148 | array << klass 149 | end 150 | end 151 | --------------------------------------------------------------------------------