PageRenderTime 52ms CodeModel.GetById 25ms RepoModel.GetById 0ms 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. This documentation section contains spice-specific plugin information. Its contents are relevant if you're doing anything related to spice.
  5. ## Spice Handle Functions
  6. [Index](https://github.com/duckduckgo/duckduckgo#index) / [Spice](#spice) / **Spice Handle Functions**
  7. ---
  8. Spice 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.
  9. The 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.
  10. Usually the Spice plugin flow works like this:
  11. * Spice plugin is triggered.
  12. * Spice handle function is called.
  13. * Spice handle function returns arguments.
  14. * Arguments are used to make a call to an external [JSONP](https://duckduckgo.com/?q=jsonp) API.
  15. * The external API returns a [JSON](https://duckduckgo.com/?q=JSON) object to the Spice callback function.
  16. * Spice callback function returns instant answer.
  17. * Instant answer formatted on screen.
  18. The 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**.
  19. ```perl
  20. package DDG::Spice::Twitter;
  21. use DDG::Spice;
  22. spice to => 'http://twitter.com/status/user_timeline/$1.json?callback={{callback}}';
  23. triggers query_lc => qr/^@([^\s]+)$/;
  24. handle matches => sub {
  25. my ($uname) = @_;
  26. return $uname if $uname;
  27. return;
  28. };
  29. 1;
  30. ```
  31. To 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.
  32. In situations where you want to trigger on sub-words, you can pass a regular expression like in this Twitter example.
  33. ```perl
  34. triggers query_lc => qr/^@([^\s]+)$/;
  35. ```
  36. The **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.
  37. In 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.
  38. The captured parts (matches) get passed to the **handle** function via the **@_** variable (a special Perl array variable).
  39. ```perl
  40. handle matches => sub {
  41. my ($uname) = @_;
  42. return $uname if $uname;
  43. return;
  44. };
  45. ```
  46. Previously 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.
  47. ```perl
  48. my ($uname) = @_;
  49. ```
  50. If we received a non-blank user name then we return it.
  51. ```perl
  52. return $uname if $uname;
  53. ```
  54. Otherwise, return nothing, which short circuits the eventual external call.
  55. ```perl
  56. return;
  57. ```
  58. When the username is returned we then plug it into the **spice to** definition.
  59. ```perl
  60. spice to => 'http://twitter.com/status/user_timeline/$1.json?callback={{callback}}';
  61. ```
  62. The **$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.
  63. The **{{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.
  64. At 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).
  65. **Back to [Index](https://github.com/duckduckgo/duckduckgo) | [Spice Overview](spice_overview.md) | [Basic tutorial](general.md#basic-tutorial)**