PageRenderTime 27ms CodeModel.GetById 15ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/documentation/spice.md

https://github.com/a-West/duckduckgo
Markdown | 98 lines | 65 code | 33 blank | 0 comment | 0 complexity | fe8203d2c1889115bc6ac08e0be1d074 MD5 | raw file
 1# Spice
 2[Index](https://github.com/duckduckgo/duckduckgo#index) / **Spice**
 3
 4---
 5
 6This documentation section contains spice-specific plugin information. Its contents are relevant if you're doing anything related to spice.
 7
 8## Spice Handle Functions
 9[Index](https://github.com/duckduckgo/duckduckgo#index) / [Spice](#spice) / **Spice Handle Functions**
10
11---
12Spice plugins have **triggers** and **handle** functions like Goodies, as explained in the [Basic tutorial](http://github.com/duckduckgo/duckduckgo#basic-tutorial). The difference is that Spice handle functions don't return an instant answer directly like Goodies. Instead, they return arguments used to call a JavaScript callback function that then returns the instant answer.
13
14The JavaScript callback function is defined in another file and is explained in detail in the [Spice callback functions](#spice-callback-functions) section. For now let's concentrate on how it gets called via the Spice handle function.
15
16Usually the Spice plugin flow works like this:
17
18* Spice plugin is triggered.
19* Spice handle function is called.
20* Spice handle function returns arguments.
21* Arguments are used to make a call to an external [JSONP](https://duckduckgo.com/?q=jsonp) API.
22* The external API returns a [JSON](https://duckduckgo.com/?q=JSON) object to the Spice callback function.
23* Spice callback function returns instant answer.
24* Instant answer formatted on screen.
25
26The following is [an example](https://duckduckgo.com/?q=twitter+duckduckgo) that calls [the Twitter API](http://twitter.com/status/user_timeline/duckduckgo.json?callback=ddg_spice_twitter). Within your **zeroclickinfo-spice** fork, you would define a similar file in the **/lib/DDG/Spice/** directory. This file is named **Twitter.pm**.
27
28```perl
29package DDG::Spice::Twitter;
30
31use DDG::Spice;
32
33spice to => 'http://twitter.com/status/user_timeline/$1.json?callback={{callback}}';
34
35triggers query_lc => qr/^@([^\s]+)$/;
36
37handle matches => sub {
38    my ($uname) = @_;
39    return $uname if $uname;
40    return;
41};
42
431;
44```
45
46To refresh your memory, the **triggers** keyword tells the plugin system when to call a plugin. In the [Basic Tutorial](general.md#basic-tutorial) we discussed using the **start** keyword to specify trigger words that need to be present at the beginning of the query. Check out the section on [Triggers](general.md#triggers) for more information.
47
48In situations where you want to trigger on sub-words, you can pass a regular expression like in this Twitter example. 
49
50```perl
51triggers query_lc => qr/^@([^\s]+)$/;
52```
53
54The **query_lc** keyword tells the trigger system to examine a lower case version of the query. The **qr/regexp/** construct is the way to specify a compiled regular expression in Perl. 
55
56In this case **^@([^\s]+)$** says look for a **@** character at the beginning of the query (the **^**) and capture (using the parenthesis) everything that isn't a space ( **[^\s]** ) until you get to the end of the query (the **$**). Therefore it will match a query like *@duckduckgo* and capture the *duckduckgo* part.
57
58The captured parts (matches) get passed to the **handle** function via the **@_** variable (a special Perl array variable).
59
60```perl
61handle matches => sub {
62    my ($uname) = @_;
63    return $uname if $uname;
64    return;
65};
66```
67
68Previously we saw the use of the **remainder** keyword as in **handle remainder**, which works well for trigger words. In a case like this one that uses a regular expression trigger, the equivalent is **handle matches**, which passes the captured parts of the regular expression to the handle function. We look at what was passed and put it into the **$uname** variable.
69
70```perl
71    my ($uname) = @_;
72```
73
74If we received a non-blank user name then we return it.
75
76```perl
77    return $uname if $uname;
78```
79
80Otherwise, return nothing, which short circuits the eventual external call.
81
82```perl
83   return;
84```
85
86When the username is returned we then plug it into the **spice to** definition.
87
88```perl
89spice to => 'http://twitter.com/status/user_timeline/$1.json?callback={{callback}}';
90```
91
92The **$uname** value from the return statement will get inserted into the **$1** placeholder in the **spice to** line such that you can plug in parameters to the API call as needed. For passing multiple parameters, check out the [Advanced spice handlers](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/README.md#advanced-spice-handlers) section.
93
94The **{{callback}}** template gets plugged in automatically with the default callback value of **ddg_spice_twitter**. That last part (twitter) is a lowercase version of the plugin name with different words separated by the **_** character.
95
96At this point the response moves from the backend to the frontend. The external API sends a JSON object to the callback function that you will also define (as explained in the [Spice Frontend Development](https://github.com/duckduckgo/duckduckgo/blob/master/documentation/spice2.md#spice-frontend) section).
97
98**Back to [Index](https://github.com/duckduckgo/duckduckgo) | [Spice Overview](spice_overview.md) | [Basic tutorial](general.md#basic-tutorial)**