REML: The Recipe Exchange Markup Language

REML is an XML schema that describes a file format for recipe exchange. I might have developed against an existing markup languages, but RecipeML is mired in licensing problems, and CookML is written in German. And I had my own ideas about recipe exchange that could better support commercial uses, such as the development of restaurant menus and cookbooks. In any case, this is a viable XML-based format for exchanging food recipes, developed from scratch by Gary Gocek.  If you have suggestions, please contact Gary.

The reml project here at SourceForge contains the schema and a reference implementation called reml-ref.  See below for more info on reml-ref.

The general notion is that an REML file contains menus (such as used in a restaurant, not a software menu).  Menus contain meals, which contain meal items, which contain ingredients and other data. For example, a menu might represent a cookbook with REML meals for chapters and REML meal items for recipes.  Or, a menu might represent a restaurant menu. A software package might offer a "customer" display mode with general comments and retail prices, and a "chef" display mode with full cooking instructions. REML is flexible; all you need is the right presentation tier for your needs.

Possible additions to the REML standard (not just the application) would be inventory support (are the ingredients on the shelf or should a shopping list be generated?) and ID tags that are intended to be unique. The problem with uniqueness is that given an XML structure, it's hard to determine uniqueness, especially in a multi-file-multi-user enterprise application. Unique IDs could be represented with vendorData elements, but vendorData is intended to be application specific, and not generic to the recipe exchange paradigm. The question is, would one application's IDs be usable by other applications, both at run-time and between runs, and is it worth trying to standardize this?

Another possible addition is a schedule element, so that meals can be associated with a date and time for preparation.  From a use case perspective, I don't see scheduling menus, but a single recipe might be scheduled.  One would not prepare only an entree for dinner, but one might want to schedule, say, Christmas cookies.  A schedule element could be added as a child of a meal or recipe.  The standard would not validate the dates, i.e., it would not be an REML error to schedule two meals for the same time.  An application would have to sort out such rules.

Scroll down for details on REML.  This is "just" documentation, so you'll want to use the actual schema saved at SourceForge to answer specific questions.  If you're not a techno-geek and want a plain English understanding of the connection between REML and using a recipe management program, feel free to send me an email.

reml-ref - a reference implementation of a REML compliant recipe manager

