diff --git a/README.md b/README.md index d6d0893..b63f043 100644 --- a/README.md +++ b/README.md @@ -1,165 +1,290 @@ -# Phase 3 Project — Single-Script Active Record CLI +# QuestCLI -## Learning Goals +QuestCLI is a Ruby command-line application built with **Active Record** and **SQLite3** that allows players to create an adventurer, accept quests, complete objectives, earn experience, and level up. -- Build a database-backed Ruby application using Active Record -- Design and interact with data models using object-oriented Ruby -- Implement a multi-class CLI frontend -- Practice working with migrations, associations, and validations +Designed as an RPG-inspired quest manager, QuestCLI demonstrates object-oriented programming principles, database design, Active Record associations, validations, and full CRUD functionality through an interactive command-line interface. -## Introduction +--- -Congrats on getting through all the material for Phase 3! You've learned how to work with databases, design models with Active Record, and write object-oriented Ruby. Now it's time to bring those skills together into a full project. +# Features -This project will focus on building a Ruby command-line application that reads from and writes to a local SQLite3 database using Active Record — no web server required. +## Adventurer Management -By the end of the project, you'll have a functioning CLI that lets users interact with your data by creating, viewing, updating, and deleting records from the terminal. +- Create adventurers +- View all adventurers +- View individual adventurer details +- Update adventurer information +- Delete adventurers -## Requirements +## Quest Management -### Models +- Accept new quests +- View all quests +- View active quests +- View completed quests +- Update quest information +- Complete quests +- Abandon quests -- At least two model classes using `ActiveRecord::Base` -- A one-to-many relationship (`has_many` / `belongs_to`) -- At least one model with validations -- Display associated data where appropriate (e.g. listing a parent record's associated children) +## Progression System -### CLI +- Earn experience by completing quests +- Automatic level progression +- Prevent duplicate quest rewards +- Track active and completed quests -- At least two Ruby classes (e.g. a `Menu` class and a model-specific helper) -- A loop or menu interface -- Ability to create, view, update, and delete records -- Update prompts should display the current value before asking for a new one +--- -## Planning +# Domain Model -- Plan out your features -- Develop user stories - - "As [ a user ], I want [ to perform this action ] so that [ I can accomplish this goal ]." - - Features should not need you there to explain them to users - - Create a `user-stories.md` file and add your user stories there +QuestCLI contains two Active Record models with a one-to-many relationship. -## Project Pitches +- A **Player** has many **Quests** +- A **Quest** belongs to one **Player** -Before you start working on your project, you'll pitch your project idea to your instructors for approval and feedback. +```text +Player +------ +id +name +level +current_xp -For your project pitch, you should include: + 1 + │ + │ has many + ▼ -- The basic story of your application -- The core features of your MVP -- The data you plan to persist and how you will structure it -- Challenges you expect to face -- How you are meeting the requirements of the project +Quest +------ +id +title +description +difficulty +xp_reward +completed +player_id +``` -**MVP ASAP** — Focus on getting your minimum viable product working first! +--- -## Example Project Domains +# Technologies -You could build a **Book Tracker** app: +- Ruby +- Active Record +- SQLite3 +- Rake -- `Author` has many `Books` -- Users can: - - Create a new book - - List all books - - Update book details - - Delete a book - - View books by a specific author +--- -Or a **Workout Log**: +# Installation -- `WorkoutSession` has many `Exercises` -- Users can: - - Log a new workout - - Add exercises - - Update reps/weights - - View or delete past workouts +Clone the repository. -## Getting Started +```bash +git clone +``` -**Fork and clone** this repository to get started. +Navigate into the project. -Install dependencies: +```bash +cd questcli +``` + +Install dependencies. ```bash bundle install ``` -Create and migrate the database: +Create the database. ```bash bundle exec rake db:create +``` + +Run the migrations. + +```bash bundle exec rake db:migrate ``` -Optionally seed the database with starter data: +(Optional) Seed the database. ```bash bundle exec rake seed ``` -Run your CLI application: +Start the application. ```bash ruby cli/main.rb ``` -## Other Useful Commands +--- -Open a Pry console with your models loaded: +# Example Gameplay -```bash -bundle exec rake console +```text +======================== + QUESTCLI +======================== + +1. Manage Players +2. Manage Quests +3. View Quest Log +4. Exit ``` -Generate a new migration: +Accepting a quest: -```bash -bundle exec rake db:create_migration NAME=create_books -``` +```text +⚔ Quest Accepted! -## Project Structure +Quest: +Recover the Ancient Relic +Difficulty: +Medium + +Reward: +250 XP + +Status: +Active ``` -├── app/ -│ └── models/ # Your Active Record model classes go here -├── cli/ -│ └── main.rb # Entry point — your CLI menu lives here -├── config/ -│ └── environment.rb # Loads gems, DB connection, and models -├── db/ -│ ├── config.yml # Database connection settings -│ ├── migrate/ # Migration files -│ └── seeds.rb # Seed data -└── spec/ # RSpec tests (optional) + +Completing a quest: + +```text +🏆 Quest Complete! + ++250 XP + +Current XP: 950 + +LEVEL UP! + +You are now Level 4. ``` -## Project Tips +--- -- Sketch your domain model first using [dbdiagram.io](https://dbdiagram.io/) -- Use `bundle exec rake console` to test your models before building the CLI -- Use `binding.pry` for debugging -- Use `puts` and `pp` or gems like `tty-table` for formatted CLI output +# CRUD Functionality -## Sample Project +## Players -A complete implementation is available on the `sample-project` branch. It demonstrates: +### Create -- **Pet Tracker** domain with Owners and Pets -- Full CRUD with Active Record and a clean menu-driven CLI -- Object-oriented design with user-friendly output -- All required features including current value prompts for updates +Create a new adventurer. -To view the sample: +### Read -```bash -git checkout sample-project +View all adventurers or inspect an individual adventurer. + +### Update + +Modify an adventurer's information. + +### Delete + +Delete an adventurer and their associated quests. + +--- + +## Quests + +### Create + +Accept a new quest. + +### Read + +View all quests, active quests, completed quests, or quests belonging to a specific player. + +### Update + +Modify quest details. + +### Delete + +Abandon a quest. + +--- + +# Validations + +## Player + +- Name is required +- Name must be unique +- Level must be greater than zero +- Current experience cannot be negative + +## Quest + +- Title is required +- Difficulty is required +- Experience reward must be greater than zero +- Every quest must belong to a player + +--- + +# Project Structure + +```text +. +├── app +│ └── models +│ ├── player.rb +│ └── quest.rb +├── cli +│ └── main.rb +├── config +│ ├── database.yml +│ └── environment.rb +├── db +│ ├── migrate +│ └── seeds.rb +└── README.md ``` -See `SAMPLE_PROJECT_README.md` for detailed documentation. +--- + +# Future Improvements + +Future versions of QuestCLI may include: + +- Character classes +- Gold and economy system +- Inventory management +- Equipment +- NPC quest givers +- Random quest generation +- Quest categories +- Achievements +- Player statistics dashboard +- Multiple save files +- ASCII art and enhanced terminal styling + +--- + +# Learning Objectives + +This project demonstrates: + +- Object-Oriented Programming +- Active Record Associations +- Active Record Validations +- Database Migrations +- SQLite3 +- CRUD Operations +- Menu-Driven CLI Design +- Ruby Classes and Modules +- Separation of Concerns + +--- -## Resources +# Author -- [dbdiagram.io](https://dbdiagram.io/) -- [Active Record Basics](https://guides.rubyonrails.org/active_record_basics.html) -- [Active Record Associations](https://guides.rubyonrails.org/association_basics.html) -- [Active Record Validations](https://guides.rubyonrails.org/active_record_validations.html) +Thomas Correia diff --git a/Rakefile b/Rakefile index 0b7ef85..18ec232 100644 --- a/Rakefile +++ b/Rakefile @@ -4,11 +4,11 @@ require "standalone_migrations" StandaloneMigrations::Tasks.load_tasks desc "Start the console" -task :console => :environment do +task console: :environment do Pry.start end desc "Seed the database" -task :seed => :environment do +task seed: :environment do load "db/seeds.rb" -end \ No newline at end of file +end diff --git a/app/models/player.rb b/app/models/player.rb new file mode 100644 index 0000000..2eac1fd --- /dev/null +++ b/app/models/player.rb @@ -0,0 +1,15 @@ +class Player < ActiveRecord::Base + validates :name, presence: true, uniqueness: true + + validates :level, + numericality: { + only_integer: true, + greater_than: 0, + } + + validates :current_xp, + numericality: { + only_integer: true, + greater_than_or_equal_to: 0, + } +end diff --git a/cli/main.rb b/cli/main.rb index 33e117f..43bbb86 100755 --- a/cli/main.rb +++ b/cli/main.rb @@ -2,19 +2,4 @@ require_relative "../config/environment" -# TODO: Build your CLI application here! -# -# Requirements: -# - Be object-oriented (at least two classes) -# - Query and update the database directly via Active Record models -# - Parse and display records in a readable format -# - Accept user input and use it to create, update, and delete records -# - Use a loop or menu interface -# - Include current value prompts for updates (e.g. show the current value -# before asking the user what they want to change it to) - -puts "Welcome to your CLI Application!" -puts - -# TODO: Implement your CLI functionality here -puts "TODO: Build your CLI application" +QuestCLI.new.run diff --git a/cli/player_menu.rb b/cli/player_menu.rb new file mode 100644 index 0000000..1d08541 --- /dev/null +++ b/cli/player_menu.rb @@ -0,0 +1,173 @@ +class PlayerMenu + MENU_ACTIONS = { + "1" => :create_player, + "2" => :view_all_players, + "3" => :view_player_details, + "4" => :update_player, + "5" => :delete_player, + }.freeze + + def run + loop do + display_menu + choice = gets.chomp + + break if return_to_main_menu?(choice) + + perform_action(choice) + end + end + + private + + def display_menu + puts "\n========================" + puts " ADVENTURER MENU" + puts "========================" + puts "1. Create Adventurer" + puts "2. View All Adventurers" + puts "3. View Adventurer Details" + puts "4. Update Adventurer" + puts "5. Delete Adventurer" + puts "6. Return to Main Menu" + print "\nChoose an option: " + end + + def return_to_main_menu?(choice) + return false unless choice == "6" + + puts "\nReturning to main menu..." + true + end + + def perform_action(choice) + action = MENU_ACTIONS[choice] + + if action + send(action) + else + puts "\nInvalid choice. Please select an option from 1 to 6." + end + end + + def create_player + print "\nEnter adventurer name: " + name = gets.chomp + + player = Player.new(name: name) + + if player.save + puts "\nAdventurer created successfully!" + display_player(player) + else + display_errors(player) + end + end + + def view_all_players + players = Player.all + + if players.empty? + puts "\nNo adventurers found." + return + end + + puts "\n========================" + puts " ADVENTURERS" + puts "========================" + + players.each do |player| + display_player(player) + end + end + + def view_player_details + player = select_player + + return unless player + + puts "\n========================" + puts " ADVENTURER DETAILS" + puts "========================" + + display_player(player) + end + + def update_player + player = select_player + + return unless player + + puts "\nCurrent name: #{player.name}" + print "Enter a new name, or press Enter to keep the current name: " + + new_name = gets.chomp + new_name = player.name if new_name.empty? + + if player.update(name: new_name) + puts "\nAdventurer updated successfully!" + display_player(player) + else + display_errors(player) + end + end + + def delete_player + player = select_player + + return unless player + + puts "\nYou selected:" + display_player(player) + + print "\nAre you sure you want to delete this adventurer? (y/n): " + confirmation = gets.chomp.downcase + + if confirmation == "y" + player.destroy + puts "\nAdventurer deleted successfully." + else + puts "\nDeletion canceled." + end + end + + def select_player + players = Player.all + + if players.empty? + puts "\nNo adventurers found." + return nil + end + + puts "\nSelect an adventurer:" + + players.each do |player| + puts "#{player.id}. #{player.name}" + end + + print "\nEnter adventurer ID: " + player_id = gets.chomp + + player = Player.find_by(id: player_id) + + puts "\nAdventurer not found." unless player + + player + end + + def display_player(player) + puts "\nID: #{player.id}" + puts "Name: #{player.name}" + puts "Level: #{player.level}" + puts "Current XP: #{player.current_xp}" + puts "------------------------" + end + + def display_errors(record) + puts "\nUnable to save adventurer:" + + record.errors.full_messages.each do |message| + puts "- #{message}" + end + end +end diff --git a/cli/quest_cli.rb b/cli/quest_cli.rb new file mode 100644 index 0000000..135dbf1 --- /dev/null +++ b/cli/quest_cli.rb @@ -0,0 +1,60 @@ +class QuestCLI + MENU_ACTIONS = { + "1" => :manage_adventurers, + "2" => :manage_quests, + "3" => :view_quest_log, + }.freeze + + def run + loop do + display_main_menu + choice = gets.chomp + + break if exit_selected?(choice) + + perform_action(choice) + end + end + + private + + def display_main_menu + puts "\n========================" + puts " QUESTCLI" + puts "========================" + puts "1. Manage Adventurers" + puts "2. Manage Quests" + puts "3. View Quest Log" + puts "4. Exit" + print "\nChoose an option: " + end + + def exit_selected?(choice) + return false unless choice == "4" + + puts "\nThanks for using QuestCLI!" + true + end + + def perform_action(choice) + action = MENU_ACTIONS[choice] + + if action + send(action) + else + puts "\nInvalid choice. Please select an option from 1 to 4." + end + end + + def manage_adventurers + PlayerMenu.new.run + end + + def manage_quests + puts "\nQuest management will be added in a future feature." + end + + def view_quest_log + puts "\nQuest log will be added in a future feature." + end +end diff --git a/config/environment.rb b/config/environment.rb index f7f8fa8..95ac6d0 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -9,8 +9,16 @@ # Set up database connection directly via Active Record require "yaml" + db_config = YAML.load_file(File.join(__dir__, "database.yml")) -ActiveRecord::Base.establish_connection(db_config[ENV.fetch("RACK_ENV", "development")]) + +ActiveRecord::Base.establish_connection( + db_config[ENV.fetch("RACK_ENV", "development")] +) # Require in all model files require_all "app/models" + +# Require reusable CLI classes +require_relative "../cli/player_menu" +require_relative "../cli/quest_cli" diff --git a/db/migrate/20260727192309_create_players.rb b/db/migrate/20260727192309_create_players.rb new file mode 100644 index 0000000..5a36f3c --- /dev/null +++ b/db/migrate/20260727192309_create_players.rb @@ -0,0 +1,11 @@ +class CreatePlayers < ActiveRecord::Migration[8.0] + def change + create_table :players do |t| + t.string :name, null: false + t.integer :level, null: false, default: 1 + t.integer :current_xp, null: false, default: 0 + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..49cb158 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,21 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[7.2].define(version: 2026_07_27_192309) do + create_table "players", force: :cascade do |t| + t.string "name", null: false + t.integer "level", default: 1, null: false + t.integer "current_xp", default: 0, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end +end diff --git a/user-stories.md b/user-stories.md new file mode 100644 index 0000000..43ae638 --- /dev/null +++ b/user-stories.md @@ -0,0 +1,405 @@ +# QuestCLI + +## Overview + +QuestCLI is a Ruby command-line application designed for users who want to manage an RPG-inspired adventurer and quest log. + +Users can create an adventurer, accept quests, update quest information, complete quests, earn experience points, level up, and abandon quests they no longer wish to pursue. + +The goal is to create a simple but engaging CLI experience that demonstrates CRUD operations, Active Record associations, database management, validations, and object-oriented Ruby design. + +--- + +# User Stories + +## Feature 1 - Create and Manage Adventurers + +**User Story:** As a user, I want to create and manage an adventurer so that I have a character who can accept quests and earn experience. + +### Details + +- Create a new player +- Enter a player name +- Assign a starting level of 1 +- Assign starting experience of 0 +- Save the player to the database +- View all saved players +- View an individual player's information +- Update a player's name +- Delete a player +- Display validation errors when player information is invalid +- Confirm before deleting a player + +--- + +## Feature 2 - Accept and View Quests + +**User Story:** As a user, I want my adventurer to accept quests so that they can begin working toward objectives and earning experience. + +### Details + +- Select the player receiving the quest +- Enter a quest title +- Enter a quest description +- Select a quest difficulty +- Assign an experience-point reward +- Save the quest to the database +- Automatically mark newly accepted quests as active +- Display a "Quest Accepted" confirmation message +- Display the newly accepted quest +- View all quests +- View quests belonging to a specific player +- View active quests +- View completed quests +- Display the player associated with each quest +- Display validation errors when quest information is invalid + +--- + +## Feature 3 - Update Quest Information + +**User Story:** As a user, I want to edit an existing quest so that I can correct mistakes or change the quest details. + +### Details + +- Select a quest to update +- Display the current quest title before requesting a new value +- Display the current description before requesting a new value +- Display the current difficulty before requesting a new value +- Display the current experience reward before requesting a new value +- Allow the user to keep an existing value by leaving the input blank +- Save updated quest information to the database +- Display confirmation after the quest is updated +- Display validation errors when updated information is invalid + +--- + +## Feature 4 - Complete Quests and Earn Experience + +**User Story:** As a user, I want my adventurer to complete accepted quests so that they earn experience and grow stronger. + +### Details + +- Select an active quest +- Confirm quest completion +- Mark the quest as completed +- Add the quest's experience reward to the associated player +- Save the updated quest +- Save the player's updated experience +- Display a "Quest Complete" confirmation message +- Display the amount of experience earned +- Display the player's updated level and experience +- Prevent a completed quest from being completed twice +- Prevent a completed quest from awarding experience more than once + +--- + +## Feature 5 - Player Level Progression + +**User Story:** As a user, I want my adventurer to level up after earning enough experience so that completing quests feels rewarding. + +### Details + +- Calculate a player's level using accumulated experience +- Check for a level increase after completing a quest +- Update the player's level when the required experience is reached +- Display a level-up message +- Display the player's current level +- Display the player's current experience +- Keep the level calculation inside the Player model + +--- + +## Feature 6 - Abandon Quests + +**User Story:** As a user, I want to abandon quests so that I can remove objectives I no longer want to pursue. + +### Details + +- Select a quest to abandon +- Display the selected quest before deletion +- Ask the user to confirm the deletion +- Delete the quest from the database +- Return to the quest menu after deletion +- Display confirmation after the quest is abandoned +- Handle an invalid quest selection without crashing the application + +--- + +## Feature 7 - CLI Navigation and User Feedback + +**User Story:** As a user, I want a clear menu-driven interface so that I can navigate the application and understand the result of each action. + +### Details + +- Display a main menu +- Display numbered menu choices +- Keep the application running inside a loop +- Allow the user to return to the previous menu +- Allow the user to exit the application +- Handle invalid menu choices +- Display clear success messages +- Display clear error messages +- Format player and quest information for readability +- Separate CLI responsibilities into at least two Ruby classes +- Use reusable helper methods to avoid repeated code + +--- + +# Quest Lifecycle + +```text +Quest Accepted + │ + ▼ +Active Quest + │ + ▼ +Quest Updated (optional) + │ + ▼ +Quest Completed + │ + ▼ +Experience Awarded + │ + ▼ +Player Levels Up (if enough XP) +``` + +A quest may also be abandoned before it is completed. + +--- + +# MVP Features + +The initial project scope will include: + +- Create players +- View players +- Update players +- Delete players +- Accept quests +- View all quests +- View quests belonging to a player +- Update quests +- Complete quests +- Abandon quests +- Award experience for completed quests +- Prevent completed quests from awarding experience twice +- Level up players +- Display validation errors +- Menu-driven CLI navigation + +--- + +# CLI Menus + +## Main Menu + +```txt +1. Manage Players +2. Manage Quests +3. View Quest Log +4. Exit +``` + +## Player Menu + +```txt +1. Create Player +2. View All Players +3. View Player Details +4. Update Player +5. Delete Player +6. Return to Main Menu +``` + +## Quest Menu + +```txt +1. Accept Quest +2. View All Quests +3. View Active Quests +4. View Completed Quests +5. Update Quest +6. Complete Quest +7. Abandon Quest +8. Return to Main Menu +``` + +--- + +# Planned Classes + +- Player +- Quest +- QuestCLI +- PlayerMenu +- QuestMenu + +The exact CLI class structure may be adjusted during development, but the application will contain at least two Ruby classes responsible for CLI behavior. + +--- + +# Database Structure + +## Players + +```txt +players +------- +id +name +level +current_xp +created_at +updated_at +``` + +## Quests + +```txt +quests +------ +id +title +description +difficulty +xp_reward +completed +player_id +created_at +updated_at +``` + +--- + +# Model Relationships + +A Player has many Quests. + +A Quest belongs to one Player. + +```text +Player + | + | has many + | + ▼ +Quest +``` + +```ruby +class Player < ActiveRecord::Base + has_many :quests, dependent: :destroy +end + +class Quest < ActiveRecord::Base + belongs_to :player +end +``` + +--- + +# Validations + +## Player Validations + +- Name must be present +- Name must be unique +- Level must be greater than 0 +- Current experience must be 0 or greater + +## Quest Validations + +- Title must be present +- Difficulty must be present +- Experience reward must be greater than 0 +- Completed must be either true or false +- Player must exist + +--- + +# Technical Challenges + +## Managing Active Record Associations + +Creating quests that correctly belong to a player and displaying the associated records. + +## Updating Existing Records + +Showing the current database value before asking the user to enter an updated value. + +## Experience and Level Progression + +Awarding experience when a quest is completed while preventing the same quest from awarding experience multiple times. + +## User Input Validation + +Handling invalid menu choices, missing records, blank values, and failed Active Record validations without crashing the application. + +## CLI Organization + +Separating menu navigation, user input, and database interactions into organized and reusable Ruby methods and classes. + +--- + +# Meeting Project Requirements + +## Active Record + +All database interactions will use Active Record. + +## Models + +The application will contain at least two Active Record models: + +- Player +- Quest + +## One-to-Many Relationship + +- Player has many Quests +- Quest belongs to Player + +## CRUD Operations + +The application will allow users to create, read, update, and delete player and quest records. + +## Validations + +At least one model will contain Active Record validations. + +## CLI Classes + +The application will contain at least two Ruby classes responsible for menu navigation and CLI behavior. + +## Menu Loop + +The application will use a loop-based menu that continues running until the user chooses to exit. + +## Update Prompts + +Update prompts will display the current value before asking the user to enter a replacement. + +--- + +# Stretch Goals + +These features are not required for the MVP: + +- Search quests by title +- Sort quests by difficulty +- Randomly generate quests +- Add gold rewards +- Add character classes +- Add quest categories +- Add deadlines to quests +- Display player statistics +- Display completion percentages +- Add achievements +- Add an inventory system +- Add items as a third model +- Add multiple experience progression systems +- Add ASCII art and enhanced CLI styling