Labs/Ubiquity/Parser 2 API Conversion Tutorial

From MozillaWiki
< Labs‎ | Ubiquity
Jump to: navigation, search

Intro

Ubiquity 0.5 introduces an updated command API, reflecting the changes in the parser as well as the new ability to localize (some) commands. This tutorial will walk you through some of the key changes you will need to make. If you are interested in writing a new Ubiquity command from scratch, please refer to the updated Command Authoring Tutorial.

Screencast Tutorial for Parser 2 API Conversion

A screencast walkthrough showing how to convert a command to Parser 2 format for Ubiquity 0.5 is available here. If this is your first time converting a command, it is recommended that you first watch the walkthrough, as it provides a clear step-by-step visual explanation of what needs to be changed.

A note on (backwards) compatibility

Please note that Ubiquity 0.5 by default uses Parser 2 and Parser 1 (and the Parser 1 API) will be deprecated in the future. For the time being, however, it is possible to use Parser 1 in Ubiquity 0.5 by going to the Ubiquity settings page and turning off "use Parser 2".

Parser 1 API commands in Parser 2

Ubiquity Old API badge.png

All commands in the previous style (Parser 1 API) will be marked with an Old API badge in the Ubiquity command list, as seen on the right. Of these commands, those which do not take any arguments (no modifiers and no direct objects, in the Parser 1 API lingo) will be automatically converted and made to work with Parser 2, but all others will be ignored by Parser 2.

Parser 2 API commands in Parser 1

While Parser 2 commands were built with some backwards compatibility in mind, there are some notable limitations. With Ubiquity 0.5 in Parser 1 mode (the "Use Parser 2" setting turned off), Parser 2 commands should be usable but they will only respond to specific prepositions. For example, a command which expects a location will only work with the preposition "near" rather than "near," "on," and "at," which all work for this argument with Parser 2 (see below for more information on semantic roles). In addition, commands will only work in English and localization does not work in Parser 1 mode.

Note, however, that Parser 2-style commands will not work at all in Ubiquity 0.1.x installs.

We strongly encourage that all community commands be rewritten in Parser 2 format to take advantage of new (and upcoming) features.

Supporting both Parser 1 and Parser 2 in Your Command Feed

There is a property added to the Command API that allows you to present different commands to clients running Parser 1 and Parser 2, called CmdUtils.parserVersion. A command can provide different options to CmdUtils.CreateCommand() and behave differently depending on this value, allowing a single command feed to cater to whatever parser the user is using.

 if (CmdUtils.parserVersion == 2) {
   //parser 2 command here
 } else {
   //parser 1 command here
 }

Converting your command

names

Where Parser 1 expected a single default verb name in the property name and possible synonyms in an optional synonyms property, Parser 2 requires that there be a single names property with an array of possible verb names.

Parser 1 (old) API:

 CmdUtils.CreateCommand({
   name: 'find',
   synonyms: ['look-for', 'search'],
   ...

Parser 2 (new) API:

 CmdUtils.CreateCommand({
   names: ['find', 'look for', 'search'],
   ...

Note also that command names may now include spaces. (Hurray!)

Specifying semantic roles

The biggest change in the Parser 2 API is that arguments are now specified using abstract semantic roles instead of language-specific markers. (See Semantic roles in Parser 2 for further explanation of semantic roles and the rationale for this change.) Arguments which previously were specified in takes, direct objects, use the role "object". Arguments which were specified in modifiers are specified using different semantic roles, based on the grammatical function of the argument, instead of by its English preposition. For example, an argument introduced by "to" would now be marked with the role goal, and an argument introduced by "using" would be encoded as instrument. These arguments are then all specified together with their nountypes in an array as property arguments.

Parser 1 (old) API:

 CmdUtils.CreateCommand({
   ...
   takes: {query: noun_arb_text}
   modifiers: {'using': noun_type_searchengine}
   ...

Parser 2 (new) API:

 CmdUtils.CreateCommand({
   ...
   arguments: [ {role: 'object', nountype: noun_arb_text, label: 'query'},
                {role: 'instrument', nountype: noun_type_searchengine} ],
   ...

Please refer to Semantic roles in Parser 2 for a list of supported semantic roles with examples of their use. Note that the label parameter (used in specifying the object argument above) is optional.

In addition, the old-style (but rare) DOType and DOLabel specifications are also no longer supported.

Using semantic roles in preview and execute

As arguments are specified using semantic roles, arguments are then passed to the preview and execute methods based on their arguments as well. Both methods are handed an object with the semantic roles as the keys, and the argument data as the values, e.g.

 { object:     { text: 'chicken',
                 data: 'chicken',
                 ... },
   instrument: { text: 'goo',
                 data: 'Google',
                 ... }
 }

Note that the structure of the argument data itself (with requisite properties text, data, and html) is the same as with the Parser 1 API.

Instead of handing the direct object's data and the other arguments' data separately to preview and execute, Parser 2 hands it to them together as one argument:

Parser 1 (old) API:

 CmdUtils.CreateCommand({
   ...
   preview: function(pblock, object, modifiers) {
     var data = {};
     data.query = object.text;
     data.engine = modifiers.using.text;
     pblock.innerHTML = CmdUtils.renderTemplate(
                          "Searching for ${query} using ${engine}.",
                          data );
   },
   execute: function(object, modifiers) {
     var data = {};
     data.query = object.text;
     data.engine = modifiers.using.text;
     displayMessage(CmdUtils.renderTemplate(
                     "Searching for ${query} using ${engine}.",
                     data );
   },
   ...

Parser 2 (new) API:

 CmdUtils.CreateCommand({
   ...
   preview: function(pblock, args) {
     var data = {};
     data.query = args.object.text;
     data.engine = args.instrument.text;
     pblock.innerHTML = CmdUtils.renderTemplate(
                          "Searching for ${query} using ${engine}.",
                          data );
   },
   execute: function(args) {
     var data = {};
     data.query = args.object.text;
     data.engine = args.instrument.text;
     displayMessage(CmdUtils.renderTemplate(
                     "Searching for ${query} using ${engine}.",
                      data );
   },
   ...

As always, it is best to first check whether the requisite arguments were filled in the parser before using it.

Making your command localizable

More detailed information on making commands localizable can be found in the tutorial Making Commands Localizable.

Ubiquity doesn't yet support the localization of commands which are not bundled with Ubiquity itself. However, the localization of community commands is being planned, so making your command localizable now will future-proof your command and will later open your command up to a much wider target audience.

In addition to the Parser 2 API changes listed above, the main change to be made is to wrap any localizable strings in your preview and execute methods with the function _(). This _() command (familiar to anyone with prior experience with Gettext) handles the localization by finding the appropriate matching localized string on preview or execution.

 displayMessage(_('No results were found.'));

In the case above, _() will then take care of finding appropriate localizations of the string "No results were found" for your command and, if available, display that string instead.

The _() function also handles renderTemplateing if you also give it an object with replacement variables. For examples, you can write the following:

 displayMessage(_("${number} results found.", {number: numOfResults}))

By default, this will display, for example, "3 results found." if numOfResults == 3 and something like "Il y a 3 résultats" given a French localization of "Il y a ${number} résultats". This templating can also be used to handle pluralization—please refer to Making Commands Localizable for more information.

There is no need to wrap any properties of the commands (names, help, author, etc.) in _(), nor do you need to wrap string previews in _(), in order for the command to be localizable. These direct properties of the command are automatically localizable as long as they are set in the original command.

More tips on making strings localizable can be found in Making Commands Localizable.

References