diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
new file mode 100644
index 0000000..3b33c28
--- /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@v2
+ - 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..f786456
--- /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@v2
+ - 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..508a3a9
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,52 @@
+AllCops:
+ NewCops: enable
+ Exclude:
+ - "Guardfile"
+ - "Rakefile"
+ - "node_modules/**/*"
+
+ DisplayCopNames: true
+
+Layout/LineLength:
+ Max: 120
+Metrics/MethodLength:
+ Max: 20
+Metrics/AbcSize:
+ Max: 50
+Metrics/ClassLength:
+ Max: 150
+Metrics/BlockLength:
+ AllowedMethods: ['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
\ No newline at end of file
diff --git a/GemFile b/GemFile
new file mode 100644
index 0000000..64f6df6
--- /dev/null
+++ b/GemFile
@@ -0,0 +1,2 @@
+source "https://rubygems.org"
+gem 'rubocop', '>= 1.0', '< 2.0'
diff --git a/GemFile.lock b/GemFile.lock
new file mode 100644
index 0000000..40454d9
--- /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.22.1)
+ parser (3.2.1.1)
+ ast (~> 2.4.1)
+ rainbow (3.1.1)
+ regexp_parser (2.7.0)
+ rexml (3.2.5)
+ rubocop (1.48.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.26.0, < 2.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (>= 2.4.0, < 3.0)
+ rubocop-ast (1.27.0)
+ parser (>= 3.2.1.0)
+ ruby-progressbar (1.13.0)
+ unicode-display_width (2.4.2)
+
+PLATFORMS
+ x64-mingw-ucrt
+
+DEPENDENCIES
+ rubocop (>= 1.0, < 2.0)
+
+BUNDLED WITH
+ 2.3.26
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6ca46d2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,169 @@
+
+
+# 📗 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)
+ - [Prerequisites](#prerequisites)
+ - [Setup](#setup)
+ - [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)
+- [📝 License](#license)
+
+
+
+# 📖 OOP School Library
+
+**OOP School Library** Is a Ruby OOP application that helps manage a library's collection of books, rentals, students, teachers, and borrowing transactions. The project aims to provide an efficient and user-friendly interface for librarians to add, and search for books, as well as manage borrower accounts and track borrowing transactions.
+
+## 🛠 Built With
+
+### Tech Stack
+
+
+ Client
+
+
+
+
+
+### Key Features
+
+- **Book class:** This class represents each book in the library, with attributes such as title, author.
+- **Rentals class:** This class represents library rentals, with attributes such as type of person, name, contact information, and borrowing history
+
+- **App class:** This class serves as the main interface for librarians, providing methods to add, shows all books, and search for books with specified id , as well as manage borrower accounts and track borrowing transactions
+
+
(back to top)
+
+**The project utilize various OOP principles, such as encapsulation, inheritance,polymorphism, Abstraction, associations, and decomposition to ensure a well-organized and easy-to-maintain codebase.**
+
+
+## 💻 Getting Started
+
+To get a local copy up and running, follow these steps.
+
+### Prerequisites
+
+In order to run this project you need:
+
+## **Prerequisites:**
+- Basic Knowledge of [Ruby](https://www.educative.io/blog/intro-to-ruby-tutorial) Programming Language
+- Basic Knowledge of [Git](https://www.freecodecamp.org/news/git-and-github-for-beginners/) Command
+
+- Watch [video](https://www.youtube.com/watch?v=RGOj5yH7evk) to get started in Git
+
+
+
+
+## **INSTALLATION**
+
+To install the application, follow these steps:
+
+- Install Ruby 2.7 or later on your system. You can download it from the official Ruby website: https://www.ruby-lang.org/en/downloads/
+
+- Clone this repository using Git:
+```bash
+git clone https://github.com/Ridwanullahi-code/OOP-School-Library.git
+```
+OR
+
+```bash
+git clone git@github.com:Ridwanullahi-code/OOP-School-Library.git
+```
+- Navigate to the project directory:
+```bash
+ cd OOP-School-Library
+```
+- Install the required gems:
+```bash
+ bundle install
+```
+## **USAGE**
+To run the application, follow these steps:
+- Navigate to the project directory:
+```bash
+ cd OOP-School-Library
+```
+- Run the application:
+```bash
+ ruby main.rb
+```
+- Follow the on-screen prompts to use the application.
+
+
+
+## 👥 Authors
+
+👤 **Ajayi Ridwan**
+
+- GitHub: [@Ridwanullahi-code](https://github.com/Ridwanullahi-code)
+- Twitter: [@Ridwanullahi22](https://twitter.com/twitterhandle)
+- LinkedIn: [LinkedIn](https://www.linkedin.com/in/ajayi-ridwan/)
+
+(back to top)
+
+
+
+## 🤝 Contributing
+If you would like to contribute to this project, please fork the repository, make your changes, and submit a pull request. Please make sure to follow the coding style and write tests for your changes.
+Feel free to check the [issues page](https://github.com/Ridwanullahi-code/OOP-School-Library/issues/)
+
+## 🚀 **About Me**
+
+I'm full stack software developer, Computer science, and Microverse student.
+
+**Stack:** Python, JavaScript, Bootstrap, Ruby, Rails, React, Redux. Available for hire!
+
+(back to top)
+
+
+
+## ⭐️ Show your support
+
+If you like this project...
+
+Feel free to give it a start
+
+(back to top)
+
+
+
+## 🙏 Acknowledgments
+
+> Give credit to everyone who inspired your codebase.
+> Thanks Microverse for providing us with the right material to aid this project development
+
+(back to top)
+
+(back to top)
+
+
+
+## 📝 License
+
+This project is [MIT](./LICENSE) licensed.
+
+_NOTE: we recommend using the [MIT license](https://choosealicense.com/licenses/mit/) - you can set it up quickly by [using templates available on GitHub](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/adding-a-license-to-a-repository). You can also use [any other license](https://choosealicense.com/licenses/) if you wish._
+
+(back to top)
\ No newline at end of file
diff --git a/app.rb b/app.rb
new file mode 100644
index 0000000..b434f67
--- /dev/null
+++ b/app.rb
@@ -0,0 +1,126 @@
+require_relative('./person')
+require_relative('./book')
+require_relative('./student')
+require_relative('./teacher')
+require_relative('./rental')
+
+class App
+ def initialize
+ @books = []
+ @people = []
+ @rentals = []
+ end
+
+ def all_books
+ if @books.empty?
+ puts 'No books in the library yet'
+ else
+ @books.each do |book|
+ puts "Title: #{book.title} Author: #{book.author}"
+ end
+ end
+ end
+
+ def all_people
+ if @people.empty?
+ puts 'No people in the library yet'
+ else
+ @people.each do |person|
+ puts "class: #{person.class} ID: #{person.id} Name: #{person.name} Age: #{person.age}"
+ end
+ end
+ end
+
+ def create_person
+ print 'Do you want to create a student (1) or teacher (2)? [input number]: '
+ type = gets.chomp.to_i
+ case type
+ when 1
+ create_student
+ when 2
+ create_teacher
+ else
+ puts 'Invalid input. Try again'
+ end
+ end
+
+ def create_student
+ print('Enter Student Age: ')
+ age = gets.chomp.to_i
+ print('Enter Student Name: ')
+ name = gets.chomp
+ print('Has parent permission? [Y/N]')
+ permission = gets.chomp.to_s.downcase == 'y'
+ @people << Student.new(age, name, parent_permission: permission)
+ puts 'Person created successfully'
+ end
+
+ def create_teacher
+ print('Enter Teacher Age: ')
+ age = gets.chomp.to_i
+ print('Enter Teacher Name: ')
+ name = gets.chomp
+ print 'Enter Teacher Specialization: '
+ special = gets.chomp
+ @people << Teacher.new(age, name, specialization: special)
+ puts 'Person created successfully'
+ end
+
+ def create_book
+ print 'Enter the title of the book: '
+ title = gets.chomp
+ print 'Enter author of the book: '
+ author = gets.chomp
+ @books << Book.new(title, author)
+ puts 'Book created successfully'
+ end
+
+ def create_rental
+ if @books.empty? || @people.empty?
+ puts "\e[31mNo books or people in the library yet.\e[0m"
+ return
+ end
+ book = select_book
+ person = select_person
+ if person && book
+ print 'Date: '
+ date = gets.chomp.to_s
+ @rentals << Rental.new(date, book, person)
+ puts 'Rental created successfully.'
+ else
+ puts "\e[31mInvalid person or book selected. Please try again.\e[0m"
+ end
+ end
+
+ def select_book
+ puts 'Select a book from the following list by number '
+ @books.each_with_index do |book, index|
+ puts "\e[34m#{index + 1}. Title: \"#{book.title}\", Author: #{book.author} \e[0m"
+ end
+ book_idx = gets.chomp.to_i - 1
+ @books[book_idx]
+ end
+
+ def select_person
+ puts 'Select a person from the following list by number (not id) '
+ @people.each_with_index do |person, index|
+ puts "\e[34m#{index + 1}. [#{person.class}] Name: #{person.name}, ID: #{person.id}, Age: #{person.age} \e[0m"
+ end
+ person_idx = gets.chomp.to_i - 1
+ @people[person_idx]
+ end
+
+ def all_rentals_for_person
+ print 'ID of person: '
+ person_id = gets.chomp.to_i
+ rentals = @rentals.select { |r| r.person.id.to_i == person_id }
+ if rentals.empty?
+ puts "\e[31mNo rentals found for this person. Please try again.\e[0m"
+ else
+ puts 'Rentals: '
+ rentals.each do |rental|
+ puts "\e[34mDate: #{rental.date}, Book: \"#{rental.book.title}\" by #{rental.book.author}\e[0m"
+ end
+ end
+ end
+end
diff --git a/base_decorator.rb b/base_decorator.rb
new file mode 100644
index 0000000..dffb7ef
--- /dev/null
+++ b/base_decorator.rb
@@ -0,0 +1,12 @@
+require_relative('./nameable')
+
+class BaseDecorator < Nameable
+ def initialize(nameable)
+ super()
+ @nameable = nameable
+ end
+
+ def correct_name
+ @nameable.correct_name
+ end
+end
diff --git a/book.rb b/book.rb
new file mode 100644
index 0000000..202c17f
--- /dev/null
+++ b/book.rb
@@ -0,0 +1,10 @@
+require_relative('./rental')
+class Book
+ attr_accessor :title, :author, :rentals
+
+ def initialize(title, author)
+ @title = title
+ @author = author
+ @rentals = []
+ end
+end
diff --git a/capitalize_decorator.rb b/capitalize_decorator.rb
new file mode 100644
index 0000000..1f5242d
--- /dev/null
+++ b/capitalize_decorator.rb
@@ -0,0 +1,8 @@
+require_relative('./base_decorator')
+require_relative('./person')
+
+class CapitalizeDecorator < BaseDecorator
+ def correct_name
+ @nameable.correct_name.capitalize
+ end
+end
diff --git a/classroom.rb b/classroom.rb
new file mode 100644
index 0000000..cf11a45
--- /dev/null
+++ b/classroom.rb
@@ -0,0 +1,19 @@
+require_relative './student'
+
+class Classroom
+ attr_accessor :label, :students
+
+ def initialize(label)
+ @label = label
+ @students = []
+ end
+
+ def add_student(student)
+ # saving each student object in an array
+ @students << student
+ student.classroom = self
+ end
+end
+
+s = Student.new('Micheal', '30', 'LT2')
+puts s
diff --git a/main.rb b/main.rb
new file mode 100644
index 0000000..817f41e
--- /dev/null
+++ b/main.rb
@@ -0,0 +1,42 @@
+require_relative './app'
+def main
+ app = App.new
+ loop do
+ puts "\n\e[32mPlease choose an option by entering a number: \e[0m"
+ puts '1. List all books'
+ puts '2. List all people'
+ puts '3. Create a person'
+ puts '4. Create a book'
+ puts '5. Create a rental'
+ puts '6. List all rentals for a given person id'
+ puts '7. Exit'
+ input = gets.chomp.to_i
+ exit_app if input == 7
+ run(app, input)
+ end
+end
+
+def run(app, input)
+ @options = {
+ 1 => app.method(:all_books),
+ 2 => app.method(:all_people),
+ 3 => app.method(:create_person),
+ 4 => app.method(:create_book),
+ 5 => app.method(:create_rental),
+ 6 => app.method(:all_rentals_for_person)
+ }
+ option = @options[input]
+ if option
+ option.call
+ else
+ puts "\e[31mInvalid option. Try again.\e[0m"
+ end
+end
+
+def exit_app
+ puts "\e[32mThank you for using this app!\e[0m"
+ sleep(1)
+ exit
+end
+
+main
diff --git a/nameable.rb b/nameable.rb
new file mode 100644
index 0000000..92bebdb
--- /dev/null
+++ b/nameable.rb
@@ -0,0 +1,5 @@
+class Nameable
+ def correct_name
+ raise NotImplementedError, 'Class must implemented'
+ end
+end
diff --git a/person.rb b/person.rb
new file mode 100644
index 0000000..918bcff
--- /dev/null
+++ b/person.rb
@@ -0,0 +1,50 @@
+require_relative('./nameable')
+# person class store person information and properties
+class Person < Nameable
+ attr_accessor :name, :age, :rentals
+ attr_reader :id
+
+ def initialize(age, name = 'Unknown', parent_permission: true)
+ super()
+ @id = generate_id
+ @age = age
+ @name = name
+ @parent_permission = parent_permission
+ @rentals = []
+ end
+
+ def display_info
+ { 'id' => @id, 'name' => @name, 'age' => @age }
+ end
+
+ def update_info(name, age)
+ @name = name
+ @age = age
+ end
+
+ def can_use_services
+ if of_age || @parent_permission
+ true
+ elsif !of_age || !@parent_permission
+ false
+ end
+ end
+
+ def correct_name
+ @name
+ end
+
+ def add_rental(date, book)
+ Rental.new(date, book, self)
+ end
+
+ private
+
+ def of_age
+ @age.to_i >= 18
+ end
+
+ def generate_id
+ rand(1..100)
+ end
+end
diff --git a/rental.rb b/rental.rb
new file mode 100644
index 0000000..673928c
--- /dev/null
+++ b/rental.rb
@@ -0,0 +1,15 @@
+require_relative('./book')
+require_relative('./person')
+require_relative('./student')
+
+class Rental
+ attr_accessor :date, :book, :person
+
+ def initialize(date, book, person)
+ @date = date
+ @book = book
+ @person = person
+ @book.rentals << self
+ @person.rentals << self
+ end
+end
diff --git a/student.rb b/student.rb
new file mode 100644
index 0000000..0cac085
--- /dev/null
+++ b/student.rb
@@ -0,0 +1,19 @@
+require_relative('./person')
+require_relative('./classroom')
+# student class store student information and properties
+class Student < Person
+ attr_accessor :classroom
+
+ def initialize(age, name = 'Unknown', parent_permission: true, classroom: nil)
+ @classroom = classroom
+ super(age, name, parent_permission: parent_permission)
+ end
+
+ def play_hook
+ '¯(ツ)/¯'
+ end
+
+ def add_classroom
+ @classroom.add_students(self)
+ end
+end
diff --git a/teacher.rb b/teacher.rb
new file mode 100644
index 0000000..9f52213
--- /dev/null
+++ b/teacher.rb
@@ -0,0 +1,14 @@
+require_relative('./person')
+# class to store teacher information and methods
+class Teacher < Person
+ attr_accessor :specialization
+
+ def initialize(age, name, parent_permission: false, specialization: nil)
+ @specialization = specialization
+ super(age, name, parent_permission: parent_permission)
+ end
+
+ def can_use_services
+ (of_age || !of_age) || (@parent_permission || !@parent_permission)
+ end
+end
diff --git a/trimmer_decorator.rb b/trimmer_decorator.rb
new file mode 100644
index 0000000..76360a5
--- /dev/null
+++ b/trimmer_decorator.rb
@@ -0,0 +1,14 @@
+require_relative('./base_decorator')
+require_relative('./person')
+require_relative('./capitalize_decorator')
+
+class TrimmerDecorator < BaseDecorator
+ def correct_name
+ name = @nameable.correct_name
+ if name.length > 10
+ name.slice(0, 10)
+ else
+ name
+ end
+ end
+end