zeroclickinfo-goodies/README.md

140 lines
5.7 KiB
Markdown
Raw Normal View History

2013-02-15 10:46:04 -08:00
DuckDuckHack Goodies
====
This documentation walks you through the process of writing a DuckDuckHack Goodie plugin.
2013-03-25 10:44:59 -07:00
Before reading this section, make sure you've read the [DuckDuckHack Intro Site](http://duckduckhack.com) and the [DuckDuckHack Developer's Overview](https://github.com/duckduckgo/duckduckgo/blob/master/README.md). If you're here to brush up on Goodie info, scroll down. If you're here to learn how to write Goodie Plugins, check out the [Goodies Overview](https://github.com/duckduckgo/duckduckgo#goodies-overview).
2011-08-09 10:35:23 -07:00
2013-04-24 11:02:00 -07:00
### Example
![morse code example](https://s3.amazonaws.com/ddg-assets/docs/goodie_example.png)
2013-02-15 10:46:04 -08:00
## Writing test files
Every goodie includes a test file in the `t` directory. For example, the **RouterPasswords** goodie uses the the test file `t/RouterPasswords.t`. This test file includes sample queries and answers, and is run automatically before every release to ensure that all goodies are triggering properly with correct answers. The test file is a Perl program that uses the Perl packages `DDG::Test::Goodie` and `Test::More` to function. Here's an annotated excerpt from `t/RouterPasswords.t` that you can use as a base:
```perl
#!/usr/bin/env perl
use strict;
use warnings;
# These modules are necessary for the functions we'll be running.
use Test::More;
use DDG::Test::Goodie;
# These zci attributes aren't necessary, but if you specify them inside your
# goodie, you'll need to add matching values here to check against.
zci answer_type => 'password';
zci is_cached => 1;
ddg_goodie_test(
[
# This is the name of the goodie that will be loaded to test.
'DDG::Goodie::RouterPasswords'
],
# This is a sample query, just like the user will enter into the DuckDuckGo
# search box.
'Belkin f5d6130' =>
test_zci(
# The first argument to test_zci is the plain text (default)
# returned from a goodie. If your goodie also returns an HTML
# version, you can pass that along explicitly as the second
# argument. If your goodie is random, you can use regexs instead of
# strings to match against.
'Default login for the BELKIN F5D6130: Username: (none) Password: password',
html => 'Default login for the BELKIN F5D6130:<br><i>Username</i>: (none)<br><i>Password</i>: password'
),
# You should include more test cases here. Try to think of ways that your
# plugin might break, and add them here to ensure they won't. Here are a
# few others that were thought of for this goodie.
);
# This function call is expected by Test::More. It makes sure the program
# doesn't exit before all the tests are run.
done_testing;
```
2013-02-15 10:46:04 -08:00
## Advanced Goodies
These advanced handle techniques are specific to Goodie plugins:
**Returning HTML**. &nbsp;Goodies return text instant answers by default, but can return simple HTML as well. In that case, simply attach the html version to the end of the return statement.
```perl
2013-05-24 16:36:34 -07:00
return $text, html => $html;
2013-02-15 10:46:04 -08:00
```
**Other zci keywords**. &nbsp;The Chars example sets the **is_cached** zci keyword. You can find other settable attributes in the [object documentation](https://metacpan.org/module/WWW::DuckDuckGo::ZeroClickInfo). For example, the [GoldenRatio Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/GoldenRatio.pm) sets the **answer_type** variable, which gets returned in the API.
```perl
zci answer_type => "golden_ratio";
```
2013-03-11 09:37:20 -07:00
## Location API
Sometimes, a plugin needs the user's location. This is where the Location API comes in. An example is the [Is it snowing?](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/Snow.pm) plugin:
2013-03-11 09:37:20 -07:00
```perl
# Phoenixville, Pennsylvania, United States
2013-03-14 11:50:30 -07:00
my $location = join(", ", $loc->city, $loc->region_name, $loc->country_name);
2013-03-11 09:37:20 -07:00
```
2013-03-11 09:40:34 -07:00
When testing on `duckpan`, the plugin will always point you to "Phoenixville, Pennsylvania, United States," but don't worry, because it will show the real location once it's live.
2013-03-11 09:37:20 -07:00
And it isn't limited to just the city, the state, and the country, either. [Location.pm](https://github.com/duckduckgo/duckduckgo/blob/master/lib/DDG/Location.pm#L6) lists all the things that you can possibly use:
```perl
my @geo_ip_record_attrs = qw( country_code country_code3 country_name region
region_name city postal_code latitude longitude time_zone area_code
continent_code metro_code );
```
2013-03-14 11:47:14 -07:00
Sample contents of `$loc`:
```perl
longitude => -75.5385
country_name => United States
area_code => 610
region_name => Pennsylvania
country_code => US
region => PA
continent_code => NA
city => Phoenixville
postal_code => 19460
latitude => 40.1246
time_zone => America/New_York
metro_code => 504
country_code3 => USA
```
## Testing the Location API
To write a test for a location aware Goodie, you'll need to pass `ddg_goodie_test` an extra parameter, a `DDG::Request` object, with the location specified. To do this, you'll need to `use DDG::Test::Location` and `use DDG::Request`. Here is a working annotated example excerpted from `t/Helpline.t`.
```perl
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
use DDG::Test::Goodie;
# These two modules are only necessary when testing with the Location API.
use DDG::Test::Location;
use DDG::Request;
zci answer_type => 'helpline';
ddg_goodie_test(
[qw(
DDG::Goodie::HelpLine
)],
# This optional argument to ddg_goodie_test is a DDG::Request object.
# The object constructor takes two arguments of its own:
# the query (usually specified in the test_zci),
# and a location object - created by test_location (with a country code).
DDG::Request->new(
query_raw => "suicide",
location => test_location("us")
),
test_zci(qr/24 Hour Suicide Hotline: [\d\-){7}]/),
);
done_testing;
```