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.
vendor
* attributes: name (required),
emailAddress, postalAddress, phoneNumber, URL
* sub-elements: vendorData (0-n)
* parent-elements: reml
menu
* 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
location
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
schedule
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
meal
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
cost
* attributes: unitAmount (decimal), totalAmount (decimal), currency
* sub-elements: vendorData (0-n)
* parent-elements: meal, mealItem, ingredient
mealItem
* 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
difficulty
An enumerated type consisting of Beginner, Normal, Expert
* parent-elements: mealItem
procedure
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
author
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
ingredient
* 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
substitute
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
nutrition
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
nutritionItem
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
NUnit
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
IUnit
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
category
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, " "