This is a Windows-based project, designed in a multi-tier fashion and implemented with MS Visual Studio .NET (C#).  reml-ref stores recipes natively in REML-conformant XML files. It would be more efficient for the application to store data in a database or other compact form, and only read and write REML for recipe exchange purposes, and it would be truly cool to implement a "recipe web service". However, the native XML storage mechanism requires no special installation at the client end, and I don't have server capacity to serve recipes from my database to the world.  reml-ref currently read, writes, and prints recipes.  See the SourceForge pages for downloads, news, feature requests, release status, etc.

Recipe Management Use Cases

I see several classes of users of recipe management systems. Most apps will not support all user types.  As features get more advanced, it becomes more important for users to enter data correctly.  For example, the string "1 cup milk" can be entered as an ingredient name in reml-ref, or the ingredient can be entered with quantity "1", unit "cup", and name "milk". If entered in three separate parts, a program could more easily look up nutritional information in a database, and then divide by the number of servings if that is also properly entered.

1. Home - Basic
This user deals with one recipe at a time. Simple categorization (such as "pasta") is usually enough. All they need is the basic recipe card format with ingredients, instructions, author, etc.  reml-ref goes beyond this (plain text import, copyright support, copy/paste between files), but reml-ref doesn't fully support the advanced home user (#2).

2. Home - Advanced
This home cook thinks about meals in advance. She wants to develop integrated meals using multiple recipes at a time. Basic nutritional information is needed. This user wants a scheduling capability, so that she can plan to prepare lasagna on Tuesday. She might want to be able to compare her meals to a diet plan, e.g., "Does this week's menu meet the limitations of the Atkins diet?" (And Atkins might want to send out meal plans in our exchange format.)

3. Home - Professional
This user needs specialized support, such as in the area of health care. She needs to be able to select recipes from a library that are appropriate for special needs (such as a diabetic patient), and she needs to be able to schedule multiple sets of meals. She needs a higher level of nutritional information than the Home - Advanced user. She needs budget, inventory and shopping list support.

4. Small Business
This user is like the Home - Professional user, but she needs wholesale and retail cost support so she can develop customer menus from the recipe management system. For example, a recipe for lasagna has cooking instructions as well as a description for the customer menu, and it has the wholesale cost to the restaurant as well as the retail price for the customer menu.

5. Health Care
This user prepares menus for patients in a professional health care setting, like a hospital or nursing home. There are all sorts of special nutritional, cost, and preparation considerations.

6. Enterprise
This user manages the menus for, say, a chain of restaurants, or a resort with multiple restaurants, or a cruise ship. She deals with wholesale suppliers from multiple countries. She deals with regional differences in the way meals are prepared, e.g., a foo-burger in El Paso might be spicier than a foo-burger in Topeka.

REML documentation

If not specified, attributes shown below are optional strings. Even when required, uniqueness is generally not required, so a user might have more than one cheesecake recipe. In some cases, it might seem like an attribute should have a more restrictive type, but I tried to be restrictive only when it seemed really important. For example, prepTime is just a string, because instead of "00:59:59.00", a home cook might want to say, "less than an hour".

reml (the root)
* attributes: version (required, must be 0.1 or 0.2 or 0.3 or 0.4 or 0.5), language, country
* sub-elements: vendor (0-n), vendorData (0-n), menu (1-n)
Notes: The standard is meant to be backward-compatible, so a REML file with an earlier version validates correctly, as long as the REML file uses a version enumerated  by the REML schema used for validation.  In other words, the set of acceptable versions is the list of all version numbers up to the version of the schema.  If a new version of the schema adds required elements, then it will not be backward compatible.  Almost every element can have zero or more vendorData children; this allows an application to add arbitrary amounts of non-standard data, but that's not a required application feature, and vendorData from one application may be ignored by other packages. Typically, an application that writes vendorData would identify the author of the vendorData, since the vendor element may be changed in the course of multiple exchanges.

* attributes: name (required), emailAddress, postalAddress, phoneNumber, URL
* sub-elements: vendorData (0-n)
* parent-elements: reml

* attributes: name (required)
* sub-elements: vendorData (0-n), userData (0-n), location (0-n), meal (0-n)
userData is intended to represent an end user's notes, if the software package allows them to be supplied.
* parent-elements: reml

A location is intended to refer to a geographical location, or some other place like a restaurant. For example, grits might be served at a restaurant franchise in South Carolina but not New York, and chicken wings might be served in New York but not South Carolina. With proper location values, a single REML file can serve a whole restaurant chain.
* attributes: country, region, locality, useWhenCurrent (boolean, default=true)
* sub-elements: vendorData (0-n)
REML does not have a notion of a user's location at any point in time, it only knows the location specified for an element.  When useWhenCurrent is true, an element is invalid if it does not specify the user's current location as determined by some software at run-time.
* parent-elements: menu, meal, mealItem, procedure, ingredient

A schedule is currently a single date-time.  This will probably be updated in the future to netter support repeating schedules.
* sub-elements: vendorData (0-n), prepDate (0-1).
* parent-elements: meal, mealItem

A meal is a collection of meal items. When you prepare a meal in your home, you don't just serve, say, a steak. You serve potatoes and dessert, too, and those items also need to be prepared, so they also need recipes. If all you want is an unrelated collection of meal items, you must wrap each one inside a meal element. The end users won't really know, assuming that no one actually tries to write REML by hand.
* attributes: name, servings (long integer)
* sub-elements: vendorData (0-n), userData (0-n), location (0-n), cost (0-1), mealItem (0-n), schedule (0-n)
Note that the meal name is optional. Note that meals and meal items have a cost and a number of servings. These are not constrained by the schema, so if a user says a meal costs a buck, and that each of the meal's five meal items also cost a buck, REML doesn't know if that's inconsistent or if the costs are cumulative.
* parent-elements: menu

* attributes: unitAmount (decimal), totalAmount (decimal), currency
* sub-elements: vendorData (0-n)
* parent-elements: meal, mealItem, ingredient 

* attributes: name (required), servings (long), pictureFile, prepTime, procedureTime
* sub-elements: vendorData (0-n), userData (0-n), createDate (date, 0-1), updateDate (date, 0-1), location (0-n), cost (0-n), difficulty (Beginner, Normal, Expert), procedure (0-n), author (0-n), nutrition (0-1), ingredient (0-n), category (0-n), schedule(0-n)
The attributes are generally unconstrained. The pictureFile might be a URL or a hard drive location. A hard drive location would not be very exchange-able, but I decided not to try to include binary data in the XML file.The category element can be arbitrary, or it can choose from a set of standard US-English-language categories (see below).
* parent-elements: meal

An enumerated type consisting of Beginner, Normal, Expert
* parent-elements: mealItem

In general, this contains the recipe preparation instructions. A meal item can contain any number of procedures, so I constrained the procedure element as shown.
* attributes: none
* sub-elements: vendorData (0-n), prepText (0-1), procedureText (0-1), location (0-n)
* parent-elements: mealItem

The author is the software user. The vendor is the software vendor.
* attributes: name (required), emailAddress, postalAddress, phoneNumber, URL
* sub-elements: vendorData (0-n)
* parent-elements: mealItem

* attributes: name (required), quantity-integer (long), quantity-numerator (long), quantity-denominator (long)
* sub-elements: vendorData (0-n), userData (0-n), location (0-n), cost (0-1), nutrition (0-1), substitute (0-1), IUnit (0-1)
The quantity attributes reflect that end users don't normally want to see "1.6667 cups", they'd rather see "1 2/3 cups". Software should avoid displaying, say, "2 0/0 pounds". The IUnit element can be arbitrary, or it can choose from a set of units (see below).
* parent-elements: mealItem

This is intended to allow the specification of an alternate ingredient within a recipe.
* attributes:name (required), quantity-integer (long), quantity-numerator (long), quantity-denominator (long)
* sub-elements: vendorData (0-n), userData (0-n), IUnit (0-1)
* parent-elements: ingredient

When known, the user can supply nutritional data.
* attributes: calories (decimal), pctCalFromFat (decimal), pctCalFromProtein (decimal), pctCalFromCarbs (decimal)
* sub-elements: vendorData (0-n), nutritionItem (0-n)
* parent-elements: mealItem

An ingredient can have any number of these. For example, an ingredient might provide multiple vitamins that can be separately listed.
* attributes: name, pctDailyReq (decimal), amount (decimal)
* sub-elements: vendorData (0-n), NUnit (0-1 nutrition units, see below)
The NUnit may be arbitrary or chosen from the enumerated type.
* parent-elements: nutrition

A nutrition unit is an enumerated type consisting of gram, g, microgram, mcg, milligram, mg, kilogram, kg, joule, kilojoule, calorie, InternationalUnit, IU.
* parent-elements: nutritionItem

An ingredient unit is an enumerated type consisting of bag, barrel, bottle, box, bunch, bushel, can, case, centimeter, cm, clove, cup, C, dash, drum, ear, gallon, gal, gram, g, handful, inch, in, jar, kilogram, kg, kilometer, k, lb, liter, li, meter, m, mile, mi, milliliter, ml, millimeter, mm, ounce, oz, package, pkg, part, peck, piece, pinch, pint, pt, pound, quart, qt, shot, slice, sprig, stick, splash, tablespoon, tbsp, T, teaspoon, tsp, t, yard, yd, ""
* parent-elements: ingredient, substitute

A meal unit's category is an enumerated type consisting of appetizer, beef, beverage, bread, cocktail, dairy, dessert, meat, pork, poultry, sauce, seafood, shellfish, soup, steak, tofu or soy, veal, vegetarian, quick and easy, just for fun, picnic, winter favorite, spring favorite, summer favorite, autumn favorite, sports themed, super bowl, holiday, christmas, halloween, thanksgiving, july 4, new year, easter, hannukah, yom kippur, rosh hashanah, kwanzaa, breakfast, lunch, brunch, dinner, weekend, college student, ethnic, african, american, asian, cajun, canadian, chinese, creole, european, french, german, greek, hawaiian, italian, japanese, kosher, mediterranean, mexican, middle eastern, polish, russian, south american, spanish, thai, ukranian, " "