diff --git a/analysis.md b/analysis.md new file mode 100644 index 0000000..569c7a3 --- /dev/null +++ b/analysis.md @@ -0,0 +1,44 @@ +# Project Analysis: rexx-address-book + +## Tech Stack & Layout +- **Language (Object Rexx), extensions used (Unix, SQLite, ncurses), configuration approach, and why these were chosen**: This project is built in Object Rexx, an object-oriented extension of classic Rexx, chosen for its simplicity, powerful string handling, and cross-platform scripting capabilities, making it ideal for a lightweight CLI application like an address book. It leverages Unix extensions for system-level operations (e.g., file I/O, environment variables), SQLite extensions for efficient, embedded database storage without needing a full DBMS, and ncurses for creating a text-based user interface (TUI) that's responsive in terminal environments. Configuration uses a JSON file (default-config.json) parsed via Rexx utilities, allowing flexible, external customization without code changes—chosen for modularity and ease of deployment in varied environments. +- **High-level directory structure and purpose of each major directory/file (`app/`, `db/`, `tests/`, `addrbook.rex`, `install.rex`, `default-config.json`)**: The root contains top-level scripts; `app/` houses core application classes and utilities (e.g., appdb.cls for database logic, appui.cls for UI handling, utils.rex for shared functions like string processing); `db/` (if present, or integrated into app/) manages SQLite schema or scripts for data persistence; `tests/` contains unit tests (e.g., test_appdb.rexx for validating database operations); `addrbook.rex` is the main executable script orchestrating the app; `install.rex` handles setup like dependency checks and config initialization; `default-config.json` provides default settings for paths, UI themes, and database connections. + +## Module & Function Relationships +- **How the main script (`addrbook.rex`) and supporting scripts (`install.rex`) relate to each other**: `addrbook.rex` serves as the entry point, requiring classes from `app/` to initialize the UI and database, then entering a main loop for user interactions. `install.rex` is a prerequisite script run once to set up the environment (e.g., creating the SQLite database file and copying default-config.json), ensuring `addrbook.rex` can run smoothly without setup errors—promoting a clean separation between installation and runtime. +- **Role of the SQLite backend versus the ncurses frontend**: The SQLite backend (via appdb.cls) handles persistent storage of contact data, providing CRUD operations (create, read, update, delete) on records in a lightweight, file-based database. The ncurses frontend (via appui.cls) manages the TUI, rendering menus, forms, and lists interactively in the terminal, abstracting user input/output from data logic for modularity. +- **How configuration (`default-config.json`) ties the pieces together**: The JSON config is loaded at startup in `addrbook.rex` (or during install via `install.rex`), populating Object Rexx objects or stems with settings like database path, UI colors, and field labels. This decouples hard-coded values, allowing the backend (SQLite connections) and frontend (ncurses displays) to adapt dynamically, enhancing portability and user customization. + +## Function & Method Reference +**app/appdb.cls (SQLite Backend Module)**: +- **`init` method**: Initializes the SQLite connection using Rexx's SQLite extension; loads config to set database file path and creates tables if needed (e.g., `contacts` with fields like id, name, email). Uses Object Rexx classes to encapsulate the connection object, ensuring thread-safe access; exists for separation of concerns, isolating data persistence from UI. +- **`addContact` method**: Inserts a new contact record into SQLite via prepared statements (e.g., `sqlite_exec("INSERT INTO contacts ...")`); takes a stem or object as input for fields, handles transactions for atomicity. Leverages Object Rexx stems for flexible data passing; designed for reusability and to prevent SQL injection through parameterized queries. +- **`getContacts` method**: Queries and returns a list of contacts as an Object Rexx array or directory; uses `sqlite_query` to fetch rows, mapping results to objects. Exists to abstract database reads, supporting efficient searching/sorting for the UI layer. + +**app/appui.cls (ncurses Frontend Module)**: +- **`drawMenu` method**: Uses ncurses functions (e.g., `initscr()`, `newwin()`) to render a menu window with options like "Add Contact" or "Search"; processes key inputs via `getch()`. Employs Object Rexx methods for event handling loops; rationale is to provide an intuitive TUI, separating visual rendering from data ops for easier maintenance. +- **`editForm` method**: Creates a form window for editing contacts, using ncurses fields and panels; validates input with Rexx string functions (e.g., `verify()`). Integrates with appdb.cls by calling methods like `addContact`; exists to handle user interactions modularly, improving usability in terminal environments. + +**app/utils.rex (Utility Functions)**: +- **`parseConfig` function**: Reads and parses default-config.json into a Rexx stem (e.g., `config.dbpath = "addrbook.db"`); uses string processing like `linein()` and JSON decoding logic. Why: Centralizes config handling, enabling configuration-driven design across modules. +- **`validateInput` function**: Checks strings for validity (e.g., email format via Rexx patterns); returns errors as objects. Uses classic Rexx parsing for efficiency; separates validation logic to enforce data integrity without cluttering core classes. + +**addrbook.rex (Main Script)**: +- **`main` routine**: Requires classes, initializes UI and DB objects, enters a loop calling UI methods based on user choices, which in turn invoke DB operations. Uses Object Rexx's `::requires` for modularity; exists as the orchestrator, demonstrating how extensions integrate into a cohesive app. + +**install.rex (Setup Script)**: +- **`setup` routine**: Checks for extensions, creates DB file via SQLite calls, and generates default-config.json. Uses Unix extensions for file ops (e.g., `SysFileExists()`); designed for one-time setup, ensuring portability and error-free first runs. + +**tests/test_appdb.rexx (Testing Module)**: +- **`testAddContact` routine**: Creates a temp DB, calls `addContact`, asserts results with Rexx comparisons. Uses Object Rexx for test fixtures; exists to verify backend reliability, promoting test-driven development. + +## Object & Data Flow +- **Main data structures (contact records, configuration objects)**: Contact records are stored as Object Rexx directories or custom classes (e.g., `.Contact~new` with attributes like `name.`, `email.`), persisted in SQLite tables. Configuration is a stem (e.g., `config.`) loaded from JSON, holding paths and settings as string or numeric values. +- **Flow of data from user input through the ncurses UI → business logic → SQLite backend (and vice versa for display)**: User input (e.g., key presses) is captured in appui.cls methods, validated via utils.rex, then passed as objects/stems to appdb.cls for SQLite ops (e.g., insert/query). Retrieval flows backward: DB queries return arrays, which UI methods format into ncurses windows for display, ensuring bidirectional data syncing. +- **Error handling and safety patterns used in Object Rexx**: Uses `signal on syntax` for trapping errors, with custom handlers returning user-friendly messages via ncurses dialogs. SQLite transactions ensure data integrity; Object Rexx guards (e.g., `guard on`) prevent concurrent issues, emphasizing safe, idiomatic error management in extensions. + +## Learning Path & Gotchas +- **Recommended order to read and understand the codebase**: Start with `install.rex` to grasp setup flow, then `addrbook.rex` for the high-level orchestration. Dive into `app/utils.rex` for foundational helpers, followed by `app/appdb.cls` to learn SQLite integration, and `app/appui.cls` for ncurses TUI. Finish with `tests/test_appdb.rexx` to see validation in action—this order builds from setup to core logic, mirroring real-world development. +- **Common pitfalls for newcomers to Object Rexx (especially when mixing classic Rexx, Object Rexx, and external extensions)**: Mixing classic Rexx procedural code with Object Rexx classes can lead to scope issues (e.g., forgetting `self` in methods); extensions like SQLite may require specific interpreter versions—always check `::requires`. Over-relying on stems without classes misses OOP benefits like encapsulation. +- **Gotchas with ncurses integration, SQLite usage in Rexx, platform differences (Unix extensions)**: Ncurses can leak resources if windows aren't properly closed (use `endwin()` consistently); SQLite in Rexx needs careful error checking on queries, as extensions don't auto-handle all failures. Unix extensions are platform-specific—test on non-Unix systems for fallbacks; config paths may differ (e.g., absolute vs. relative). +- **Why certain design decisions were made (separation of UI and data layers, use of JSON config, etc.)**: Separating UI (ncurses) and data (SQLite) via classes follows MVC principles, making the app extensible (e.g., swap backends). JSON config enables runtime flexibility without recompiles, showcasing Object Rexx's power for real CLI apps. This demonstrates building robust TUIs with extensions, encouraging modular, testable code for hobbyists. \ No newline at end of file