Ruby Diet Manager

Overview

For this lab you will implement a small diet manager in Ruby. To do this your system will maintain a database of foods - both basic foods and recipes composed of other foods (both recipes and basic). In addition, your system will maintain a log of foods that the user consumes each day. The commands will allow users to add and delete foods, to log and unlog foods for a given date, to save the log and food database, and to print a variety of reports.

The Food Database

The food database is a simple text file, named FoodDB.txt. The file has one text line per food, with the food and associated information in CSV format. There are two types of lines, representing the two types of food, basic and recipe:

name,b,calories
name,r,name,name,...,name

The first form, identified by a "b" in the second column, is a basic food. All we record for such foods are the name and the calories by unit serving. The second form, identified by an "r" in the second column, is a recipe. In this case, the third and following fields name the constituent foods in the recipe (if a given food is used in multiple units then it is simply listed multiple times).

Example Food Database

Orange (medium),b,67
Bread slice,b,80
Peanut butter,b,175
Jelly,b,155

PB&J Sandwich,r,Bread slice,Bread slice,Peanut butter,Jelly

The Diet Log

The diet log is also a simple CSV formatted text file, named DietLog.txt, with one line per food item logged; it is perfectly legitimate to have the same food listed multiple times for a given day. The log lines each have two fields: a date and a food name. Obviously, the food names must exist in the database. You are free to choose the date format that you find most appropriate. Take a look at the date oriented objects in the Ruby libraries.

Example Log Line

4/15/2024,PG&J Sandwich

The Interactive Command Language

quit

Saves the food database and the log (if they've changed) and exits. This also occurs if the program encounters end of file.


save

Saves the food database and log (if changed) without exiting.


new  food
 name,calories

Saves a new basic food and its calories. Reports an error and does nothing if a food of the given name is already in the database.


new  recipe
 name,name1,...,nameN

Saves a new recipe name consisting of the N named foods that follow. If name is already in the database, or if any of the N constituent food names are not in the database, report an error and do nothing.


print
name

Prints basic information on the named food (if there is no such food, print an error message and do nothing)
For a basic food, simply print the food name and the calories:

Orange 67

For recipes, the constituents are printed out hierarchically, using as many levels as required. A constituent food is printed with the number of units used in the recipe and the total calories.

PB&J Sandwich 490
  Bread (2) 160
  Peanut Butter 175
  Jelly 155


print all

Prints information on all the foods in the database, using the format(s) shown above.


find  
prefix

Prints information on all the foods in the database that begin with the given prefix. In matching the prefix, the case of the letters is ignored.


log
name

Adds one unit of the named food to the log for today. Simply prints a message if the food is not in the database.


log
name,date

Adds one unit of the named food to the log for the specified date. Simply prints a message if the food is not in the database.


delete
name,date

Removes one unit of the named food from the log for the given date. Does nothing if the food is not in the log on that date.


show

Shows the log of foods for today. If a food occurs several times, it is printed just once with the number of units following the name in parentheses.


show
date

Shows the log of foods for the given date as with plain show above.


show all

Shows the log of foods for all dates in the log, organized by ascending date. The log for each day is preceded by a line with the day's date.

The Ruby Program

You have great freedom in designing and implementing your solution. This project provides no skeleton files. The only constraints placed on your design is that you must define and use the following classes:

FoodDB
BasicFood
Recipe
Log
LogItem

Basic Food Information

There are many sites on the Web with food calorie information. Select 12-15 basic foods to initialize your database and to use in constructing recipes.

Submission

To encourage incremental development there are 5 levels of submissions. You may stop at any level and receive up to the cumulative grade at that point, but you must have submitted an entry for all preceding levels. The value of each level is split evenly between product code and unit tests.

At each level submit the production code AND unit tests that support the requirements for that level to a single directory named DietManager. When you have completed a level use the commit message "*** Level n Complete ***" before starting the next level. Only your last completed level will be graded, but your git log must demonstrate that you developed and tested the application incrementally to receive full credit.

Level 1 (10%)

At this level, the program can read the FoodDB.txt file, parse the CSV lines, and build up the internal food database. The commands supported are quit and print all.

Level 2 (10%)

For this level you will enhance Level 1 by implementing the print name and find prefix commands.

Level 3 (20%)

For this level you will add the commands that can update the database: new food, new recipe, save, and the change to quit to check for modification before exiting.

Level 4 (20%)

For this level you will add support for the log name and log name,date commands, as well as show all.

Level 5 (15%)

This is the last level, where you will add support for show, show date, and delete name,date.

Implementation Quality (15%)

Effective use of source control - frequency of submissions, quality of commit messages.

Clear, concise, and correct comments before classes, methods, and significant blocks of code.

Consistent indentation following the Ruby conventions (2 space indentation).

Names for classes, methods, and variables that clearly reflect the meaning and intention of the associated element.

Local variables of methods can be a tad more cryptic than instance and class variables.
Do not create global variables (though it is permissible to use those provided by the Ruby environment (e.g., $stdin).

Process Recording (10%)

As in previous projects, you are expected to estimate and track your effort for as many of the levels as you complete, using this Activity Journal.

 

Sample output

To avoid confusion please refer to this sample output. You shall assume that correct input is given for all commands.

Unit Testing

For the first release of Levels 1, 2 and 3 you need to supply at one unit test ruby file with at least two assertions in that unit test ruby file. This file must be called test_diet.rb. However, if you have already created individual test files for your classes please submit those files instead of the test_diet.rb file.

Here is an example of a unit test with a partial implementation of the BasicFood class.

Unit Test example
Partial Basic Food class example
Expected console output with some initial comments