diff --git a/.bundle/config b/.bundle/config
new file mode 100644
index 0000000..6d9698e
--- /dev/null
+++ b/.bundle/config
@@ -0,0 +1,2 @@
+---
+BUNDLE_PATH: "ruby-capstone"
diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
new file mode 100644
index 0000000..e284163
--- /dev/null
+++ b/.github/workflows/linters.yml
@@ -0,0 +1,20 @@
+name: Linters
+
+on: pull_request
+
+jobs:
+ rubocop:
+ name: Rubocop
+ runs-on: ubuntu-22.04
+
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-ruby@v1
+ with:
+ ruby-version: ">=3.1.x"
+ - name: Setup Rubocop
+ run: |
+ gem install --no-document rubocop -v '>= 1.0, < 2.0' # https://docs.rubocop.org/en/stable/installation/
+ [ -f .rubocop.yml ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/ruby/.rubocop.yml
+ - name: Rubocop Report
+ run: rubocop --color
\ No newline at end of file
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..f44b191
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,19 @@
+name: Tests
+
+on: pull_request
+
+jobs:
+ rspec:
+ name: RSpec
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-ruby@v1
+ with:
+ ruby-version: 3.1.x
+ - name: Setup RSpec
+ run: |
+ [ -f Gemfile ] && bundle
+ gem install --no-document rspec -v '>=3.0, < 4.0'
+ - name: RSpec Report
+ run: rspec --force-color --format documentation
\ No newline at end of file
diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 0000000..400e887
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,54 @@
+AllCops:
+ NewCops: enable
+ Exclude:
+ - "Guardfile"
+ - "Rakefile"
+ - "node_modules/**/*"
+
+ DisplayCopNames: true
+
+Layout/LineLength:
+ Max: 130
+Metrics/MethodLength:
+ Max: 25
+Metrics/AbcSize:
+ Max: 50
+Metrics/ClassLength:
+ Max: 150
+Metrics/BlockLength:
+ IgnoredMethods: ['describe']
+ Max: 30
+
+
+Style/Documentation:
+ Enabled: false
+Style/ClassAndModuleChildren:
+ Enabled: false
+Style/EachForSimpleLoop:
+ Enabled: false
+Style/AndOr:
+ Enabled: false
+Style/DefWithParentheses:
+ Enabled: false
+Style/FrozenStringLiteralComment:
+ EnforcedStyle: never
+
+Layout/HashAlignment:
+ EnforcedColonStyle: key
+Layout/ExtraSpacing:
+ AllowForAlignment: false
+Layout/MultilineMethodCallIndentation:
+ Enabled: true
+ EnforcedStyle: indented
+Lint/RaiseException:
+ Enabled: false
+Lint/StructNewOverride:
+ Enabled: false
+Style/HashEachMethods:
+ Enabled: false
+Style/HashTransformKeys:
+ Enabled: false
+Style/HashTransformValues:
+ Enabled: false
+Layout/EndOfLine:
+ Enabled: False
\ No newline at end of file
diff --git a/Database/schema.sql b/Database/schema.sql
new file mode 100644
index 0000000..f949bde
--- /dev/null
+++ b/Database/schema.sql
@@ -0,0 +1,64 @@
+-- create catalog of my things database
+CREATE DATABASE catalog;
+
+-- create label table
+
+CREATE TABLE label (
+ id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ title VARCHAR(100) NOT NULL,
+ color VARCHAR(100) NOT NULL
+);
+
+-- create book table
+CREATE TABLE book (
+ id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ author_id INT REFERENCES author(id),
+ published_date DATE,
+ publisher TEXT,
+ archived BOOLEAN NOT NULL DEFAULT FALSE,
+ cover_state VARCHAR(5),
+ label_id INT REFERENCES label(id)
+);
+
+-- create game table
+CREATE TABLE game (
+ id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ published_date DATE,
+ multiplayer BOOLEAN NOT NULL DEFAULT FALSE,
+ last_played_at DATE,
+ author_id int REFERENCES author(id),
+);
+
+-- create author table
+CREATE TABLE author (
+ id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ first_name VARCHAR(100) NOT NULL,
+ last_name VARCHAR(100) NOT NULL,
+);
+
+-- create genre table
+CREATE TABLE Genre(
+ id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
+ name VARCHAR(60) NOT NULL
+);
+
+-- create item table
+
+CREATE TABLE item(
+ id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
+ genre_id INT,
+ author_id INT,
+ label_id INT,
+ publish_date Date,
+ archived BOOLEAN,
+ FOREIGN KEY(genre_id) REFERENCES Genre(id) ON UPDATE CASCADE,
+ FOREIGN KEY(author_id) REFERENCES Author(id) ON UPDATE CASCADE,
+ FOREIGN KEY(label_id) REFERENCES Label(id) ON UPDATE CASCADE
+);
+
+-- create music_album table
+CREATE TABLE MusicAlbum(
+ id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
+ on_spotify BOOLEAN NOT NULL,
+ FOREIGN KEY(id) REFERENCES item(id) ON UPDATE CASCADE
+);
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..1a22d6d
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,3 @@
+source 'https://rubygems.org'
+
+gem 'rubocop', '>= 1.0', '< 2.0'
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..41ea951
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,34 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ ast (2.4.2)
+ json (2.6.3)
+ parallel (1.23.0)
+ parser (3.2.2.1)
+ ast (~> 2.4.1)
+ rainbow (3.1.1)
+ regexp_parser (2.8.0)
+ rexml (3.2.5)
+ rubocop (1.51.0)
+ json (~> 2.3)
+ parallel (~> 1.10)
+ parser (>= 3.2.0.0)
+ rainbow (>= 2.2.2, < 4.0)
+ regexp_parser (>= 1.8, < 3.0)
+ rexml (>= 3.2.5, < 4.0)
+ rubocop-ast (>= 1.28.0, < 2.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (>= 2.4.0, < 3.0)
+ rubocop-ast (1.28.1)
+ parser (>= 3.2.1.0)
+ ruby-progressbar (1.13.0)
+ unicode-display_width (2.4.2)
+
+PLATFORMS
+ x86_64-linux
+
+DEPENDENCIES
+ rubocop (>= 1.0, < 2.0)
+
+BUNDLED WITH
+ 2.4.13
diff --git a/LICENSE b/LICENSE
index f182899..8f1d0fa 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,21 @@
-MIT License
-
-Copyright (c) 2023 Brhanu Hailu
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+MIT License
+
+Copyright (c) 2023 Brhanu Hailu, Daniel Matama
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 75fc403..bf04f7d 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,189 @@
-# capstone-ruby-practice
\ No newline at end of file
+
+
+# 📗 Table of Contents
+
+- [📖 About the Project](#about-project)
+ - [🛠 Built With](#built-with)
+ - [Tech Stack](#tech-stack)
+ - [Key Features](#key-features)
+ - [🚀 Live Demo](#live-demo)
+- [💻 Getting Started](#getting-started)
+ - [Setup](#setup)
+ - [Prerequisites](#prerequisites)
+ - [Install](#install)
+ - [Usage](#usage)
+ - [Run tests](#run-tests)
+ - [Deployment](#triangular_flag_on_post-deployment)
+- [👥 Authors](#authors)
+- [🔭 Future Features](#future-features)
+- [🤝 Contributing](#contributing)
+- [⭐️ Show your support](#support)
+- [🙏 Acknowledgements](#acknowledgements)
+- [❓ FAQ (OPTIONAL)](#faq)
+- [📝 License](#license)
+
+
+
+# 📖 [Catalogue_Of_My_Things]
+
+**[Catalogue_Of_My_Things]** is a console app that will help to keep a record of different types of things one owns: books, music albums, movies, and games. Everything is based on the UML class diagram. The data is stored in JSON files but we also prepare a database with tables structure analogical the program's class structure.
+
+## 🛠 Built With
+
+### Tech Stack
+
+
+ Server
+
+
+
+
+Database
+
+
+
+
+
+### Key Features
+
+- **[User Interface]**
+- **[Add Database to store the whole data]**
+- **[preserve data in json format]**
+
+
(back to top)
+
+
+
+## 🚀 Video Link
+
+> - [Video Link](https://drive.google.com/file/d/1jBsHmWNemll98kqnGH5JWwbRKP6Eb4G9/view?usp=sharing)
+
+
+(back to top)
+
+
+
+## 💻 Getting Started
+
+To get a local copy up and running, follow these steps.
+
+### Prerequisites
+
+In order to run this project you need:
+
+```sh
+ install sudo apt-get install ruby-full
+```
+
+### Setup
+
+Clone this repository to your desired folder:
+
+```sh
+ cd my-folder
+ git clone https://github.com/brhanuhailu/ruby-capstone.git
+```
+
+
+### Install
+
+Install this project with:
+
+```sh
+ cd project
+ bundle install
+```
+### Usage
+
+To run the project, execute the following command:
+```sh
+ ruby main.rb
+```
+### Run tests
+
+To run Linter tests, run the following command:
+```sh
+ rubocop
+```
+### Run unit tests
+
+To run unit tests, run the following command:
+```sh
+ Rspec
+```
+### Deployment
+
+Not applicable.
+
+
+(back to top)
+
+
+
+## 👥 Authors
+
+👤 **Daniel Matama**
+
+- GitHub: [@Recillah-Khamala](https://github.com/danielmatama)
+- Twitter: [@recillahk](https://twitter.com/danmatama)
+- LinkedIn: [Recillah Khamala](https://www.linkedin.com/in/danielmatama-mwebesa/)
+
+👤 **Brhanu Hailu**
+
+- GitHub: [@brhanuhailu](https://github.com/brhanuhailu)
+- Twitter: [@tigrayfurtune](https://twitter.com/TigrayCountry)
+- LinkedIn: [LinkedIn](https://www.linkedin.com/in/brhanu-hailu-85578a246/)
+
+👤 **Yodit Abebe**
+
+- GitHub: [@yodit93](https://github.com/yodit93)
+- Twitter: [@@yodtwit](https://twitter.com/yodtwit)
+- LinkedIn: [LinkedIn](https://www.linkedin.com/in/yodit-abebe-ayalew/)
+
+(back to top)
+
+
+
+## 🔭 Future Features
+
+- [ ] **[Add more unit tests]**
+- [ ] **[Create complete databases]**
+
+(back to top)
+
+
+
+## 🤝 Contributing
+
+Contributions, issues, and feature requests are welcome!
+
+Feel free to check the [issues page](https://github.com/brhanuhailu/ruby-capstone/issues).
+
+(back to top)
+
+
+
+## ⭐️ Show your support
+
+If you like this project give a ⭐️ and We are so excited to accept you feedback to improve our profession.
+
+(back to top)
+
+
+
+## 🙏 Acknowledgments
+
+we would like to thank Microverse
+
+(back to top)
+
+
+
+
+## 📝 License
+
+This project is [MIT](https://github.com/brhanuhailu/ruby-capstone/blob/dev/LICENSE) licensed.
diff --git a/app.rb b/app.rb
new file mode 100644
index 0000000..39b089d
--- /dev/null
+++ b/app.rb
@@ -0,0 +1,152 @@
+require_relative 'book'
+require_relative 'label'
+require_relative 'item'
+require_relative 'game'
+require_relative './store/preserve_data'
+require_relative 'author'
+require_relative './src/music/genre'
+require_relative './src/music/music_album'
+require_relative './handler'
+require 'json'
+require 'date'
+
+class App < Handler
+ def initialize
+ super
+ @genres = []
+ @music_albums = []
+ @store = PreserveData.new
+ @books = File.empty?('./store/books.json') ? [] : @store.load_data('./store/books.json')
+ @labels = File.empty?('./store/labels.json') ? [] : @store.load_data('./store/labels.json')
+ @games = File.empty?('./store/games.json') ? [] : @store.load_data('./store/games.json')
+ @authors = File.empty?('./store/authors.json') ? [] : @store.load_data('./store/authors.json')
+ end
+
+ def list_books
+ @books.each do |book|
+ puts "#{book['id']} - #{book['author']} - #{book['published_date']}"
+ end
+ end
+
+ def list_labels
+ @labels.each do |label|
+ puts "#{label['id']} - #{label['title']} - #{label['color']}"
+ end
+ end
+
+ def create_book
+ puts 'Enter author'
+ author = gets.chomp
+ puts 'Enter published date (YYYY-MM-DD))'
+ published_date = gets.chomp
+ puts 'Enter publisher'
+ publisher = gets.chomp
+ puts 'Enter cover state'
+ cover_state = gets.chomp
+ puts 'Enter label title'
+ label_title = gets.chomp
+ puts 'Enter label color'
+ label_color = gets.chomp
+ label = create_label(label_title, label_color)
+ book = create_book_object(author, published_date, publisher, cover_state, label)
+ @books << book
+ @labels << label
+ @store.save_data(@books, './store/books.json')
+ @store.save_data(@labels, './store/labels.json')
+ puts 'Book created successfully'
+ end
+
+ def create_label(title, color)
+ label = Label.new(title, color)
+ { 'id' => label.id, 'title' => label.title, 'color' => label.color }
+ end
+
+ def create_book_object(author, published_date, publisher, cover_state, label)
+ book = Book.new(author, published_date, publisher, cover_state, label)
+ {
+ 'id' => book.id,
+ 'author' => book.author,
+ 'published_date' => book.published_date,
+ 'publisher' => book.publisher,
+ 'cover_state' => book.cover_state,
+ 'label' => book.label
+ }
+ end
+
+ def list_games
+ if @games.empty?
+ puts 'No games found in the system currently'
+ else
+ puts '=================================================================:'
+ puts 'List of Games:'
+ puts '=================================================================:'
+ puts 'ID - Game Name - Last Played Date - Published Date - Multiplayer'
+ @games.each do |game|
+ puts "#{game['id']} - #{game['game_name']} - #{game['last_played_at']} - #{game['publish_date']} - #{game['multiplayer']}"
+ end
+ puts '=================================================================:'
+ end
+ end
+
+ def list_authors
+ if @authors.empty?
+ puts 'No authors found in the system currently'
+ else
+ puts '=============================================:'
+ puts 'List of Authors:'
+ puts '=============================================:'
+ puts 'ID - First Name - Last Name'
+ @authors.each do |author|
+ puts "#{author['id']} - #{author['first_name']} - #{author['last_name']}"
+ end
+ puts '=============================================:'
+ end
+ end
+
+ def create_games
+ puts 'write Name of the game'
+ game_name = gets.chomp
+ puts 'Write last played date (YYYY-MM-DD))'
+ last_played_at = gets.chomp
+ puts 'Enter published date (YYYY-MM-DD))'
+ publish_date = gets.chomp
+ puts 'Enter multiplayer (yes/no)'
+ multiplayer = gets.chomp.downcase == 'yes'
+ puts 'Enter first name'
+ first_name = gets.chomp
+ puts 'Enter last name'
+ last_name = gets.chomp
+ author = create_author(first_name, last_name)
+ @authors << author
+ @store.save_data(@authors, './store/authors.json')
+ game = create_game_object(game_name, last_played_at, publish_date, multiplayer)
+ @games << game
+ @store.save_data(@games, './store/games.json')
+ puts 'Game is created successfully'
+ end
+
+ def create_author(first_name, last_name)
+ author = Author.new(first_name, last_name)
+ {
+ 'id' => author.id,
+ 'first_name' => author.first_name,
+ 'last_name' => author.last_name
+ }
+ end
+
+ def create_game_object(game_name, last_played_at, publish_date, multiplayer)
+ game = Game.new(game_name, last_played_at, publish_date, multiplayer)
+ {
+ 'id' => game.id,
+ 'game_name' => game.game_name,
+ 'last_played_at' => game.last_played_at,
+ 'publish_date' => game.publish_date,
+ 'multiplayer' => game.multiplayer
+ }
+ end
+
+ def exit_app
+ puts 'Thanks for using the app!'
+ exit
+ end
+end
diff --git a/author.rb b/author.rb
new file mode 100644
index 0000000..473da15
--- /dev/null
+++ b/author.rb
@@ -0,0 +1,22 @@
+require_relative 'item'
+
+class Author
+ attr_reader :id
+ attr_accessor :first_name, :last_name, :items
+
+ def initialize(first_name, last_name)
+ @id = Random.rand(1..1000)
+ @first_name = first_name
+ @last_name = last_name
+ @items = []
+ end
+
+ def full_name
+ "#{@first_name} #{@last_name}"
+ end
+
+ def add_author(item)
+ @items.push(item)
+ item.author = self
+ end
+end
diff --git a/book.rb b/book.rb
new file mode 100644
index 0000000..3aa4727
--- /dev/null
+++ b/book.rb
@@ -0,0 +1,16 @@
+require_relative 'item'
+
+class Book < Item
+ attr_accessor :publisher, :cover_state
+
+ def initialize(author, published_date, publisher, cover_state, label = nil)
+ super(nil, author, published_date, label)
+ @publisher = publisher
+ @cover_state = cover_state
+ end
+
+ def can_be_archived?
+ result = super
+ result || cover_state == 'bad'
+ end
+end
diff --git a/game.rb b/game.rb
new file mode 100644
index 0000000..19555ea
--- /dev/null
+++ b/game.rb
@@ -0,0 +1,26 @@
+require_relative 'item'
+require 'date'
+
+class Game < Item
+ attr_accessor :id, :game_name, :multiplayer, :last_played_at, :publish_date
+
+ def initialize(game_name, last_played_at, publish_date, multiplayer)
+ super(nil, nil, nil, publish_date)
+ @id = id || Random.rand(1..1000)
+ @game_name = game_name
+ @multiplayer = multiplayer
+ @last_played_at = Date.parse(last_played_at)
+ @publish_date = Date.parse(publish_date)
+ end
+
+ def move_to_archive
+ @archived = true if can_be_archived?
+ end
+
+ private
+
+ def can_be_archived?
+ age_in_years = Time.now.year - @last_played_at.year
+ age_in_years >= 2
+ end
+end
diff --git a/handler.rb b/handler.rb
new file mode 100644
index 0000000..5c0151d
--- /dev/null
+++ b/handler.rb
@@ -0,0 +1,70 @@
+require_relative './store'
+require_relative 'storage'
+
+class Handler
+ include JsonStorage
+ include Storage
+
+ def add_music_album
+ puts 'Album name: '
+ name = gets.chomp
+ puts 'Genre: '
+ genre_name = gets.chomp
+ @genres.push(Genre.new(genre_name))
+ store_genre_data
+
+ puts 'Date of publish [Enter date in format (yyyy-mm-dd)]'
+ publish_date = gets.chomp
+
+ puts 'Is it available on Spotify? Y/N'
+ on_spotify = gets.chomp.downcase
+ case on_spotify
+ when 'y'
+ @music_albums.push(MusicAlbum.new(name, publish_date, true))
+ when 'n'
+ @music_albums.push(MusicAlbum.new(name, publish_date, false))
+ end
+ puts 'Music album created'
+ store_music_data
+ end
+
+ def albums
+ music_data = get_data('./store/music_albums.json')
+ puts 'No music found' if music_data.empty?
+ music_data.each do |music_album|
+ puts "Album_name: #{music_album['music_name']} | On_spotify: #{music_album['music_on_spotify']}"
+ end
+ end
+
+ def genres
+ data = get_data('./store/genres.json')
+ puts 'No genre found' if data.empty?
+ data.each do |genre|
+ puts "Genre name: #{genre['genre_name']}"
+ end
+ end
+
+ def store_music_data
+ array = []
+ @music_albums.each do |music|
+ array << {
+ music_id: music.id,
+ music_name: music.name,
+ music_on_spotify: music.on_spotify
+
+ }
+ end
+ update_data(array, './store/music_albums.json')
+ end
+
+ def store_genre_data
+ array = []
+ @genres.each do |genre|
+ array << {
+ genre_id: genre.id,
+ genre_name: genre.name
+ }
+ end
+ update_data(array, './store/genres.json')
+ end
+end
diff --git a/item.rb b/item.rb
new file mode 100644
index 0000000..998ec96
--- /dev/null
+++ b/item.rb
@@ -0,0 +1,39 @@
+require 'date'
+
+class Item
+ attr_reader :id, :label, :author, :genre
+ attr_accessor :published_date, :archived
+
+ def initialize(genre, author, published_date, label = nil)
+ @id = Random.rand(1..1000)
+ @genre = genre
+ @author = author
+ @label = label
+ @published_date = published_date
+ @archived = false
+ end
+
+ def can_be_archived?
+ age_in_years = Time.now.year - Date.parse(@published_date).year
+ age_in_years >= 10
+ end
+
+ def move_to_archive
+ @archived = true if can_be_archived?
+ end
+
+ def label=(label)
+ @label = label
+ label.add_item(self) unless label.items.include?(self)
+ end
+
+ def author=(author)
+ @author = author
+ author.add_author(self) unless author.items.include?(self)
+ end
+
+ def genre=(genre)
+ @genre = genre
+ genre.add_item(self) unless genre.items.include?(self)
+ end
+end
diff --git a/label.rb b/label.rb
new file mode 100644
index 0000000..8b44ec5
--- /dev/null
+++ b/label.rb
@@ -0,0 +1,15 @@
+class Label
+ attr_accessor :title, :color, :items, :id
+
+ def initialize(title, color)
+ @id = Random.rand(1..1000)
+ @title = title
+ @color = color
+ @items = []
+ end
+
+ def add_item(item)
+ @items << item
+ item.label = self if item.label.nil?
+ end
+end
diff --git a/main.rb b/main.rb
new file mode 100644
index 0000000..a6f2d22
--- /dev/null
+++ b/main.rb
@@ -0,0 +1,95 @@
+require_relative 'app'
+
+class MainMenu
+ attr_reader :app
+
+ def initialize(app)
+ @app = app
+ end
+
+ def user_options(choice)
+ menu_options = {
+ 1 => method(:list_books),
+ 2 => method(:list_music),
+ 3 => method(:list_games),
+ 4 => method(:list_genres),
+ 5 => method(:list_labels),
+ 6 => method(:list_authors),
+ 7 => method(:create_book),
+ 8 => method(:create_music),
+ 9 => method(:create_games),
+ 0 => method(:exit_app)
+ }
+ if menu_options.key?(choice)
+ menu_options[choice].call(app)
+ else
+ puts 'Invalid option, please try again!'
+ end
+ end
+
+ def display
+ puts 'Welcome to your library'
+ puts 'Please choose an option'
+ puts '1. List all books'
+ puts '2. List all music albums'
+ puts '3. List all games'
+ puts '4. List all genres'
+ puts '5. List all labels'
+ puts '6. List all authors'
+ puts '7. Create a book'
+ puts '8. Create a music album'
+ puts '9. Create a game'
+ puts '0. Exit'
+ end
+
+ def list_books(app)
+ app.list_books
+ end
+
+ def list_music(app)
+ app.albums
+ end
+
+ def list_games(app)
+ app.list_games
+ end
+
+ def list_genres(app)
+ app.genres
+ end
+
+ def list_labels(app)
+ app.list_labels
+ end
+
+ def list_authors(app)
+ app.list_authors
+ end
+
+ def create_book(app)
+ app.create_book
+ end
+
+ def create_music(app)
+ app.add_music_album
+ end
+
+ def create_games(app)
+ app.create_games
+ end
+
+ def exit_app(app)
+ app.exit_app
+ end
+end
+
+def main
+ app = App.new
+ main_menu = MainMenu.new(app)
+ loop do
+ main_menu.display
+ choice = gets.chomp.to_i
+ main_menu.user_options(choice)
+ end
+end
+main
diff --git a/spec/author_spec.rb b/spec/author_spec.rb
new file mode 100644
index 0000000..7eac509
--- /dev/null
+++ b/spec/author_spec.rb
@@ -0,0 +1,25 @@
+require_relative './spec_helper'
+RSpec.describe Author do
+ describe '#add_author' do
+ let(:author) { Author.new('John', 'Doe') }
+ let(:item) { Item.new('gameone', 'hall', '2022-1-2', nil) }
+
+ it 'adds an item to the author' do
+ author.add_author(item)
+ expect(author.items).to include(item)
+ end
+
+ it 'sets the author of the item to the current author' do
+ author.add_author(item)
+ expect(item.author).to eq(author)
+ end
+ end
+
+ describe '#full_name' do
+ let(:author) { Author.new('John', 'Doe') }
+
+ it 'returns the full name of the author' do
+ expect(author.full_name).to eq('John Doe')
+ end
+ end
+end
diff --git a/spec/book_spec.rb b/spec/book_spec.rb
new file mode 100644
index 0000000..c6e67ad
--- /dev/null
+++ b/spec/book_spec.rb
@@ -0,0 +1,44 @@
+require_relative './spec_helper'
+describe Book do
+ before(:all) do
+ label = Label.new('Fiction', 'blue')
+ @book = Book.new('author', '2020-01-01', 'publisher', 'good', label)
+ end
+
+ describe '#initialize' do
+ it 'creates a new Book instance' do
+ expect(@book).to be_an_instance_of Book
+ end
+
+ it 'the author of the book should be "author"' do
+ expect(@book.author).to eq('author')
+ end
+
+ it 'the publication date of the book should be "2020-01-01"' do
+ expect(@book.published_date).to eq('2020-01-01')
+ end
+
+ it 'the publisher of the book should be "publisher"' do
+ expect(@book.publisher).to eq('publisher')
+ end
+
+ it 'the cover state of the book should be "good"' do
+ expect(@book.cover_state).to eq('good')
+ end
+
+ it 'the label of the book should be a Label instance' do
+ expect(@book.label).to be_kind_of Label
+ end
+ end
+
+ describe '#can_be_archived?' do
+ it 'should return true for less than 10 years' do
+ expect(@book.can_be_archived?).to eq(false)
+ end
+
+ it 'should return false for more than 10 years' do
+ @book.published_date = '2010-01-01'
+ expect(@book.can_be_archived?).to eq(true)
+ end
+ end
+end
diff --git a/spec/game_spec.rb b/spec/game_spec.rb
new file mode 100644
index 0000000..62e691b
--- /dev/null
+++ b/spec/game_spec.rb
@@ -0,0 +1,59 @@
+RSpec.describe Game do
+ let(:game) { Game.new('Game Name', '2022-01-01', '2022-01-01', 'Yes') }
+
+ describe '#initialize' do
+ it 'sets the game_name' do
+ expect(game.game_name).to eq('Game Name')
+ end
+
+ it 'sets the multiplayer' do
+ expect(game.multiplayer).to eq('Yes')
+ end
+
+ it 'sets the last_played_at' do
+ expect(game.last_played_at).to eq(Date.parse('2022-01-01'))
+ end
+
+ it 'sets the publish_date' do
+ expect(game.publish_date).to eq(Date.parse('2022-01-01'))
+ end
+ end
+
+ describe '#move_to_archive' do
+ context 'when the game is older than 2 years' do
+ let(:game) { Game.new('Game Name', '2020-01-01', '2022-01-01', 'Yes') }
+
+ it 'moves the game to archive' do
+ game.move_to_archive
+ expect(game.archived).to be true
+ end
+ end
+
+ context 'when the game is newer than 2 years' do
+ let(:game) { Game.new('Game Name', '2022-01-01', '2022-01-01', 'Yes') }
+
+ it 'does not move the game to archive' do
+ game.move_to_archive
+ expect(game.archived).to be false
+ end
+ end
+ end
+
+ describe '#can_be_archived?' do
+ context 'when the game is older than 2 years' do
+ let(:game) { Game.new('Game Name', '2020-01-01', '2022-01-01', 'Yes') }
+
+ it 'returns true' do
+ expect(game.send(:can_be_archived?)).to be true
+ end
+ end
+
+ context 'when the game is newer than 2 years' do
+ let(:game) { Game.new('Game Name', '2022-01-01', '2022-01-01', 'Yes') }
+
+ it 'returns false' do
+ expect(game.send(:can_be_archived?)).to be false
+ end
+ end
+ end
+end
diff --git a/spec/genre_spec.rb b/spec/genre_spec.rb
new file mode 100644
index 0000000..d34b4b7
--- /dev/null
+++ b/spec/genre_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+require_relative '../src/music/genre'
+RSpec.describe Genre do
+ let(:name) { 'Action' }
+ let(:id) { 42 }
+ item = Item.new('genre', 'name', 'published_date')
+ subject { Genre.new(name, id: id) }
+ describe '#initialize' do
+ it 'sets the name' do
+ expect(subject.name).to eq(name)
+ end
+ it 'sets the id' do
+ expect(subject.id).to eq(id)
+ end
+ it 'initializes an empty items array' do
+ expect(subject.items).to be_empty
+ end
+ end
+ describe '#add_item' do
+ it 'adds the item to the items array' do
+ subject.add_item(item)
+ expect(subject.items).to include(item)
+ end
+ it 'sets the genre on the item' do
+ expect(item).to receive(:genre=).with(subject)
+ subject.add_item(item)
+ end
+ end
+end
diff --git a/spec/item_spec.rb b/spec/item_spec.rb
new file mode 100644
index 0000000..9a8780d
--- /dev/null
+++ b/spec/item_spec.rb
@@ -0,0 +1,70 @@
+require_relative './spec_helper'
+describe Book do
+ before(:all) do
+ @label = Label.new('Fiction', 'blue')
+ @item = Item.new('genre', 'author', '2020-01-01', nil)
+ end
+
+ describe '#initialize' do
+ it 'creates a new item instance' do
+ expect(@item).to be_an_instance_of Item
+ end
+
+ it 'the genre of the item should be "genre"' do
+ expect(@item.genre).to eq('genre')
+ end
+
+ it 'the author of the item should be "author"' do
+ expect(@item.author).to eq('author')
+ end
+
+ it 'the publication date of the book should be "2010-01-01"' do
+ expect(@item.published_date).to eq('2020-01-01')
+ end
+
+ it 'the label of the book should be a Label instance' do
+ expect(@item.label).to be nil
+ end
+ end
+
+ describe '#can_be_archived?' do
+ it 'should return false for less than 10 years' do
+ expect(@item.can_be_archived?).to eq(false)
+ end
+
+ it 'should return true for more than 10 years' do
+ @item.published_date = '2010-01-01'
+ expect(@item.can_be_archived?).to eq(true)
+ end
+ end
+
+ describe '#move_to_archive' do
+ it 'should return true if can_be_archived? is true' do
+ @item.published_date = '2010-01-01'
+ expect(@item.move_to_archive).to eq(true)
+ end
+ end
+
+ describe '#label=' do
+ it 'should add the item to the label' do
+ @item.label = @label
+ expect(@label.items).to include(@item)
+ end
+ end
+
+ describe '#author=' do
+ it 'should add the item to the author' do
+ author = Author.new('John', 'Doe')
+ @item.author = author
+ expect(author.items).to include(@item)
+ end
+ end
+
+ describe '#genre=' do
+ it 'should add the item to the genre' do
+ genre = Genre.new('Fiction')
+ @item.genre = genre
+ expect(genre.items).to include(@item)
+ end
+ end
+end
diff --git a/spec/label_spec.rb b/spec/label_spec.rb
new file mode 100644
index 0000000..b46c754
--- /dev/null
+++ b/spec/label_spec.rb
@@ -0,0 +1,29 @@
+require_relative './spec_helper'
+
+describe Label do
+ before(:all) do
+ @label = Label.new('Fiction', 'blue')
+ end
+
+ describe '#initialize' do
+ it 'creates a new Label instance' do
+ expect(@label).to be_an_instance_of Label
+ end
+
+ it 'the title of the label should be "Fiction"' do
+ expect(@label.title).to eq('Fiction')
+ end
+
+ it 'the color of the label should be "blue"' do
+ expect(@label.color).to eq('blue')
+ end
+ end
+
+ describe '#add_item' do
+ it 'should add an item to the label' do
+ book = Book.new('author', '2020-01-01', 'publisher', 'good', nil)
+ @label.add_item(book)
+ expect(@label.items).to include(book)
+ end
+ end
+end
diff --git a/spec/music_album_spec.rb b/spec/music_album_spec.rb
new file mode 100644
index 0000000..243482d
--- /dev/null
+++ b/spec/music_album_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../src/music/music_album'
+require_relative '../item'
+describe MusicAlbum do
+ context 'Given an instance of the music album class' do
+ before :each do
+ @music = MusicAlbum.new('Kaweesi', '2013-09-03', true)
+ end
+ it 'creates an instance of the music album class' do
+ expect(@music).to be_instance_of MusicAlbum
+ end
+ it 'return true for the can be archived method' do
+ expect(@music.can_be_archived?).to be true
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..4416388
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,6 @@
+require_relative '../book'
+require_relative '../label'
+require_relative '../game'
+require_relative '../author'
+require_relative '../item'
+require_relative '../src/music/genre'
diff --git a/src/music/genre.rb b/src/music/genre.rb
new file mode 100644
index 0000000..56899a7
--- /dev/null
+++ b/src/music/genre.rb
@@ -0,0 +1,14 @@
+class Genre
+ attr_reader :items, :name, :id
+
+ def initialize(name, id: rand(1..1000))
+ @id = id
+ @name = name
+ @items = []
+ end
+
+ def add_item(item)
+ @items << item
+ item.genre = self
+ end
+end
diff --git a/src/music/music_album.rb b/src/music/music_album.rb
new file mode 100644
index 0000000..a93ae7b
--- /dev/null
+++ b/src/music/music_album.rb
@@ -0,0 +1,15 @@
+require_relative '../../item'
+class MusicAlbum < Item
+ attr_accessor :name, :publish_date, :on_spotify
+
+ def initialize(name, publish_date, on_spotify)
+ @id = Random.rand(1..1000)
+ super(nil, nil, publish_date, archived)
+ @name = name
+ @on_spotify = on_spotify
+ end
+
+ def can_be_archived?
+ super && @on_spotify
+ end
+end
diff --git a/storage.rb b/storage.rb
new file mode 100644
index 0000000..072d0a6
--- /dev/null
+++ b/storage.rb
@@ -0,0 +1,22 @@
+require 'json'
+
+module JsonStorage
+ def update_data(array, file_path)
+ opts = {
+ array_nl: "\n",
+ object_nl: "\n",
+ indent: ' ',
+ space_before: ' ',
+ space: ' '
+ }
+ json = JSON.generate(array, opts)
+ File.write(file_path, json)
+ end
+
+ def get_data(file_path)
+ return [] unless File.exist?(file_path)
+
+ file = File.read(file_path)
+ JSON.parse(file)
+ end
+end
diff --git a/store.rb b/store.rb
new file mode 100644
index 0000000..27bf353
--- /dev/null
+++ b/store.rb
@@ -0,0 +1,14 @@
+require_relative './src/music/music_album'
+require_relative './src/music/genre'
+require_relative './app'
+require 'json'
+
+module Storage
+ def save_music_albums; end
+
+ def load_music_albums; end
+
+ def save_genres; end
+
+ def load_genres; end
+end
diff --git a/store/authors.json b/store/authors.json
new file mode 100644
index 0000000..955d61d
--- /dev/null
+++ b/store/authors.json
@@ -0,0 +1,47 @@
+[
+ {
+ "id": 180,
+ "first_name": "bvcx",
+ "last_name": "nbcc"
+ },
+ {
+ "id": 631,
+ "first_name": "Brhanu",
+ "last_name": "Hailu"
+ },
+ {
+ "id": 483,
+ "first_name": "marki",
+ "last_name": "msess"
+ },
+ {
+ "id": 839,
+ "first_name": "Abebe",
+ "last_name": "kebede"
+ },
+ {
+ "id": 868,
+ "first_name": "kebede",
+ "last_name": "melaku"
+ },
+ {
+ "id": 393,
+ "first_name": "baedd",
+ "last_name": "dagdg"
+ },
+ {
+ "id": 113,
+ "first_name": "mario",
+ "last_name": "littel"
+ },
+ {
+ "id": 548,
+ "first_name": "yodit",
+ "last_name": "abebe"
+ },
+ {
+ "id": 753,
+ "first_name": "first",
+ "last_name": "last"
+ }
+]
\ No newline at end of file
diff --git a/store/books.json b/store/books.json
new file mode 100644
index 0000000..cd225c4
--- /dev/null
+++ b/store/books.json
@@ -0,0 +1,134 @@
+[
+ {
+ "id": 535,
+ "author": "author1",
+ "published_date": "2010-09-09",
+ "publisher": "pub",
+ "cover_state": "good",
+ "label": {
+ "id": 476,
+ "title": "fiction",
+ "color": "green"
+ }
+ },
+ {
+ "id": 256,
+ "author": "author2",
+ "published_date": "1998-07-30",
+ "publisher": "pubmed",
+ "cover_state": "bad",
+ "label": {
+ "id": 84,
+ "title": "title2",
+ "color": "orange"
+ }
+ },
+ {
+ "id": 345,
+ "author": "author3",
+ "published_date": "1765-03-14",
+ "publisher": "pubmed",
+ "cover_state": "bad",
+ "label": {
+ "id": 170,
+ "title": "science",
+ "color": "red"
+ }
+ },
+ {
+ "id": 858,
+ "author": "a",
+ "published_date": "2017-08-04",
+ "publisher": "pub",
+ "cover_state": "good",
+ "label": {
+ "id": 181,
+ "title": "history",
+ "color": "violet"
+ }
+ },
+ {
+ "id": 844,
+ "author": "yod",
+ "published_date": "220-09-25",
+ "publisher": "Biomed",
+ "cover_state": "good",
+ "label": {
+ "id": 157,
+ "title": "Science",
+ "color": "yellow"
+ }
+ },
+ {
+ "id": 973,
+ "author": "John",
+ "published_date": "1990-12-12",
+ "publisher": "Nasa",
+ "cover_state": "bad",
+ "label": {
+ "id": 918,
+ "title": "Robotic",
+ "color": "dark"
+ }
+ },
+ {
+ "id": 206,
+ "author": "a",
+ "published_date": "1888-02-02",
+ "publisher": "pub",
+ "cover_state": "bad",
+ "label": {
+ "id": 214,
+ "title": "iction",
+ "color": "red"
+ }
+ },
+ {
+ "id": 82,
+ "author": "yodi",
+ "published_date": "2023-2-1",
+ "publisher": "puplisher",
+ "cover_state": "good",
+ "label": {
+ "id": 569,
+ "title": "title",
+ "color": "color"
+ }
+ },
+ {
+ "id": 901,
+ "author": "dfsdf",
+ "published_date": "2023-2-2",
+ "publisher": "ghgh",
+ "cover_state": "goood",
+ "label": {
+ "id": 323,
+ "title": "trer",
+ "color": "fgfdg"
+ }
+ },
+ {
+ "id": 767,
+ "author": "a",
+ "published_date": "2012",
+ "publisher": "pub",
+ "cover_state": "bad",
+ "label": {
+ "id": 544,
+ "title": "science",
+ "color": "red"
+ }
+ },
+ {
+ "id": 418,
+ "author": "e",
+ "published_date": "56",
+ "publisher": "p",
+ "cover_state": "bad",
+ "label": {
+ "id": 462,
+ "title": "la",
+ "color": "c"
+ }
+ }
+]
\ No newline at end of file
diff --git a/store/games.json b/store/games.json
new file mode 100644
index 0000000..37d357e
--- /dev/null
+++ b/store/games.json
@@ -0,0 +1,58 @@
+[
+ {
+ "id": 146,
+ "game_name": "jhkjkk",
+ "last_played_at": "2023-5-2",
+ "publish_date": "2022-05-03",
+ "multiplayer": "jhhjhhj"
+ },
+ {
+ "id": 395,
+ "game_name": "valleyball",
+ "last_played_at": "2023-6-2",
+ "publish_date": "2022-05-03",
+ "multiplayer": "vlc"
+ },
+ {
+ "id": 601,
+ "game_name": "hwrhawi",
+ "last_played_at": "1-2-2023",
+ "publish_date": "2023-06-02",
+ "multiplayer": "mcccccc"
+ },
+ {
+ "id": 59,
+ "game_name": "merseir",
+ "last_played_at": "1-2-2023",
+ "publish_date": "2023-02-02",
+ "multiplayer": true
+ },
+ {
+ "id": 567,
+ "game_name": "abeb",
+ "last_played_at": "2023-02-05",
+ "publish_date": "2022-04-01",
+ "multiplayer": true
+ },
+ {
+ "id": 222,
+ "game_name": "Gametwo",
+ "last_played_at": "2023-02-01",
+ "publish_date": "2023-02-03",
+ "multiplayer": true
+ },
+ {
+ "id": 229,
+ "game_name": "mario",
+ "last_played_at": "2023-09-01",
+ "publish_date": "1990-04-03",
+ "multiplayer": true
+ },
+ {
+ "id": 826,
+ "game_name": "game",
+ "last_played_at": "2010-02-01",
+ "publish_date": "1998-02-01",
+ "multiplayer": true
+ }
+]
\ No newline at end of file
diff --git a/store/genres.json b/store/genres.json
new file mode 100644
index 0000000..2e76b1b
--- /dev/null
+++ b/store/genres.json
@@ -0,0 +1,6 @@
+[
+ {
+ "genre_id" : 422,
+ "genre_name" : "g"
+ }
+]
\ No newline at end of file
diff --git a/store/labels.json b/store/labels.json
new file mode 100644
index 0000000..9617913
--- /dev/null
+++ b/store/labels.json
@@ -0,0 +1,57 @@
+[
+ {
+ "id": 476,
+ "title": "fiction",
+ "color": "green"
+ },
+ {
+ "id": 84,
+ "title": "title2",
+ "color": "orange"
+ },
+ {
+ "id": 170,
+ "title": "science",
+ "color": "red"
+ },
+ {
+ "id": 181,
+ "title": "history",
+ "color": "violet"
+ },
+ {
+ "id": 157,
+ "title": "Science",
+ "color": "yellow"
+ },
+ {
+ "id": 918,
+ "title": "Robotic",
+ "color": "dark"
+ },
+ {
+ "id": 214,
+ "title": "iction",
+ "color": "red"
+ },
+ {
+ "id": 569,
+ "title": "title",
+ "color": "color"
+ },
+ {
+ "id": 323,
+ "title": "trer",
+ "color": "fgfdg"
+ },
+ {
+ "id": 544,
+ "title": "science",
+ "color": "red"
+ },
+ {
+ "id": 462,
+ "title": "la",
+ "color": "c"
+ }
+]
\ No newline at end of file
diff --git a/store/music_albums.json b/store/music_albums.json
new file mode 100644
index 0000000..c44dc44
--- /dev/null
+++ b/store/music_albums.json
@@ -0,0 +1,3 @@
+[
+
+]
\ No newline at end of file
diff --git a/store/preserve_data.rb b/store/preserve_data.rb
new file mode 100644
index 0000000..01aaa88
--- /dev/null
+++ b/store/preserve_data.rb
@@ -0,0 +1,10 @@
+class PreserveData
+ def save_data(data, filename)
+ data = JSON.pretty_generate(data)
+ File.write(filename, data)
+ end
+
+ def load_data(filename)
+ JSON.parse(File.read(filename))
+ end
+end