diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 000000000..b65806aa8 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,6 @@ +# Summary + +* [Introduction](README.md) +* [Welcome](duckduckhack/welcome/ddh-intro.md) + * [Setting up your development environment](duckduckhack/welcome/setup-dev-environment.md) + diff --git a/duckduckhack/api-answers/forum-lookup.md b/duckduckhack/api-answers/forum-lookup.md new file mode 100644 index 000000000..d4645a345 --- /dev/null +++ b/duckduckhack/api-answers/forum-lookup.md @@ -0,0 +1,450 @@ +# How to Make a Forum Lookup + +Some of our most popular Instant Answers have been direct access to forums, such as [Stack Overflow](#) or [Reddit](#). We'd love to see more community wisdom made part of search results, and this tutorial is one example of how to do that. + +Together we'll build an Instant Answer that directly displays Hacker News posts alongside DuckDuckGo.com search results: + +![Hacker News Spice](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fia-screenshots.s3.amazonaws.com%2Fhacker_news_index.png%3Fnocache%3D4600&f=1) + +You can see it in action by searching for ["hn dropbox"](#), for example. + +## How It Works + +When a user searches anything containing words such as "hn", "hn search", or "hacker news" at certain locations in the query, DuckDuckGo will trigger this Instant Answer. + +When the Instant Answer is triggered, DuckDuckGo executes its front-end code on the search results page. The page will make an AJAX call to the Hacker News API, and pass the response to the Instant Answer's callback. If any articles come back, the Instant Answer will parse, sort, and display each item to the user. + +Simple enough. So how do we make that work in code? + +## Anatomy of this Instant Answer + +Because this Instant Answer calls an external API, it's called a "Spice" Instant Answer. All Spice Instant Answers are kept together in the [Spice repository](#) on Github. + +A Spice is a combination of several backend and frontend files, each handling a different aspect of the process. + +Backend files: + +File | Purpose | Location +-----|---------|--------- +[`HackerNews.pm`](#) | Specifies the query triggers, Hacker News API endpoint, and the metadata (such as attribution, name, and so on). | Perl files are placed in the [`zeroclickinfo-spice/lib/DDG/Spice`](#) directory. +[`HackerNews.t`](#) | A test file; it asserts that specific search queries will trigger (or not trigger) this Instant Answer. | Test files are placed in the [`zeroclickinfo-spice/t`](#) directory. + +Frontend files: + +File | Purpose | Location +-----|---------|--------- +[`hacker_news.js`](#) | When the IA is triggered, this file runs on the search results page. It processes the response from the Hacker News API and specifies how to display it. | Frontend files are placed in the [`zeroclickinfo-spice/share/spice/hacker_news/`](#) directory. +[`hacker_news.css`](#) | A minor, optional, custom css file | [`zeroclickinfo-spice/share/spice/hacker_news/`](#) +[`footer.handlebars`](#) | A minor, optional [sub-template](#), a custom handlebars HTML template used as part of the main template. Its use is specified in `hacker_new.js`. | [`zeroclickinfo-spice/share/spice/hacker_news/`](#) + +That's it - these are all the files and functionality necessary to create this Instant Answer. Next, we'll go line by line and build it together from scratch. + +## Set Up Your Development Environment + +Before we begin coding, we'll need to set up our development environment. There are three main steps: + +1. Fork the [Spice Repository](#) on Github.com. ([How?](#)) +2. Fork the [DuckDuckGo environment](#) on Codio.com (our tools). ([How?](#)) +3. Clone your Github fork onto the Codio environment. ([How?](#)) + +If this is your first time developing an Instant Answer, check out our [detailed, step-by-step guide](#) to getting your development environment set up. + +## Create a New Instant Answer + +In Codio, load the terminal, and change into the Spice repository's home directory, `zeroclickinfo-spice`: +[Screenshot of clicking terminal] + +``` +[08:17 PM codio@border-carlo workspace ]$ cd zeroclickinfo-spice +``` + +The `duckpan` tool helps make and test Instant Answers. To create new Spice boilerplate, run **`duckpan new`**: + +``` +[08:18 PM codio@border-carlo zeroclickinfo-spice {master}]$ duckpan new +Please enter a name for your Instant Answer : +``` + +Type `Hacker Newz` (since *Hacker News* already exists in the repository, we'll change one letter for this tutorial). The tool will do the rest: + +``` +Please enter a name for your Instant Answer : Hacker Newz +Created file: lib/DDG/Spice/HackerNewz.pm +Created file: share/spice/hacker_newz/hacker_newz.handlebars +Created file: share/spice/hacker_newz/hacker_newz.js +Created file: t/HackerNewz.t +Successfully created Spice: HackerNewz +``` + +That's convenient: The files have each been named - and located - according to the project's conventions. Internally, each file contains correct boilerplate to save us time. + +## `HackerNewz.pm` + +Let's open up `HackerNewz.pm`. + +Navigate using the Codio file tree on the left, and double click on the file, in the `lib/DDG/Spice/` directory. It'll be full of comments and sample code we can change as we please. + +### Settings and Metadata + +Each Instant Answer is a Perl package, so we start by declaring the package namespace in CamelCase format. This was done automatically for us when we ran the `duckpan new` command: + +```perl +package DDG::Spice::HackerNewz; +``` + +Next, change the comments to contain a short abstract. Easy enough: + +```perl +# ABSTRACT: Search for Hacker News +``` + +Now we'll import the Spice class (as well as tell the Perl compiler to be strict) - also already done for us: + +```perl +use strict; +use DDG::Spice; +``` + +On the next line, we'll leave caching on. By default, caching saves the results to individual API calls for an hour. Of course, this may not be right for some Instant Answers - so you can just replace `1` with `0`. There are several options when it comes to caching - [learn more in the API reference](#). + +```perl +spice is_cached => 1; +``` + +Now for the Metadata. Because there's so many Instant Answers, metadata helps us organize, describe, and attribute your contribution. They are also used to automatically generate [Instant Answer Pages](https://duck.co/ia) - plus give you credit right on DuckDuckGo.com. + +For example, these are the Metadata values used in the live *HackerNews* answer. You can learn more in the [metadata reference](#). + +```perl +primary_example_queries "hn postgresql"; +description "Search the Hacker News database for related stories/comments"; +name "HackerNews"; +code_url "https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/HackerNews.pm"; +icon_url "/i/www.hnsearch.com.ico"; +topics "programming", "social"; +category "forums"; +attribution github => ['https://github.com/adman','Adman'], + twitter => ['http://twitter.com/adman_X','Adman']; +``` + +### API Endpoint + +With the formalities out of the way, let's define the most important element of our Instant Answer - the API call. This is a URL to which we'll make a GET request. + +*How do we choose an API? Currently, the community can only accept JSON or JSONP APIs. Due to DuckDuckGo's [scale](https://duckduckgo.com/traffic.html), APIs must be [free, fast, credible, and reliable](#). If you have a particular API in mind, check out the [API requirements](#).* + +We're just hacking for now, so let's enter our URL for querying the Hacker News Search API: + +```perl +spice to => 'https://hn.algolia.com/api/v1/search?query=$1&tags=story'; +``` + +Notice the `$1` - that's a placeholder for a dynamic value our Instant Answer will provide. Many Instant Answers take advantage of this for search endpoints, but others might not need it at all. Others may use [multiple placeholders](#). Feel free to leave it out of your URL. + +What fills the `$1`? Our *handle* function, which we'll talk about in a bit. + +### Indicate our Callback Function + +In most cases, APIs allow for a "callback" parameter, which we'd usually include in the URL like this: + +```perl +http://www.api.awesome.com/?q=&callback= +``` + +By naming a callback, the JSON object returns neatly wrapped in a JavaScript function call. This function is often specified the `callback` parameter - but that depends on the API. + +This particular API doesn't allow us to specify a callback. No worries - we'll account for this by setting the `wrap_jsonp_callback` attribute to `1`: + +```perl +spice wrap_jsonp_callback => 1; +``` + +Now, when the JSON is returned by the API, DuckDuckGo will wrap our result in a call to our Spice's JavaScript callback function. We'll define this function in the frontend, in `hacker_newz.js`. + +### Triggers + +How will DuckDuckGo know to display our Instant Answer on a user's search? That's what *triggers* are for: + +```perl +triggers startend => "hn", "hackernews", "hacker news", "news.yc", "news.ycombinator.com", "hn search", "hnsearch", "hacker news search", "hackernews search"; +``` + +This tells DuckDuckGo that if any of these strings occurs at the *start or end* of any user's search query, it should activate our Instant Answer and attempt calling the API. There are several types of triggers in addition to `startend` - [see them all here](#). + +Of course, simply matching a trigger doesn't guarantee the API will return anything useful - just that the API is worth trying. + +### Handle Function + +Remember our `$1` placeholder before? It's filled in by the `handle` function. This function acts as the last filter before we call the API. Whatever the handle function returns will be inserted into the API call. If it returns nothing, the API will not be called. + +```perl +handle remainder => sub { + return unless $_; + return; +}; +``` + +Within our `handle` function, `$_` is a special variable that takes on the value of `remainder`. The `remainder` refers to the rest of the query after removing our matched triggers. + +This function is a simple case: it returns the *remainder* of the query, unless it's blank. The *remainder* is just the query minus the trigger. If a user searches 'hacker news meteor', the remainder would be 'meteor'. + +While triggers specify when to trigger our Instant Answer, handle functions are used to limit those cases. Handle functions can get more complicated if necessary, by including regular expressions and returning *multiple* placeholders: [learn more about handle functions here](#). + +There's one final line of code on our backend. Because this is a Perl package, it must return `1` at the end to indicate successful loading: + +```perl +1; +``` + +Our Spice backend is complete. Functionally, we've told DuckDuckGo: + +- *Where* to call the API (endpoint) +- *When* to call the API (triggers) +- *When not* to call the API (handle function) + +We're done with our backend. Next, we'll tell DuckDuckGo how to display any results we get back. + +## `hacker_newz.js` + +Let's open up `hacker_newz.js`. Navigate using the Codio file tree on the left, and double click on the file, in the `zeroclickinfo-spice/share/spice/hacker_news/` directory. It, too, will be full of comments and sample code we can change as we please. + +### JavaScript Formalities + +Our JavaScript file is wrapped inside the "[module pattern](http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript)" that makes sure we can access the global scope, but that no variables inside this pattern will leak into the global scope. We also take advantage of JavaScript's [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FFunctions_and_function_scope%2FStrict_mode). + +```javascript +(function(env) { + "use strict"; + + // Everything else... + +})(this); +``` + +It's not at all critical to understand this - simply that it is required for any Instant Answer frontend. + +### Define the Callback + +Our frontend callback is what handles any data from our API call. When our API call returns, its response is passed to this callback as `api_result`. It should already be included in the file: + +```javascript +env.ddg_spice_hacker_newz = function(api_result) { + // Everything else... +} +``` + +### Call Spice.Failed() If Nothing Returned + +Just because our Instant Answer triggered doesn't mean the API will necessarily return anything. Here, we check for the case of no response, error response, or empty response. + +The default code is a good start. **However, this code should be customized to fit the response of your particular API.** + +```javascript +if (!api_result || api_result.error) { + return Spice.failed('hacker_newz'); +} +``` + +The Hacker News API, in particular, returns its data inside a `hits` property - so we check for its existence. + +Like many APIs, the results come as an *array*. That means we'll also check if `hits` has a `length`. That way, if no results were returned from the API, we can stop that as well. + +```javascript +if(!api_result || !api_result.hits || api_result.hits.length === 0) { + return Spice.failed('hacker_newz'); +} +``` + +It's important to use `Spice.failed()` because this lets the rest of the Spice system know our Spice isn't going to display so that other relevant answers can be given an opportunity to display. + +### Display the Data + +With results in hand, we call `Spice.add()` to display our Spice to the user. + +While our `hacker_newz.js` boilerplate doesn't contain this, you might have noticed that our final [`hacker_news.js`](#) uses `DDG.require()`. This is not necessary for all Instant Answers, but is used to include external libraries, like [MomentJS](http://momentjs.com/) in this case. You can [learn more about `DDG.require()` here](#). + +```javascript +DDG.require('moment.js', function(){ // Not required for most Instant Answers + Spice.add({ + // Display properties go here + }); +}); +``` + +### Set Our Display Properties + +Let's look inside the `Spice.add()` call. It's passed an object with display properties - let's go through each. A full explanation of each display property can be found in the [Display Reference](#). + +The `id` is automatically inserted for us: + +```javascript +id: 'hacker_newz', +``` + +The `name` is the name of the clickable tab in the AnswerBar containing our Instant Answer. + +```javascript +name: 'Social', +``` + +We specify the `data` returned by the API. This is usually `api_result` or the sub-property containing the actual content - in this case, `hits`. + +```javascript +data: api_result.hits, +``` + +The `meta` property defines all the surrounding details of the Instant Answer, such as the phrase "Showing 20 Hacker News Submissions", or the link to the information source. You can learn about each meta property available to you in the [Display Reference](#). + +In this example, `sourceUrl` is calculated at the top of the callback in [`hacker_news.js`](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/share/spice/hacker_news/hacker_news.js#L8) + +```javascript +meta: { + sourceName: 'HN Search', + sourceUrl: sourceUrl, + total: api_result.hits, + itemType: (api_result.hits.length === 1) ? 'Hacker News submission' : 'Hacker News submissions', + searchTerm: decodeURIComponent(query) +}, +``` + +To prepare our data for displaying as HTML, we define a `normalize` function. This optional function takes each raw API item, and returns an item ready for displaying in the HTML template. + +The `normalize` function is run on each item in the API result. For convenience, the original properties of each API result (which are not overwritten) are also included. Learn more about the [`normalize` function here](#). + +```javascript +normalize: function(item) { + return { + title: item.title, + url: (item.url) ? item.url : 'https://news.ycombinator.com/item?id=' + item.objectID, + points: item.points || 0, + num_comments: item.num_comments || 0, + post_domain: extractDomain(item.url), + date_from: moment(item.created_at_i * 1000).fromNow(), + arrowUrl: DDG.get_asset_path('hacker_news','arrow_up.png') + }; +}, +``` + +Let's specify what HTML templates we'll use to display each item. The vast majority of Instant Answers use the DuckDuckHack [built-in templates system](#). There are all sorts of specialized templates, from displaying places on a map, to displaying movie titles, to products, and lookup information. Each of these can be customized using [options and variants](#). + +[Template groups](#) are convenient presets. They're specified in the `group` property. The other properties you see under templates customize the behavior of the group. For example, `detail: false` makes sure items will always be displayed as tiles. Learn more about these options in the [templates reference](#). + +```javascript +templates: { + group: 'text', + options: { + footer: Spice.hacker_news.footer + }, + detail: false, + item_detail: false, + variants: { + tileTitle: "3line-small", + tileFooter: "3line" + } +}, +``` + +You'll notice the inclusion of the `Spice.hacker_news.footer` sub-template. This refers to the [footer.handlebars](#) file also in the `share/spice/hacker_news/` directory. You can learn more about the [inclusion of sub-templates here](#). + +Finally, we'll define the properties on which we'll sort results. [Learn more about sorting here](#). + +```javascript +sort_fields: { + score: function(a, b){ + return (a.points > b.points) ? -1 : 1; + }, + date: function(a, b){ + return (a.created_at_i > b.created_at_i) ? -1 : 1; + } +}, +sort_default: 'score' +``` + +As an aside, for those interested in doing more advanced things in the frontend, the Instant Answer framework also provides a JavaScript API with useful functions you can call. + +As far as our "Hacker Newz" Instant Answer is concerned, our frontend is complete. We've fully specified how DuckDuckGo should display our data. + +## Handlebars Templates + +Many [built-in templates](#) allow for inserting sub-templates to fill out particular features. For example, sub-templates can be used to create custom footers, calls-to-action, or decide how to display lists of values. + +In this case, you'll notice that there is a `footer.handlebars` function found in [`share/spice/hacker_news`](#). You'll also notice that above in the `templates` property, it's specified to be used as the template footer: + +```javascript +templates: { + ... + options: { + footer: Spice.hacker_news.footer + }, + ... +} +``` + +Sub-templates can either be built-in or created custom for your Instant Answer. You can learn more about how they work in the [sub-templates reference](#). + +## CSS Files + +You'll notice there's a [css file](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/share/spice/hacker_news/hacker_news.css) in the `share/spice/hacker_news/` directory. + +While any CSS files in the directory will be included automatically, **this is no longer necessary or encouraged**. Instead, the more stable and maintainable option is to use [variants](#). + +## Test File + +Creating a test file for your Instant Answer is a critical requirement for [submitting](#) your Instant Answer. You can learn more in the [Test File Reference](#). + +In this case, `duckpan new` created a test file for us, under [`t/HackerNewz.t`](#). We'll specify two test queries to make sure they trigger our Instant Answer: + +```perl +#!/usr/bin/env perl + +use strict; +use warnings; +use Test::More; +use DDG::Test::Spice; + +ddg_spice_test( + [qw( DDG::Spice::HackerNewz )], + 'hn duckduckgo' => test_spice( + '/js/spice/hacker_newz/duckduckgo', + call_type => 'include', + caller => 'DDG::Spice::HackerNewz' + ), + 'hn postgresql' => test_spice( + '/js/spice/hacker_newz/postgresql', + caller => 'DDG::Spice::HackerNewz', + ), +); + +done_testing; +``` + +A test file is required for submitting your Instant Answer. However, we don't need it to proceed with interactively testing our code, which we'll do next. + +## Interactively Test Our Instant Answer + +Inside Codio, we can preview the behavior of all Instant Answers on a local test server. + +In Codio, load the terminal, and make sure you're in your repository's home directory. If not, change into it. + +Enter the **`duckpan server`** command and press Enter. + +``` +[08:18 PM codio@border-carlo zeroclickinfo-spice {master}]$ duckpan server + +``` + +The terminal should print some text and let you know that the server is listening on port 5000. + +``` +Starting up webserver... + +You can stop the webserver with Ctrl-C + +HTTP::Server::PSGI: Accepting connections at http://0:5000/ +``` + +Click the "**DuckPAN Server**" button at the top of the screen. A new browser tab should open and you should see the DuckDuckGo Homepage. Type your query to see the results (actual search results will be placeholders.) + +[Screenshot] + +Have questions? [Email us](mailto:open@duckduckgo.com) or [say hello on Slack!](mailto:QuackSlack@duckduckgo.com?subject=AddMe) \ No newline at end of file diff --git a/duckduckhack/api-answers/local-search.md b/duckduckhack/api-answers/local-search.md new file mode 100644 index 000000000..6f872ac4f --- /dev/null +++ b/duckduckhack/api-answers/local-search.md @@ -0,0 +1,3 @@ +# How to Add a Local Search + +(NY Bike Share) \ No newline at end of file diff --git a/duckduckhack/api-answers/transit-schedule.md b/duckduckhack/api-answers/transit-schedule.md new file mode 100644 index 000000000..fded09c87 --- /dev/null +++ b/duckduckhack/api-answers/transit-schedule.md @@ -0,0 +1,3 @@ +# How to Make a Transit Times Lookup + +(SEPTA Train) \ No newline at end of file diff --git a/duckduckhack/assets/air_quality.png b/duckduckhack/assets/air_quality.png new file mode 100644 index 000000000..4c8ad2ca4 Binary files /dev/null and b/duckduckhack/assets/air_quality.png differ diff --git a/duckduckhack/assets/alternative_spotify.png b/duckduckhack/assets/alternative_spotify.png new file mode 100644 index 000000000..53c6458c0 Binary files /dev/null and b/duckduckhack/assets/alternative_spotify.png differ diff --git a/duckduckhack/assets/blue_pill.png b/duckduckhack/assets/blue_pill.png new file mode 100644 index 000000000..ffc2a7404 Binary files /dev/null and b/duckduckhack/assets/blue_pill.png differ diff --git a/duckduckhack/assets/bpm_ms.png b/duckduckhack/assets/bpm_ms.png new file mode 100644 index 000000000..af1d8fa92 Binary files /dev/null and b/duckduckhack/assets/bpm_ms.png differ diff --git a/duckduckhack/assets/gcf.png b/duckduckhack/assets/gcf.png new file mode 100644 index 000000000..c81ac6b9a Binary files /dev/null and b/duckduckhack/assets/gcf.png differ diff --git a/duckduckhack/assets/heads_tails.png b/duckduckhack/assets/heads_tails.png new file mode 100644 index 000000000..3db7b6de3 Binary files /dev/null and b/duckduckhack/assets/heads_tails.png differ diff --git a/duckduckhack/assets/parking_ny.png b/duckduckhack/assets/parking_ny.png new file mode 100644 index 000000000..1a012ff77 Binary files /dev/null and b/duckduckhack/assets/parking_ny.png differ diff --git a/duckduckhack/assets/sales_tax.png b/duckduckhack/assets/sales_tax.png new file mode 100644 index 000000000..967fb2a25 Binary files /dev/null and b/duckduckhack/assets/sales_tax.png differ diff --git a/duckduckhack/assets/url_encode.png b/duckduckhack/assets/url_encode.png new file mode 100644 index 000000000..fd1f040f6 Binary files /dev/null and b/duckduckhack/assets/url_encode.png differ diff --git a/duckduckhack/spice/spice_advanced_backend.md b/duckduckhack/backend-reference/api-reference.md similarity index 73% rename from duckduckhack/spice/spice_advanced_backend.md rename to duckduckhack/backend-reference/api-reference.md index 0fecf22b1..b53716fd0 100644 --- a/duckduckhack/spice/spice_advanced_backend.md +++ b/duckduckhack/backend-reference/api-reference.md @@ -1,22 +1,36 @@ -##Spice Handlers +# API Reference -- [Multiple Placeholders in Spice To URL](http://duck.co/duckduckhack/spice_advanced_backend#Multiple-Placeholders-in-Spice-To-URL) +## API Criteria -- [Returning Multiple Values (to Spice From)](http://duck.co/duckduckhack/spice_advanced_backend#Returning-Multiple-Values-to-Spice-From) +With millions of search queries triggering Instant Answers every day, it's important to choose APIs wisely. There are several criteria for APIs used in Spice Instant Answers. -- [API Keys](http://duck.co/duckduckhack/spice_advanced_backend#api-keys) +### Technical constraints -- [JSON -> JSONP](http://duck.co/duckduckhack/spice_advanced_backend#json-gt-jsonp) +- APIs called by Spice Instant Answers **must use the JSON or JSONP formats**. We do not support the use of XML (it's coming soon though!), HTML, or plain text responses. +- APIs should respond to requests in **less than one second** (< 1s). +- Avoid **static JSON files**. These often have large payloads and require heavy local processing. Not sure? [Talk to us about your idea](mailto:open@duckduckgo.com). -- [Pure JS functions](http://duck.co/duckduckhack/spice_advanced_backend#pure-js-functions) +### Reliability -- [Caching API Responses](http://duck.co/duckduckhack/spice_advanced_backend#caching-api-responses) +- APIs used should be **reliable**. Pick sources that will be most likely be around and accurate for the foreseeable future. +- APIs created by contributors solely for the purpose of an Instant Answer cannot be accepted. -- [Caching API Calls](http://duck.co/duckduckhack/spice_advanced_backend#caching-api-calls) +### Credibility -## Multiple Placeholders in Spice To URL +- APIs used should represent the **most credible source** for the information. This means it should draw upon the preferred data source of the relevant community. Look for posts and sources like [these](https://duck.co/forum/thread/37/great-resources-for-instant-answer-ideas) which have been suggested by others. +- APIs must have the appropriate permissions or rights to serve their information. -If you need to substitute multiple parameters into the API call like how the [RandWord Spice](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/RandWord.pm) uses two numbers to specify the min and max length of the random word, you can use the **Spice from** keyword: +## Multiple API endpoints + +Learn how to handle multiple API endpoints when developing a DuckDuckGo Instant Answer. + +[Watch video on Vimeo](https://vimeo.com/137152536) + +![https://vimeo.com/137152536](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fscreencast_multiple-endpoints.jpg&f=1) + +## Multiple Placeholders in API URL + +If you need to substitute multiple parameters into the API call you can use the **Spice from** keyword. In the following example, the [RandWord Spice](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/RandWord.pm) uses two numbers to specify the min and max length of the random word: ```perl spice from => '(?:([0-9]+)\-([0-9]+)|)'; @@ -37,8 +51,6 @@ handle remainder => sub { } ``` - - Then the the string `10-100` would be sent to the `spice from` regexp, which would capture the two numbers into `$1` and `$2`. These two placeholders are then used to replace `$1` and `$2` in the `spice to` URL: ```perl @@ -47,7 +59,7 @@ spice to => 'http://api.wordnik.com/v4/words.json/randomWord?minLength=$1&maxLen **Note:** The reason why you do not need to specify a **from** keyword by default, is that the default value of `spice from` is **(.*)**, which means whatever you return gets gets captured into `$1`. -## Returning Multiple Values (to Spice From) +## Passing Multiple Values to Spice From You can have multiple return values in your handle function like the [AlternativeTo Spice](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/AlternativeTo.pm). @@ -89,24 +101,6 @@ Some APIs don't do JSONP by default, i.e. don't have the ability to return the J spice wrap_jsonp_callback => 1; ``` -## Pure JS functions - -Sometimes no external API is necessary to deliver the Instant Answer like how the [Flash Version Spice](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/FlashVersion.pm) just prints out your [Flash Player version](https://duckduckgo.com/?q=flash+version) using an [internal call](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/share/spice/flash_version/flash_version.js). - -In cases like these you can define a **spice\_call\_type** as 'self' like this: - -```perl -spice call_type => 'self'; -``` - -Then in the handle function you can return call, e.g.: - -```perl -return $_ eq 'flash version' ? call : (); -``` - -The return of **call** will run whatever is in the **call\_type** setting. **self** is a special keyword to just run the callback function directly, in this case **ddg\_spice\_flash_version()**. - ## Caching Spice Instant Answers have two forms of caching: API Response caching (remembers the JSON returned from the API) and API Call caching (remembers the API call URL created for a given query). Both of these will be explained with examples. diff --git a/duckduckhack/backend-reference/data-files.md b/duckduckhack/backend-reference/data-files.md new file mode 100644 index 000000000..190831403 --- /dev/null +++ b/duckduckhack/backend-reference/data-files.md @@ -0,0 +1,53 @@ +# Data Files + +Instant Answers - particularly Goodies - can use simple text files for display or processing. These files can be read once and reused to answer many queries without cluttering up your source code. + +The `share` function gives each Instant Answer access to a subdirectory of the repository's [`share`](https://github.com/duckduckgo/zeroclickinfo-goodies/tree/master/share/goodie) directory. The subdirectory for your Instant Answer is based on its Perl package name which is transformed from CamelCase to underscore_separated_words. + +## Usage + + +The [MAC Address Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/andrey/js-keycodes-cheatsheet/lib/DDG/Goodie/MacAddress.pm) uses the `share` directory to hold data for processing purposes: + +```perl +my %oui_db = share("oui_database.txt")->slurp; +``` + +Here the `share` function grabs the `oui_database.txt` file. The returned object's `slurp` method is called which pushes each line of the file into an array. + +The full code performs some additional parsing on each line of the `slurp`-ed array: + +```perl +my %oui_db = map { chomp; my (@f) = split(/\\n/, $_, 2); ($f[0] => $f[1]); } share("oui_database.txt")->slurp; +``` + +You may decide to take advantage of particular data formats, such as JSON, as does the [Independence Day Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IndependenceDay.pm): + +```perl +my $data = share('independence_days.json')->slurp; +$data = decode_json($data); +``` + +Another example involves parsing a YAML file, as does the [Paper Size Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Paper.pm): + +```perl +use YAML::XS 'LoadFile'; + +my $sizes = LoadFile(share('sizes.yml')); +``` + +## UTF-8 Encoding + +One further consideration is whether your data file contains non-ASCII characters. If so, you will want to prepare the file with UTF-8 encoding. The [Shortcut Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Shortcut.pm) uses a UTF-8 data file: + +```perl +my @shortcuts = share('shortcuts.csv')->slurp(iomode => '<:encoding(UTF-8)'); +``` + +As with the Passphrase Goodie example above, each line becomes an entry in the resulting array. With this `iomode` set, the UTF-8 characters used to denote system-specific keys will be handled properly throughout the rest of the processing. + +## Generating Data Files + +In some cases, you may need to generate the data files you will `slurp` from the share directory. If so, please put the required generation scripts in the Goodie's `share` directory. While **shell scripts are preferred**, your scripts can be written in the language of your choice. + +As an example, the [CurrencyIn Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CurrencyIn.pm) uses a [combination of Shell and Python scripts](https://github.com/duckduckgo/zeroclickinfo-goodies/tree/master/share/goodie/currency_in) to generate its input data, `currency.txt`. \ No newline at end of file diff --git a/duckduckhack/goodie/goodie_helpers.md b/duckduckhack/backend-reference/goodie-helpers.md old mode 100755 new mode 100644 similarity index 94% rename from duckduckhack/goodie/goodie_helpers.md rename to duckduckhack/backend-reference/goodie-helpers.md index 4143350ff..ca9dfe2ea --- a/duckduckhack/goodie/goodie_helpers.md +++ b/duckduckhack/backend-reference/goodie-helpers.md @@ -1,12 +1,11 @@ # Goodie Helpers -A few commonly used functions are wrapped up into useful helpers that can be easily used by any goodie. These are implemented as "roles" that can be used adding the following line to your Goodie's Perl module (.pm file): +We've wrapped several commonly used functions into useful helpers that can be used by any Goodie Instant Answer. These are implemented as "roles" that can be used adding the following line to your Goodie's Perl module (.pm file): ```perl with 'DDG::GoodieRole::RoleName'; ``` - ## Numbers Often matching and outputting numbers intelligently is required, for this the NumberStyler role is ideal: diff --git a/duckduckhack/advanced/language_and_location_apis.md b/duckduckhack/backend-reference/language-location-apis.md similarity index 100% rename from duckduckhack/advanced/language_and_location_apis.md rename to duckduckhack/backend-reference/language-location-apis.md diff --git a/duckduckhack/submitting-your-instant-answer/metadata.md b/duckduckhack/backend-reference/metadata.md similarity index 96% rename from duckduckhack/submitting-your-instant-answer/metadata.md rename to duckduckhack/backend-reference/metadata.md index 9ddab1f0b..95a4626d0 100644 --- a/duckduckhack/submitting-your-instant-answer/metadata.md +++ b/duckduckhack/backend-reference/metadata.md @@ -5,7 +5,6 @@ Including metadata helps us to categorize and describe your Instant Answer. This Metadata is added to your Instant Answer Perl file, above the triggers. For example, the ["Alternative To" Spice](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/AlternativeTo.pm) has the following metadata: ```perl - name "AlternativeTo"; primary_example_queries "alternative to notepad"; secondary_example_queries "alternative to photoshop for mac", "free alternative to spotify for windows"; @@ -23,7 +22,7 @@ Metadata will be used in several places relating to your Instant Answer, includi ------ -## name +## `name` A unique name for this Instant Answer. @@ -33,7 +32,7 @@ While this can be arbitrary, it is considered good practice to have the chosen n name "Subreddit Search"; ``` -## source +## `source` The name of the data source used for your Instant Answer. @@ -41,7 +40,7 @@ The name of the data source used for your Instant Answer. source "Reddit"; ``` -## icon_url (optional) +## `icon_url` (optional) The favicon URL for the data source used. @@ -59,7 +58,7 @@ or, for DuckDuckGo-sourced favicons, icon_url "/i/reddit.com.ico"; ``` -## description +## `description` A succinct explanation of what the Instant Answer does. For clarity, *exclude* the source name if possible. @@ -67,7 +66,7 @@ A succinct explanation of what the Instant Answer does. For clarity, *exclude* t description "Search for Subreddits"; ``` -## primary_example_queries +## `primary_example_queries` Examples of the most common types of queries which will trigger the Instant Answer. @@ -75,7 +74,7 @@ Examples of the most common types of queries which will trigger the Instant Answ primary_example_queries "/r/pizza", "subreddit nature"; ``` -## secondary_example_queries (optional) +## `secondary_example_queries` (optional) Examples of other, less common, trigger words or phrases for the Instant Answer, if applicable. @@ -83,7 +82,7 @@ Examples of other, less common, trigger words or phrases for the Instant Answer, secondary_example_queries "r/accounting"; ``` -## category +## `category` The category into which your Instant Answer best fits. @@ -124,7 +123,7 @@ Supported categories include: category "forums"; ``` -## topics +## `topics` A list of the topics to which your Instant Answer applies. @@ -159,7 +158,7 @@ Supported topics include: topics "social", "entertainment", "special_interest"; ``` -## code_url +## `code_url` URL for the Instant Answer code on github. @@ -167,7 +166,7 @@ URL for the Instant Answer code on github. code_url "https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/RedditSubSearch.pm"; ``` -## attribution +## `attribution` Information about you, the author. diff --git a/duckduckhack/spice/spice_attributes.md b/duckduckhack/backend-reference/spice-attributes.md similarity index 100% rename from duckduckhack/spice/spice_attributes.md rename to duckduckhack/backend-reference/spice-attributes.md diff --git a/duckduckhack/spice/spice_triggers.md b/duckduckhack/backend-reference/triggers.md similarity index 52% rename from duckduckhack/spice/spice_triggers.md rename to duckduckhack/backend-reference/triggers.md index 4cd26b6b0..5938f0353 100644 --- a/duckduckhack/spice/spice_triggers.md +++ b/duckduckhack/backend-reference/triggers.md @@ -1,51 +1,38 @@ -## Triggers +# Triggers -There are two types of triggers, **words** and **regex**. We insist that you use word triggers whenever possible as they are simpler and faster. +Triggers tell DuckDuckGo on which search queries to initiate an Instant Answer. There are two types of triggers, **words** and **regex**. -[Word trigger example](http://duck.co/duckduckhack/spice_triggers#word-triggeres) - -```perl -triggers start => "trigger my instant answer", "trigger myIA", "myIA"; -``` - -[Trigger locations](http://duck.co/duckduckhack/spice_triggers#trigger-locations) - -- `start` — Word exists at the start of the query -- `end` — Word exists at the end of the query -- `startend` — Word is at the beginning or end of the query -- `any` — Word is anywhere in the query +The two types of triggers cannot be combined, but you may consider a third option of [Regex Guards](#regex-guards). We insist that you use word triggers whenever possible as they are simpler and faster. ## Word Triggers -### Usage +Word triggers specify *what* to look for, and *where* to look for it: ```perl triggers => ``` - - -#### Examples +For example: ```perl -triggers start => "trigger my instant answer", "trigger myIA", "myIA"; +triggers start => "stackoverflow", "stack overflow", "SO"; ``` -or +You may find the [qw](http://perlmeme.org/howtos/perlfunc/qw_function.html) function convenient: ```perl -@triggers = qw(these are separate triggers for my instant answer); +@triggers = qw(john jacob jingleheimer schmidt); triggers any => @triggers; ``` -or +You can also use multiple trigger statements together: ```perl -triggers start => "starting phrase of query"; -triggers end => "ending phrase of query"; +triggers start => "tv", "television"; +triggers end => "schedule", "hours"; ``` -## Trigger Locations +### Word Trigger Locations - `start` — Word exists at the start of the query - `end` — Word exists at the end of the query @@ -56,7 +43,11 @@ triggers end => "ending phrase of query"; ## Regex Triggers -### Usage +***We insist that you use word triggers whenever possible as they are simpler and faster.*** + +***Note that you cannot combine the use of Regex Triggers with Word Triggers.*** + +Regex triggers specify *which version of the query*, and a *regular expression* to apply. ```perl triggers => @@ -85,13 +76,11 @@ triggers query_raw => $regex; - `query_nowhitespace` — `query` with all whitespace removed - `query_clean` — `query_lc`, but with whitespace and non-alphanumeric ascii removed -**Note:** You **cannot** combine the use of **Regex Triggers** with **Word Triggers**. - ## Regex Guards -We much prefer you use **trigger words** when possible because they are faster on the backend. In some cases however, **regular expressions** are necessary, e.g. you need to trigger on sub-words. In this case we suggest you consider using a **word trigger** and supplement it with a **regex guard**. A regex guard is a return clause immediately inside the `handle` function. +Trigger words are coarse filters; they may send you queries you cannot handle. Your Instant Answer should return nothing in these cases. As such, you generally need to further qualify the query in your code. - +We much prefer you use Word Triggers when possible because they are faster on the backend. In some cases however, **regular expressions** are necessary, e.g., you need to trigger on sub-words. In this case we suggest you consider using a **word trigger** and supplement it with a **regex guard**. A regex guard is a return clause immediately inside the `handle` function. A good example of this is the Base64 goodie. In this case we want to trigger on queries with the form "base64 encode/decode \". Here's an excerpt from [Base64.pm](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Base64.pm) which shows how this case is handled using a word trigger, with a regex guard: @@ -100,9 +89,18 @@ triggers startend => "base64"; handle remainder => sub { return unless $_ =~ /^(encode|decode|)\s*(.*)$/i; +``` + +Another example, the [Base Goodie's](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Base.pm) has a `return` statement paired with an `unless` right on the first line of its `handle` function: +```perl +handle remainder => sub { + return unless /^([0-9]+)\s*(?:(?:in|as)\s+)?(hex|hexadecimal|octal|oct|binary|base\s*([0-9]+))$/; + ... +} ``` + ## Triggers in Multiple Languages We have plans to make it possible to trigger Instant Answers in many different languages. Until an internationalization mechanism is place, to uphold maintainability and consistency, **we cannot accept pull requests that add languages directly in the code.** \ No newline at end of file diff --git a/duckduckhack/cheat-sheets/key-bindings.md b/duckduckhack/cheat-sheets/key-bindings.md new file mode 100644 index 000000000..31b58399f --- /dev/null +++ b/duckduckhack/cheat-sheets/key-bindings.md @@ -0,0 +1 @@ +# How to Make a Key Bindings Cheat Sheet diff --git a/duckduckhack/cheat-sheets/language-reference.md b/duckduckhack/cheat-sheets/language-reference.md new file mode 100644 index 000000000..578d1818b --- /dev/null +++ b/duckduckhack/cheat-sheets/language-reference.md @@ -0,0 +1 @@ +# How to Make a Language Reference Cheat Sheet \ No newline at end of file diff --git a/duckduckhack/cheat-sheets/programming-syntax.md b/duckduckhack/cheat-sheets/programming-syntax.md new file mode 100644 index 000000000..9ed79d901 --- /dev/null +++ b/duckduckhack/cheat-sheets/programming-syntax.md @@ -0,0 +1,179 @@ +# How to Make a Programming Cheat Sheet + +It's always delightful when search results answer our question in fewer steps than we expected. Programming language Cheat Sheets are no exception. Naturally, our community would love to encourage more of them. + +Cheat Sheets are the easiest type of Instant Answer to contribute. There are several kinds you can make: reference cheat sheets, language cheat sheets, key bindings, in addition to programming cheat sheets. In this tutorial, we'll learn how the Regular Expressions Cheat Sheet was made. + +![Regex Cheat Sheet](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fia-screenshots.s3.amazonaws.com%2Fregex_cheat_sheet_index.png%3Fnocache%3D6203&f=1) + +## How Cheat Sheets Work + +Cheat Sheets trigger when users search for their topic together with keywords such as "help", "commands", "guide", "reference", and "syntax" (check out the [full list of terms](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CheatSheets.pm)). For example, searching for "regex help" would trigger the Regex Cheat Sheet Instant Answer. + +Learn more about [how to trigger your Cheat Sheet](#) in the reference. + +## Anatomy of a Cheat Sheet + +Cheat sheets only require you to add one file with the information to present. In our case, that would be `regex.json`. + +File | Purpose | Location +-----|---------|--------- +[`regex.json`](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/share/goodie/cheat_sheets/json/regex.json)|Specify cheat sheet info and display settings|Placed inside the Cheat Sheets Goodie directory: [`zeroclickinfo-goodies/share/goodie/cheat_sheets/json/`](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/share/goodie/cheat_sheets/json/) + +## Set Up Your Development Environment + +Before we begin coding, we'll need to set up our development environment. There are three main steps: + +1. Fork the [Spice Repository](#) on Github.com. ([How?](#)) +2. Fork the [DuckDuckGo environment](#) on Codio.com (our tools). ([How?](#)) +3. Clone your Github fork onto the Codio environment. ([How?](#)) + +If this is your first time setting up, fear not! Check out our [detailed, step-by-step guide](#) to setting up. + +## Create a JSON File + +In Codio, use the left-hand panel to navigate to the `/zeroclickinfo-goodies` repository directory. Then use the file tree to click into the `/share/goodie/cheat_sheets` directory. Finally, click the **json** folder. + +Up in the **File menu**, click **"Create New File"**, and enter the name of your cheat sheet as a JSON file (make sure it's saving to the `cheat_sheets/json` directory). In our case, since our topic is 'regex', we'll name our file `regex.json`. + +Erase any pre-filled contents that Codio might have inserted, and replace with the open and close brackets, indicating a JSON object. + +```javascript +{ + +} +``` + +## Add Metadata + +Let's add the metadata for our cheat sheet - the information that helps classify, organize, and display our Instant Answer. Start by entering a unique `id` for your Cheat Sheet: + +```javascript +{ + "id": "regex_cheat_sheet", +} +``` + +Next, add a name and description for your Instant Answer: + +```javascript +{ + "id": "regex_cheat_sheet", + "name": "Regex Cheat Sheet", + "description": "Regular expression syntax", +} +``` + +Let's cite a source and link for our information, whenever possible, under `metadata`: + +```javascript +{ + "id": "regex_cheat_sheet", + "name": "Regex Cheat Sheet", + "description": "Regular expression syntax", + "metadata": { + "sourceName": "Cheatography", + "sourceUrl": "http://www.cheatography.com/davechild/cheat-sheets/regular-expressions/" + }, +} +``` + +## Add Cheat Sheet Settings + +Right now, since we named our file `regex.json`, our Cheat Sheet will trigger on phrases like 'regex guide' or 'regex syntax'. If we want it to trigger on words other than 'regex,' we can specify aliases. Add the following code beneath `metadata`: + +```javascript +"aliases": [ + "regexp", "regular expression", "regular expressions" +], +``` + +Next, we decide the form in which the cheat sheet will be displayed. There are four cheat sheet template types: + +![Cheat sheet template types](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fcheatsheet-template-types.png&f=1) + +We'll choose the 'code' template, because it fits our content the best: + +```javascript +"template_type": "code", +``` + +## Fill Out Content + +Now it's time to fill in our Cheat Sheet's helpful content. This is done as an object, under the `sections` property: + +```javascript +"sections": { + ... +} +``` + +Each section is a key-value pair of the section's name, and an array: + +```javascript +"sections": { + "Assertions": [ + + ], + "POSIX Classes": [ + + ] + ... +} +``` + +Each section's array lists objects, each with `key` and `val` properties. These contain our visible content: + +```javascript +"sections": { + "Assertions": [ + { + "val": "Lookahead assertion", + "key": "?=" + }, { + "val": "Negative lookahead", + "key": "?!" + }, + ... + ], + ... +}, +``` + +You can see (and copy-paste) the full contents of the `sections` property in the [`regex.json` file on Github](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/share/goodie/cheat_sheets/json/regex.json). The full JSON syntax for entering this information is documented in the [Cheat Sheets reference page](#). + +Finally, we can specify precisely in what order to display sections using the `section_order` property. + +```javascript +"section_order": ["Anchors", "Character Classes", "POSIX Classes", "Pattern Modifiers", "Escape Sequences", "Quantifiers", "Groups and Ranges", "Assertions", "Special Characters", "String Replacement"] +``` + +Important note: In order to be displayed, every section in `sections` must appear in `section_order`. + +## Test Your Cheat Sheet + +Let's see our Cheat Sheet in action. To do this, we'll create a test server that will allow you to view your Instant Answer as it would appear above DuckDuckGo search results. + +1. In Codio, open your Terminal by clicking on **Tools > Terminal**. + + (Screenshot) + +2. Change into the `zeroclickinfo-goodies` directory by typing `cd zeroclickinfo-goodies` at the command line. +3. Next, type **`duckpan server`** and press "**Enter**". The Terminal should print some text and let you know that the server is listening on port 5000. + + ``` + Starting up webserver... + You can stop the webserver with Ctrl-C + HTTP::Server::PSGI: Accepting connections at http://0:5000/ + ``` + +4. Click the "**DuckPAN Server**" button at the top of the screen. A new browser tab should open and you should see the DuckDuckGo Homepage. Type **"regex cheat sheet"** and press "**Enter**". + + (Screenshot) + +5. You should see your cheat sheet show up in the search results! **Make sure it displays correctly; check that all escaped characters and code blocks appear as you intended.** +6. When you're done testing, go back to the Terminal, and press "**Ctrl+C**" to shut down the DuckPAN Server. The Terminal should return to a regular command prompt. + +Congrats - you've made a working Instant Answer! If you've made an original cheat sheet, find out how to contribute it live on DuckDuckGo.com in the [Contributing section](#). + +Need help? [Join us over on Slack!](mailto:QuackSlack@duckduckgo.com?subject=AddMe) \ No newline at end of file diff --git a/duckduckhack/ddh-index.md b/duckduckhack/ddh-index.md index 236d82577..c67e5f37c 100755 --- a/duckduckhack/ddh-index.md +++ b/duckduckhack/ddh-index.md @@ -2,71 +2,3 @@ - **Getting Started** - [Welcome!](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/getting-started/ddh-intro.md) - - [How to Contribute](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/getting-started/contributing.md) - - [Determine Your Instant Answer Type](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/getting-started/determine_your_instant_answer_type.md) - - [Setup Your Development Environment](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/getting-started/setup_dev_environment.md) - -- **Goodie** - - [Overview](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/goodie/goodie_overview.md) - - [Quick Start](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/goodie/goodie_quickstart.md) - - [Basic Tutorial](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/goodie/goodie_basic_tutorial.md) - - [Triggers Overview](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/goodie/goodie_triggers.md) - - [Goodie Display](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/goodie/goodie_displaying.md) - - [Advanced Handle Functions](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/goodie/goodie_advanced_handle_functions.md) - - [Helpers](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/goodie/goodie_helpers.md) - - [Creating Cheat Sheets](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/goodie/goodie_cheat_sheets.md) - - [FAQ](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/resources/faq.md#goodie) - -- **Spice** - - [Overview](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/spice/spice_overview.md) - - [Basic Tutorial](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/spice/spice_basic_tutorial.md) - - [Triggers Overview](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/goodie/spice_triggers.md) - - [Spice Display](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/spice/spice_displaying.md) - - [More Spice Tutorials](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/spice/spice_frontend_walkthroughs.md) - - [Advanced Backend](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/spice/spice_advanced_backend.md) - - [Advanced Frontend](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/spice/spice_advanced_frontend.md) - - [JavaScript API Reference](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/spice/spice_js_api.md) - - [Perl API Reference](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/spice/spice_perl_api.md) - - [Spice Attributes](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/spice/spice_attributes.md) - - [FAQ](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/resources/faq.md#spice) - -- **Fathead** - - [Overview](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/fathead/fathead_overview.md) - - [Basic Tutorial](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/fathead/fathead_basic_tutorial.md) - - [FAQ](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/resources/faq.md#fathead) - -- **Longtail** - - [Overview](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/longtail/longtail_overview.md) - - [Basic Tutorial](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/longtail/longtail_basic_tutorial.md) - - [FAQ](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/resources/faq.md#longtail) - -- **Instant Answer Display** - - [Display Reference](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/instant-answer-display/display_reference.md) - - [Templates Overview](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/instant-answer-display/templates_overview.md) - - [Template Groups](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/instant-answer-display/template_groups.md) - - [Templates Reference](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/instant-answer-display/templates_reference.md) - - [Sub-templates](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/instant-answer-display/subtemplates.md) - - [Handlebars Helpers](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/instant-answer-display/handlebars_helpers.md) - - [Design Styleguide](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/instant-answer-display/design_styleguide.md) - -- **Testing** - - [Triggers](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/testing/testing_triggers.md) - - [Display (HTML/testing/.md)](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/testing/testing_html.md) - - [Test Files](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/testing/test_files.md) - - [Testing with the Location & Language APIs](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/testing/testing_location_language_apis.md) - -- **Submitting Your Instant Answer** - - [Preparing for a Pull Request](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/submitting-your-instant-answer/preparing_for_a_pull_request.md) - - [Metadata](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/submitting-your-instant-answer/metadata.md) - - [Submission and Review](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/submitting-your-instant-answer/submission_and_review.md) - -- **Advanced** - - [Language & Location APIs](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/advanced/language_and_location_apis.md) - -- **Resources** - - [Other Development Environments](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/resources/other_development_environments.md) - - [Code Styleguide](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/resources/code_styleguide.md) - - [Common Pitfalls](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/resources/common_pitfalls.md) - - [How to QA an Instant Answer](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/resources/how_to_qa.md) - - [Video Tutorials](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/resources/video-tutorials.md) - - [FAQ](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/resources/faq.md) diff --git a/duckduckhack/ddh-prev-next.json b/duckduckhack/ddh-prev-next.json index 1759f16a2..715c7d4c0 100755 --- a/duckduckhack/ddh-prev-next.json +++ b/duckduckhack/ddh-prev-next.json @@ -9,228 +9,5 @@ "next": ["determine_your_instant_answer_type.md"] }, - "determine_your_instant_answer_type.md": { - "prev": ["contributing.md"], - "next": ["setup_dev_environment.md"] - }, - - "setup_dev_environment.md": { - "prev": ["determine_your_instant_answer_type.md"], - "next": ["goodie_overview.md", "spice_overview.md", "fathead_overview.md", "longtail_overview.md"] - }, - - "goodie_overview.md": { - "prev": ["determine_your_instant_answer_type.md"], - "next": ["goodie_quickstart.md"] - }, - - "goodie_quickstart.md": { - "prev": ["goodie_overview.md"], - "next": ["goodie_basic_tutorial.md"] - }, - - "goodie_basic_tutorial.md": { - "prev": ["goodie_quickstart.md"], - "next": ["goodie_triggers.md"] - }, - - "goodie_triggers.md": { - "prev": ["goodie_basic_tutorial.md"], - "next": ["goodie_advanced_handle_functions.md"] - }, - - "goodie_displaying.md": { - "prev": ["goodie_triggers.md"], - "next": ["goodie_advanced_handle_functions.md"] - }, - - "goodie_advanced_handle_functions.md": { - "prev": ["goodie_triggers.md"], - "next": ["goodie_helpers.md"] - }, - - "goodie_helpers.md": { - "prev": ["goodie_advanced_handle_functions.md"], - "next": ["goodie_cheat_sheets.md"] - }, - - "goodie_cheat_sheets.md": { - "prev": ["goodie_helpers"], - "next": [] - }, - - "spice_overview.md": { - "prev": ["determine_your_instant_answer_type.md"], - "next": ["spice_basic_tutorial.md"] - }, - - "spice_basic_tutorial.md": { - "prev": ["spice_overview.md"], - "next": ["spice_triggers.md"] - }, - - "spice_triggers.md": { - "prev": ["spice_basic_tutorial.md"], - "next": ["spice_displaying.md"] - }, - - "spice_displaying.md": { - "prev": ["spice_triggers.md"], - "next": ["spice_frontend_walkthroughs.md"] - }, - - "spice_frontend_walkthroughs.md": { - "prev": ["spice_displaying.md"], - "next": ["spice_advanced_backend.md"] - }, - - "spice_advanced_backend.md": { - "prev": ["spice_frontend_walkthroughs.md"], - "next": ["spice_advanced_frontend.md"] - }, - - "spice_advanced_frontend.md": { - "prev": ["spice_advanced_backend.md"], - "next": ["spice_js_api.md"] - }, - - "spice_js_api.md": { - "prev": ["spice_advanced_frontend.md"], - "next": ["spice_perl_api.md"] - }, - - "spice_perl_api.md": { - "prev": ["spice_js_api.md"], - "next": ["spice_attributes.md"] - }, - - "spice_attributes.md": { - "prev": ["spice_perl_api.md"], - "next": ["testing_triggers.md"] - }, - - "fathead_overview.md": { - "prev": ["determine_your_instant_answer_type.md"], - "next": ["fathead_basic_tutorial.md"] - }, - - "fathead_basic_tutorial.md": { - "prev": ["fathead_overview.md"], - "next": ["testing_triggers.md"] - }, - - "longtail_overview.md": { - "prev": ["determine_your_instant_answer_type.md"], - "next": ["longtail_basic_tutorial.md"] - }, - - "longtail_basic_tutorial.md": { - "prev": ["longtail_overview.md"], - "next": [] - }, - - "display_reference.md": { - "prev": [], - "next": ["templates_overview.md"] - }, - - "templates_overview.md": { - "prev": ["display_reference.md"], - "next": ["template_groups.md"] - }, - - "template_groups.md": { - "prev": ["templates_overview.md"], - "next": ["templates_reference.md"] - }, - - "templates_reference.md": { - "prev": ["templates_overview.md"], - "next": ["subtemplates.md"] - }, - - "subtemplates.md": { - "prev": ["templates_reference.md"], - "next": ["handlebars_helpers.md"] - }, - - "handlebars_helpers.md": { - "prev": ["subtemplates.md"], - "next": ["design_styleguide.md"] - }, - - "design_styleguide.md": { - "prev": ["handlebars_helpers.md"], - "next": [""] - }, - - "testing_triggers.md": { - "prev": ["determine_your_instant_answer_type.md"], - "next": ["testing_html.md"] - }, - - "testing_html.md": { - "prev": ["testing_triggers.md"], - "next": ["test_files.md"] - }, - - "test_files.md": { - "prev": ["testing_html.md"], - "next": ["testing_location_language_apis.md"] - }, - - "testing_location_language_apis.md": { - "prev": ["test_files.md"], - "next": [] - }, - - "preparing_for_a_pull_request.md" : { - "prev" : ["testing_location_language_apis.md"], - "next" : ["metadata.md"] - }, - - "metadata.md": { - "prev": ["preparing_for_a_pull_request.md"], - "next": ["submission_and_review.md"] - }, - - "submission_and_review.md": { - "prev": ["metadata.md"], - "next": [] - }, - - "language_and_location_apis.md": { - "prev": [], - "next": ["testing_location_language_apis.md"] - }, - - "other_development_environments.md": { - "prev": [], - "next": ["code_styleguide.md"] - }, - - "code_styleguide.md": { - "prev": ["other_development_environments.md"], - "next": ["common_pitfalls.md"] - }, - - "common_pitfalls.md": { - "prev": ["code_styleguide.md"], - "next": ["how_to_qa.md"] - }, - - "how_to_qa.md": { - "prev": ["common_pitfalls.md"], - "next": ["video-tutorials.md"] - }, - - "video-tutorials.md": { - "prev": ["how_to_qa.md"], - "next": ["faq.md"] - }, - "faq.md": { - "prev": ["video-tutorials.md"], - "next": [] - } } diff --git a/duckduckhack/fathead/fathead_basic_tutorial.md b/duckduckhack/fathead/fathead_basic_tutorial.md deleted file mode 100644 index 1f6031d0d..000000000 --- a/duckduckhack/fathead/fathead_basic_tutorial.md +++ /dev/null @@ -1,232 +0,0 @@ -# Basic Fathead Tutorial - -In this tutorial, we'll be making a Fathead Instant Answer that shows example source code for a "Hello World" program in whatever language is specified by the user's query. The live Instant Answer looks like this: https://duckduckgo.com/?q=hello+world+scala. As discussed in [the Fathead overview](/duckduckhack/fathead_overview), our goal is to generate an output.txt, which will look something like this: - -###### output.txt (snippet) - -``` -hello world (python) A Hello World in python (python.py)
#!/usr/bin/env python\nprint "Hello World"\n https://github.com/leachim6/hello-world -hello world (postscript page) A Hello World in postscript_page (postscript_page.ps)
% run> gs -q postscript_page.ps\n/pt {72 div} def\n/y 9 def\n/textdraw {/Courier findfont 12 pt scalefont setfont 8 pt y moveto show} def\n\n72 72 scale\n0 0 0 setrgbcolor\n\n(Hello world!) textdraw\nshowpage\nquit https://github.com/leachim6/hello-world -hello world (perl) A Hello World in perl (perl.pl)
#!/usr/bin/perl\nprint "Hello World\\n";\n https://github.com/leachim6/hello-world -``` - -## Step 1: Write Perl module - -To begin, use your favourite text editor like [gedit](http://projects.gnome.org/gedit/), notepad, or [emacs](http://www.gnu.org/software/emacs/) to open `lib/DDG/Fathead/HelloWorld.pm` and type the following: - -```perl -package DDG::Fathead::HelloWorld; -# ABSTRACT: Provides an example "Hello World" program for a given programming language -``` - -Next, type the following [use statement](https://duckduckgo.com/?q=perl+use) to import [the magic](https://github.com/duckduckgo/duckduckgo/tree/master/lib/DDG) behind our Instant Answer system. - -```perl -use DDG::Fathead; -``` - -Next, we'll add some metadata fields. These should be self-explanatory, but for more information, please refer to [Metadata](/duckduckhack/metadata). - -```perl -primary_example_queries "hello world perl"; - -secondary_example_queries - "javascript hello world", - "hello world in c"; - -description "Hello World programs in many program languages"; - -name "HelloWorld"; - -icon_url "/i/www.github.com.ico"; - -source "GitHub"; - -code_url "https://github.com/duckduckgo/zeroclickinfo-fathead/tree/master/share/fathead/hello_world"; - -topics "geek", "programming"; - -category "programming"; - -attribution - twitter => ['https://twitter.com/jperla', 'jperla'], - web => ['http://www.jperla.com/blog', 'Joseph Perla']; -``` - -Finally, all Perl packages that load correctly should [return a true value](http://stackoverflow.com/questions/5293246/why-the-1-at-the-end-of-each-perl-package) so add a 1 on the very last line, and make sure to also add a newline at the end of the file. - -```perl -1; - -``` - -Our Perl package is now complete. The entire file should look like this: - -```perl -package DDG::Fathead::HelloWorld; -# ABSTRACT: Provides an example "Hello World" program for a given programming language - -use DDG::Fathead; - -primary_example_queries "hello world perl"; - -secondary_example_queries - "javascript hello world", - "hello world in c"; - -description "Hello World programs in many program languages"; - -name "HelloWorld"; - -icon_url "/i/www.github.com.ico"; - -source "GitHub"; - -code_url "https://github.com/duckduckgo/zeroclickinfo-fathead/tree/master/share/fathead/hello_world"; - -topics "geek", "programming"; - -category "programming"; - -attribution - twitter => ['https://twitter.com/jperla', 'jperla'], - web => ['http://www.jperla.com/blog', 'Joseph Perla']; - -1; - -``` - -## Step 2: Create a directory - -Every Fathead has a directory under share/fathead/ that contains all files except the one we just created. The name of the directory must be the name of the Perl module converted to [snake case](https://en.wikipedia.org/wiki/Snake_case). For this tutorial, we'll use `share/fathead/hello_world/`. - -## Step 3: Write the fetch.sh script - -Every Fathead Instant Answer requires a `fetch.sh` file. This shell script is invoked to fetch the remote data that we need in order to generate our output. For example, following script will clone a git repository that contains a collection of hello world source files: - -###### share/fathead/hello_world/fetch.sh - -```shell -#!/bin/sh - -git clone git://github.com/leachim6/hello-world.git download -``` - -In this case, the git repository is just a collection of source files; we'll need to do some parsing in order to get it into the format we need. Note that *git clone* is not the only way to fetch data. Most Fatheads use *curl* or *wget*. - -**Also Note:** All temporary files should be placed in the `download/` subdirectory within your Fathead's directory. For this example, that means `share/fathead/hello_world/download/`. *git* creates this subdirectory for us, but if your plugin uses a different tool, you make have to include `mkdir download` in your fetch script. - -## Step 4: Write the parsing script - -The data we just fetched needs to be parsed before we can use it, so we'll write a script to do that. Parse scripts can be written in any language, but please use a common language like Perl, Python, JavaScript, Go, or similar. The harder your script is to run, the harder it will be for us to integrate your Instant Answer into our environment, and the harder future maintenance will be. **Please keep things as simple and straightforward as possible.** - -**Note:** Our machines are running **Ubuntu 12.04**. These are the machines that will be used to test and run your parser, so **please make sure your language and dependencies are compatible with our environment**. - -Since Fatheads can have vastly different data sources, we can't tell you what is the best approach to parsing. We suggest you look through the [Fathead repository](https://github.com/duckduckgo/zeroclickinfo-fathead/tree/master/share/fathead) to get ideas from other developers. - -For the purposes of this tutorial, we're going to use Python to parse the git repository we just cloned. - -###### share/fathead/hello_world/parse.py - -```python -if __name__ == "__main__": - # setup logger - logging.basicConfig(level=logging.INFO,format="%(message)s") - logger = logging.getLogger() - - # dump config items - count = 0 - with open("output.txt", "wt") as output_file: - for filepath in glob.glob('download/*/*'): - _,filename = os.path.split(filepath) - # ignore some "languages" - if filename not in ['ls.ls', 'readlink.readlink', 'piet.png']: - - language,_ = os.path.splitext(filename) - with open(filepath, 'r') as f: - source = f.read() - source = source.replace('\\n', '~~~n') - source = source.replace('\n', '\\n') - source = source.replace('~~~n', '\\\\n') - source = source.replace('\t', '\\t') - - item = HelloWorldItem(language, filename, source) - if count % 10 == 0: - logger.info("%d languages processed" % count ) - - count += 1 - output_file.write(str(item)) - logger.info("Parsed %d domain rankings successfully" % count) -``` - -This uses a relatively simple loop, `for filepath in glob.glob('download/*/*'):` which iterates over each of the files in the repository our fetch script cloned into the `download/` directory. After doing some checks to make sure the files is a "Hello World" program, it opens the file, reads the file, escapes newline (`\n`) and tab (`\t`) characters and then then "parses" the file to create an `item` using the `HelloWorldItem` class: - -###### parse.py (continued) - -```python -class HelloWorldItem: - def __init__(self, language, filename, source): - self.language = language - self.filename = filename - self.source = source - - def __str__(self): - - fields = [ "hello world (%s)" % self.language, #title - "A", #type - "", - "", - "", #categories - "", - "", #see_also - "", - "", #external_links - "", - "", #images - "Hello World in %s (%s)
%s" % (self.language, self.filename, self.source), - "https://github.com/leachim6/hello-world" - ] - - output = "%s\n" % ("\t".join(fields)) - - return output -``` - -The `HelloWorldItem` class works by defining the string representation of itself, which we later use to print our object into `output.txt`: `output_file.write(str(item))`. - -The most important thing to note about the string representation function, `def __str__(self):`, is that it creates an array containing the relevant information we need to pass along to `output.txt`, which **includes a few blank lines to represent the optional output fields we have not used** and then joins them with tab characters (`\t`). It also appends a newline (`\n`) at the end of our built string. This string now represent a single line in our `output.txt` file and denotes the **title**, **abstract**, **synopsis** and the **source_url**. - -## Step 5: Write a wrapper for the parse script - -Since the parse script can be written in any language, we use a thin wrapper called `parse.sh` so we have a consistent name to execute. This is easy: - -###### parse.sh - -```shell -#!/bin/sh - -python parse.py -``` - -## Step 6 - -The last thing we need to write is a README.txt. This file is used to inform other developers and DuckDuckGo staff of any special considerations. At the very least, it should list dependencies required by your parse script. You may also want to mention known shortcomings, opportunities for future improvement, or anything else you think might be important. - -###### README.txt - -``` -Dependencies: - -Python 3 - -To install in Ubuntu 10.04 (Lucid): - -sudo apt-get install python3-minimal -``` - -## Step 7 - -You're done! The only thing left is to submit your Instant Answer for review. Please see [Preparing for a Pull Request](/duckduckhack/preparing_for_a_pull_request). - -**Note:** The duckpan server cannot (currently) run Fatheads. That means you can't run or test your plugin before submission. - diff --git a/duckduckhack/fathead/fathead_overview.md b/duckduckhack/fathead/fathead_overview.md deleted file mode 100644 index 5a4f71d57..000000000 --- a/duckduckhack/fathead/fathead_overview.md +++ /dev/null @@ -1,121 +0,0 @@ -# Fathead Instant Answers - -**If you have questions, [request an invite to our Slack team](mailto:QuackSlack@duckduckgo.com?subject=AddMe) and we'll be happy to chat!** - -Fatheads are key-value Instant Answers backed by a database. The keys of the database are typically words or phrases, and they are also used as the triggers for the Instant Answer. When a database key is queried, the corresponding row from the database is returned, which is typically a paragraph of text. Developing a Fathead Instant Answer entails writing a program that generates an **output.txt** file. This tab-delimited file indicates the keys and values for the database, as well as some other important information discussed below. The program may be written in Perl, Python, JavaScript, or Ruby, and if necessary, will be run periodically to keep the database current. - -The **output.txt** file that is generated will be consumed by the DuckDuckGo backend, cleaned up (details below) and then finally entered into an SQL database. - -## Structure - -Each Fathead Instant Answer has its own directory, which looks like this: - -- ``lib/DDG/Fathead/FatheadName.pm`` – a Perl file that lists some meta information about the Instant Answer - -- ``share/fathead/fathead_name/fetch.sh`` – a shell script called to fetch the data. - -- ``share/fathead/fathead_name/download/`` – a directory to hold temp files created by fetch.sh - -- ``share/fathead/fathead_name/parse.xx`` – the script used to parse the data once it has been fetched. .xx can be .pl, .py, .rb, .js, etc. depending on what language you use. - -- ``share/fathead/fathead_name/parse.sh`` – a shell script wrapper around parse.xx - -- ``share/fathead/fathead_name/README.txt`` – Please include any dependencies here, or other special instructions for people trying to run it. Currently, Fathead Instant Answers require some hand work by DuckDuckGo staff during integration. - -- ``share/fathead/fathead_name/output.txt`` – the output file. It generally should **not** be committed to github, but may be committed if it is small (<1MB). - -- ``share/fathead/fathead_name/data.url`` – an optional pointer to a URL in the cloud somewhere, which contains the data to process. - - -## Data File Format - -Please name the output file output.txt (tab delimited) but do not store the data file(s) in the repository (as noted above) unless it is under 1MB. - -The output file needs to use UTF-8 encoding so we can process it. Please make sure you write your parse scripts accordingly or we'll probably run into some problems getting it integrated. - -The output format from `parse.xx` depends on the type of content. In any case, it should be a tab delimited file, with one line per entry. Usually there is no need for newline characters, but if there is a need for some reason, escape them with a backslash like `\\\n`. If you want a newline displayed, use `
` - -Every line in the output file must contain thirteen fields, separated by tabs. Some of the fields may be empty. The fields are as follows: - - 1. Full article title. Must be unique across the data set of this Instant Answer. *This field is required.* Examples: `Perl` - - 2. Type of article. `A` for actual articles, `D` for disambiguation pages, or `R` for redirects. *This field is required.* - - 3. *For redirects only.* An alias for a title such as a common misspelling or AKA. The format is the full title of the Redirect, e.g., DuckDuckGo. Examples: `Duck Duck Go -> DuckDuckGo` - - 4. *Ignore.* - - 5. Categories. An article can have multiple categories, and category pages will be created automatically. An example of a category page can be seen at [http://duckduckgo.com/c/Procedural_programming_languages](http://duckduckgo.com/c/Procedural_programming_languages). Multiple categories must be separated by an escaped newline, `\\n`. Categories should generally end with a plural noun. Examples: `Procedural programming languages\\n` - - 6. *Ignore.* - - 7. Related topics. These will be turned into links in the Zero-click Info box. Examples: `[[Perl Data Language]]`. If the link name is different, `[[Perl Data Language|PDL]]`. - - 8. *Ignore.* - - 9. External links. These will be displayed first when an article is shown. The canonical example is an official site, which looks like ``[$url Official site]\\n``. You can have several, separated by an escaped newline, though only a few will be used. You can also have before and after text or put multiple links in one. Examples: ``Before text [$url link text] after text [$url2 second link].\\n`` - - 10. *For disambiguation pages only.* Content of disambiguation page. Should be a list, where each item is a link to a page followed by a one sentence description ending in a period. The items have to be separated by ``\n``. Examples: ``*[[Enum.value]], returns the value part of the Enum.\n*[[Pair.value]], returns the value part of the Pair.`` - - 11. Image. You can reference an external image that we will download and reformat for display. Examples: ``[[Image:$url]]`` - - 12. Abstract. This is the snippet info. It should generally be ONE readable sentence, ending in a period. Examples: ``Perl is a family of high-level, general-purpose, interpreted, dynamic programming languages.`` - - 13. URL. This is the full URL for the source. If all the URLs are relative to the main domain, this can be relative to that domain. Examples: `http://www.perl.org` - - - -An example snippet from parse.xx written in [Perl](https://duckduckgo.com/Perl) may look like this: - -```perl - -my $title = $line[0] || ''; -my $type = $line[1] || ''; -my $redirect = $line[2] || ''; -my $otheruses = $line[3] || ''; -my $categories = $line[4] || ''; -my $references = $line[5] || ''; -my $see_also = $line[6] || ''; -my $further_reading = $line[7] || ''; -my $external_links = $line[8] || ''; -my $disambiguation = $line[9] || ''; -my $images = $line[10] || ''; -my $abstract = $line[11] || ''; -my $source_url = $line[12] || ''; - -print "$title\t$type\t\t\t$categories\t\t$see_also\t\t$external_links\t$disambiguation\t$images\t$abstract\t$source_url\n"; -``` - -There is a pre-process script that is run on this output, which: - -* Drops duplicates (on `$title`). - -* Reduces `$abstract` to one sentence. - -* Drops records that look like spam. - -* Normalizes spacing. - -* Makes sure the `$abstract` ends in a sentence. - - -## Code Blocks - -If you want to include a code snippet or another pre-formatted example in the abstract, like the [perl](https://duckduckgo.com/?q=perl+open) Fathead, wrap the code block like this: - -```html -
code block goes here
-``` - -For multiline code snippets, use only `\n` to separate lines: - -```html -
foo\nbar\nbaz
-``` - -## Notes - -There should be no duplicates in the `$page` (first) variable. If you have multiple things named the same thing you have a number of options: - - make disambiguation pages - - put everything in one snippet - - pick the most general one diff --git a/duckduckhack/spice/spice_advanced_frontend.md b/duckduckhack/frontend-reference/accessing-query-remainder.md similarity index 86% rename from duckduckhack/spice/spice_advanced_frontend.md rename to duckduckhack/frontend-reference/accessing-query-remainder.md index ffdb1b294..0ab936971 100644 --- a/duckduckhack/spice/spice_advanced_frontend.md +++ b/duckduckhack/frontend-reference/accessing-query-remainder.md @@ -1,14 +1,12 @@ -# Advanced Spice Frontend +# Accessing the Query Remainder -## Accessing the query remainder - -It's possible to get the user's full original query with `DDG.get_query()`, but often you may only need a specific part of it. +While it's possible to get the user's full original query with [`DDG.get_query()`](#), but often you may only need a specific part of it. For example, let's look at the [Hacker News IA](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/HackerNews.pm). If a user searches for something like "hacker news oculus rift", you may want to ignore the trigger words ("hacker news") and only retrieve what the user was searching for ("oculus rift"). -You could replace those trigger words manually, but it's better not to have a duplicate list of trigger words between your backend and frontend code. Luckily there's a way to get the user's query as processed by the backend code. +You could replace those trigger words manually, but it's better not to have a duplicate list of trigger words between your backend and frontend code. Luckily, there's a way to get the user's query as processed by the backend code. ```javascript var script = $('[src*="/js/spice/hacker_news/"]')[0], @@ -19,7 +17,7 @@ var script = $('[src*="/js/spice/hacker_news/"]')[0], console.log(decodedQuery); // "oculus rift" ``` -Here's what this snippet does: +Here's what this snippet does, line by line: ``` var script = $('[src*="/js/spice/hacker_news/"]')[0], diff --git a/duckduckhack/goodie/goodie_cheat_sheets.md b/duckduckhack/frontend-reference/cheat-sheet-reference.md similarity index 66% rename from duckduckhack/goodie/goodie_cheat_sheets.md rename to duckduckhack/frontend-reference/cheat-sheet-reference.md index 0c0210731..c385cee72 100644 --- a/duckduckhack/goodie/goodie_cheat_sheets.md +++ b/duckduckhack/frontend-reference/cheat-sheet-reference.md @@ -4,6 +4,8 @@ A popular (and perfect) use of Goodies is to create cheat sheets which are avail ![tmux cheat sheet](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Ftmux_cheat_sheet.png&f=1) +For detailed instructions on how to create a Cheat Sheet, follow any of the [Cheat Sheet Walkthroughs](#). + ## Cheat Sheet Ideas A cheat sheet is not always the best representation for your data. Sometimes, an Instant Answer is better built as a full Goodie or another type of Instant Answer. When thinking about your cheat sheet idea, think about what is useful to a searcher. Keyboard shortcuts, video game cheat codes, and similar data can be wonderfully useful as a cheat sheet. Here are some other cheat sheet Instant Answers we love: @@ -14,53 +16,19 @@ A cheat sheet is not always the best representation for your data. Sometimes, an [Harry Potter spells](https://duckduckgo.com/?q=harry+potter+spells+cheat+sheet&ia=cheatsheet) [Tennis info](https://duckduckgo.com/?q=tennis+cheat+sheet&ia=cheatsheet) -You can also [check out all the Cheat Sheets that others have made](https://duck.co/ia?q=cheat+sheet) to inspire you for another topic! - -## How to Add Your Cheat Sheet - -Once you have an [idea for a cheat sheet](#cheat-sheet-ideas), it's easy to add it to the DuckDuckGo Cheat Sheet Goodie. - -**The first step is to [set up your development environment](https://duck.co/duckduckhack/setup_dev_environment).** During setup [following the steps](https://duck.co/duckduckhack/setup_dev_environment) to set up your environment and choose the option to fork/clone the Goodies repository (where Cheat Sheets live). - -### Create a JSON File - -After cloning the Goodies repository, you simply need to add **one JSON file** to the existing `/json` folder in the [Cheat Sheets share directory](https://github.com/duckduckgo/zeroclickinfo-goodies/tree/master/share/goodie/cheat_sheets/json). - -To do this in Codio, use the left-hand panel to enter the `/zeroclickinfo-goodies` repository directory. Then use the file tree to click into the `/share/goodie/cheat_sheets` directory. Then click the **json** folder. - -Up in the **File menu**, click **"Create New File"**, and enter the name of your cheat sheet as a JSON file (make sure it's saving to the `cheat_sheets` directory). For example, if the trigger for your cheat sheet is "gopro cheat sheet" create a new file called `gopro.json`. - -Erase any pre-filled contents, and enter the values for your cheat sheet using the [cheat sheet JSON syntax](#cheat-sheet-json-syntax). Feel free to copy the code in the following section into your new file as a convenient template. - -Conveniently, with cheat sheets there's no need to create a new Instant Answer. There is also no need to edit the `CheatSheets.pm` file, `cheat_sheets.js`, or `cheat_sheets.css`. Simply save your new file, and proceed to test your work. +You can also [check out all the Cheat Sheets that others have made](https://duck.co/ia?q=cheat+sheet) to inspire you for another topic. -### Test Your Cheat Sheet +## Creating a Cheat Sheet -The next step is to test your code and make sure it works like you intended. To do this, we'll create a test server that will allow you to view your Instant Answer as it would appear above DuckDuckGo search results. +*For detailed instructions on how to create a Cheat Sheet, follow any of the [Cheat Sheet Walkthroughs](#).* -1. In Codio, open your Terminal by clicking on **Tools > Terminal**. -2. Change into the `zeroclickinfo-goodies` directory by typing `cd zeroclickinfo-goodies` at the command line. -3. Next, type **`duckpan server`** and press "**Enter**". The Terminal should print some text and let you know that the server is listening on port 5000. +All DuckDuckGo cheat sheets actually fall under one single Instant Answer - the Cheat Sheet Goodie. Each cheat sheet is defined in its own JSON file, in the [`/json`](https://github.com/duckduckgo/zeroclickinfo-goodies/tree/master/share/goodie/cheat_sheets/json) folder of the Cheat Sheet Goodie directory. - ``` - Starting up webserver... - You can stop the webserver with Ctrl-C - HTTP::Server::PSGI: Accepting connections at http://0:5000/ - ``` - -4. Click the "**DuckPAN Server**" button at the top of the screen. A new browser tab should open and you should see the DuckDuckGo Homepage. Type the name of your cheat sheet, plus **"cheat sheet"** and press "**Enter**". -5. You should see your cheat sheet show up in the search results! **Make sure it displays correctly; check that all escaped characters and code blocks appear as you intended.** -6. When you're done testing, go back to the Terminal, andpress "**Ctrl+C**" to shut down the DuckPAN Server. The Terminal should return to a regular command prompt. - -When your cheat sheet works like you want it to, you're ready to submit your contribution. - -### Submit Your Pull Request - -Submitting your cheat sheet is similar to submitting any Instant Answer contribution. New code is submitted using pull requests on GitHub. To make a pull request, follow the [submission and review instructions](https://duck.co/duckduckhack/submission_and_review). +That means there's no need to create a new Instant Answer. There is also no need to edit the `CheatSheets.pm` file, `cheat_sheets.js`, or `cheat_sheets.css`. Simply save your new JSON file, and proceed to test your work. ## How Are Cheat Sheets Triggered? -Triggering is built in to the main Cheat Sheets Goodie. When the name of your cheat sheet file is searched with any of the [built-in trigger words](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CheatSheets.pm), your Instant Answer will be shown. +Triggering is already built in to the main Cheat Sheets Goodie. When the name of your cheat sheet file is searched together with any of the [built-in trigger words](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CheatSheets.pm), your Instant Answer will be shown. For example, for the *vim* text editor, the Instant Answer will be triggered on: @@ -69,19 +37,21 @@ For example, for the *vim* text editor, the Instant Answer will be triggered on: - "vim *commands*" - "vim *guide*" - "vim *shortcuts*" -- ...and so on. View all terms in [CheatSheets.pm](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CheatSheets.pm). +- ...and so on. + +*If you're curious you can view all terms listed in [CheatSheets.pm](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CheatSheets.pm).* If you'd like to add more names for the subject of your cheat sheet (in addition to the file name), you can specify them in the `aliases` property of your cheat sheet JSON file. For example, if your cheat sheet file is `lord-of-the-rings.json`, a natural alias is 'LOTR'. For details check out the [Cheat Sheet JSON Reference](#cheat-sheet-json-reference). ## Cheat Sheet JSON Reference -Below is a summary of the [`vim.json`](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/share/goodie/cheat_sheets/vim.json) file, which displays a cheat sheet when searching for ["vim cheat sheet"](https://duckduckgo.com/?q=vim+cheat+sheet&ia=answer). +Below is a summary of the [`vim.json`](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/share/goodie/cheat_sheets/json/vim.json) file, which displays a cheat sheet when searching for ["vim cheat sheet"](https://duckduckgo.com/?q=vim+cheat+sheet&ia=answer). ![vim cheat sheet](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fvim_cheat_sheet.png&f=1) -The above Instant Answer was created by simply adding [`vim.json`](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/share/goodie/cheat_sheets/vim.json), explained below. +The above Instant Answer was created by simply adding [`vim.json`](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/share/goodie/cheat_sheets/json/vim.json), explained below. -**For convenience, we encourage you to copy the [`vim.json`](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/share/goodie/cheat_sheets/vim.json) code into your new file, as a starting point.** Copy the [raw file](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/share/goodie/cheat_sheets/vim.json), as the JSON below won't work due to inline comments. +**For convenience, we encourage you to copy the [`vim.json`](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/share/goodie/cheat_sheets/json/vim.json) code into your new file, as a starting point.** Copy the [raw file](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/share/goodie/cheat_sheets/json/vim.json), as the JSON below won't work due to inline comments. ```javascript { @@ -151,6 +121,8 @@ The above Instant Answer was created by simply adding [`vim.json`](https://githu We've seen a wonderfully wide variety of cheat sheets; we realized that one visual format doesn't fit all ideas. We've created an *optional* `template_type` property so you can pick the best look for your cheat sheet. +![Cheat sheet template types](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fcheatsheet-template-types.png&f=1) + Here are the available `template_type` values: - `keyboard` - the default (see it live at ["vim cheatsheet"](https://duckduckgo.com/?q=vim+cheatsheet&ia=cheatsheet)) @@ -158,8 +130,6 @@ Here are the available `template_type` values: - `code` - (see it live at ["regex cheatsheet"](https://duckduckgo.com/?q=regex+cheat+sheet&ia=cheatsheet)) - `reference` - (see it live at ["wu-tang cheatsheet"](https://duckduckgo.com/?q=wu-tang+cheat+sheet&ia=cheatsheet)) -![Cheat sheet template types](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fcheatsheet-template-types.png&f=1) - ### Syntax for `key` Property Cheat sheet actions often have several key combinations, which you can indicate in the syntax of each `key` property. diff --git a/duckduckhack/instant-answer-display/design_styleguide.md b/duckduckhack/frontend-reference/design-styleguide.md similarity index 100% rename from duckduckhack/instant-answer-display/design_styleguide.md rename to duckduckhack/frontend-reference/design-styleguide.md diff --git a/duckduckhack/instant-answer-display/display_reference.md b/duckduckhack/frontend-reference/display-reference.md similarity index 100% rename from duckduckhack/instant-answer-display/display_reference.md rename to duckduckhack/frontend-reference/display-reference.md diff --git a/duckduckhack/instant-answer-display/handlebars_helpers.md b/duckduckhack/frontend-reference/handlebars-helpers.md similarity index 100% rename from duckduckhack/instant-answer-display/handlebars_helpers.md rename to duckduckhack/frontend-reference/handlebars-helpers.md diff --git a/duckduckhack/spice/spice_js_api.md b/duckduckhack/frontend-reference/js-api-reference.md similarity index 85% rename from duckduckhack/spice/spice_js_api.md rename to duckduckhack/frontend-reference/js-api-reference.md index 6f3dc7fe0..19ac4e6a4 100644 --- a/duckduckhack/spice/spice_js_api.md +++ b/duckduckhack/frontend-reference/js-api-reference.md @@ -1,12 +1,10 @@ -## Spice JavaScript API Reference +## JavaScript API Reference -For a list of avaliable Spice JS functions see: +For a list of available frontend JavaScript functions see: -- [DDG Namespace Functions](http://duck.co/duckduckhack/spice_js_api#ddg-namespace) -- [Spice Namespace Functions](http://duck.co/duckduckhack/spice_js_api#spice-namespace) - -# DDG Namespace +# Functions in the DDG Namespace +The following functions are useful in the broader context of DuckDuckGo's frontend and available for use by Instant Answers. ## get_query_encoded() @@ -15,7 +13,6 @@ Provides the value of `DDG.get_query` as a URIEncoded string Note: The query is trimmed (i.e. no leading or trailing spaces) and has any extra spaces within the query removed - ## get_query() Provides the search query, as displayed in the search box @@ -23,7 +20,6 @@ Provides the search query, as displayed in the search box Note: The query is trimmed (i.e. no leading or trailing spaces) and has any extra spaces within the query removed - ## get_is_safe_search() Indicates if "safe search" is on, for the current query @@ -32,10 +28,9 @@ Indicates if "safe search" is on, for the current query *boolean*, Either `1` or `0` (on/off) - ## get_asset_path(spice_name, asset) -Provides the path to the given asset, for the specified Spice Instant Answer +Provides the path to the given asset, for the specified Instant Answer Example: @@ -43,7 +38,7 @@ Example: **Parameters** -**spice_name**: *string*, The name of the Spice (this gives us the correct `/spice/share` sub-directory) +**IA_name**: *string*, The name of the Instant Answer (this gives us the correct `/share/...` relative path) **asset**: *string*, The filename of the asset, including extension @@ -255,11 +250,11 @@ Example: ## commifyNumber(value) -Returns the comma seperated string representation of a number +Returns the comma separated string representation of a number **Parameters** -**value**: *number*, the number to comma seperate +**value**: *number*, the number to comma separate Example: @@ -326,42 +321,32 @@ Example: ------ -# Spice Namespace - - -## add(ops) -Add a Spice Instant Answer to the AnswerBar and display it - -Note: A detailed explanation of `Spice.add()` can be found in [Displaying your Spice](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/spice/spice_displaying.md) - -**Parameters** - -**ops**: *object*, The object containing all necessary information for creating a Spice Instant Answer +# DDH Namespace +The following functions are available and relevant to both Spice and Goodie Instant Answers. ## getDOM(id) -Provides a scoped DOM for a given Spice id +Provides a scoped DOM for a given the Instant Answer's id. -Note: Seeing as we maintain a cache, this is more efficient than using jQuery, i.e. `$('spice_name')` +Note: Seeing as we maintain a cache, this is more efficient than using jQuery, i.e. `$('IA_name')` Example: -`var $my_spice = Spice.getDOM('my_spice')` +`var $my_IA = DDH.getDOM('my_IA')` **Parameters** -**id**: *string*, The id of the Spice Instant Answer, should match the `id` property of the object given to `Spice.add()` +**id**: *string*, The id of the Instant Answer. If a Spice, should match the `id` property of the object given to `Spice.add()`. **Returns** -*object*, A jQuery object, matching the selector that targets the given Spice id - +*object*, A jQuery object, matching the selector that targets the given id. ## registerHelper(id, fn) -Provides access to `Handlebars.registerHelper()` so you can register helpers for your Spice +Provides access to `Handlebars.registerHelper()` so you can register helpers for your Instant Answer. **Parameters** @@ -370,9 +355,24 @@ Provides access to `Handlebars.registerHelper()` so you can register helpers for **fn**: *function*, The function we are registering +# Spice Namespace + +The following functions are available and relevant to Spice Instant Answers. + +## add(options) + +Add an Instant Answer to the AnswerBar and display it + +Note: A detailed explanation of `Spice.add()` can be found in [Displaying your Spice](#) + +**Parameters** + +**options**: *object*, The object containing all necessary information for creating a Spice Instant Answer + + ## failed(id) -Alerts the frontend that a Spice has stopped executing, preventing it from being displayed. +Alerts the frontend that an Instant Answer has stopped executing, preventing it from being displayed. Note: This is generally used when a Spice API returns no useful results. @@ -380,9 +380,12 @@ Example: ```javascript if (/* check for no results */) { - + Spice.failed() +} ``` +To see an example of this, visit the [Forum Lookup Walkthrough](#). + **Parameters** -**id**: *string*, The id of the Spice Instant Answer, should match the `id` property of the object given to `Spice.add()` \ No newline at end of file +**id**: *string*, The id of the Spice Instant Answer, should match the `id` property of the object given to `Spice.add()` diff --git a/duckduckhack/goodie/goodie_displaying.md b/duckduckhack/frontend-reference/setting-goodie-display.md similarity index 87% rename from duckduckhack/goodie/goodie_displaying.md rename to duckduckhack/frontend-reference/setting-goodie-display.md index 4503c46e5..89effd479 100644 --- a/duckduckhack/goodie/goodie_displaying.md +++ b/duckduckhack/frontend-reference/setting-goodie-display.md @@ -1,33 +1,83 @@ -# Displaying Your Goodie in the DuckDuckGo AnswerBar +# Displaying Your Goodie Instant Answer -The final step of providing your Goodie results is displaying them in the DuckDuckGo AnswerBar. +*Making a Cheat Sheet? Skip to the [Cheat Sheet Reference](#).* -## Setting Display Properties in a Goodie +The final step in every Instant Answer is displaying it to the user. All Instant Answers display in the "AnswerBar", which is the area above the organic search results. The way each Instant Answer is displayed and behaves is determined by a set of [display options](#) and [events](https://duck.co/duckduckhack/display_reference#events). This document shows you how to set these options for a Goodie. + +[Screenshot of answerbar just like spice] + +## Easy Structured Responses + +*If your answer requires html templates and special interactions, consider [setting display properties](#setting-display-properties-in-a-goodie).* + +Many Goodies are simple operations that return a string response. For example, the [Flip Text Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/FlipText.pm): + +![flip text goodie](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fflip_text_goodie.png&f=1) + +Displaying such Goodies is easy. Instead of setting display properties, simply return three properties: + +- Input (what the user typed in, perhaps highlighting how it was parsed) +- Operation (the term for what happened) +- Result (the final answer) + +For example, for the Flip Text Goodie: -Goodies are displayed according to a set of properties which can (mostly) be defined in each Goodie's Perl file. These options are returned as a hash called `structured_answer` when the Perl file finishes running. This hash is returned alongside the `$plaintext` string version of your Goodie result, used for the API): +![flip text goodie diagram](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fdiagrams%2Fflip_text_goodie_diagram.png&f=1) + +### Passing Structured Responses + +Here is the return statement of the [Flip Text Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/FlipText.pm): ```perl -return $plaintext, +my $result = upside_down($input); + +return $result, # text-only Goodie result, for the API structured_answer => { - ... + input => [html_enc($input)], + operation => 'Flip text', + result => html_enc($result), }; ``` -For an example of how this works, take a look at the final return statement of the [BPM to ms](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/BPMToMs.pm) Goodie Perl file. +The following properties are returned in the `structured_answer` hash: -Specifying options in Perl is the most straightforward method, but there are several optional properties that cannot be specified on the server-side. These must be [specified in the Goodie's frontend](#setting-goodie-display-properties-in-the-frontend), in a JavaScript file. +#### `input` [required, but can be empty] *array* -## Available Options +- Make sure to `html_enc()` any user input to prevent [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) +- To avoid displaying an input, pass an empty array: `[]` (see the [GUID Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/GUID.pm#L49)) + +#### `operation` [required] *string* + +- Stated as a command, e.g. "Calculate", "Flip text", "URL decode", etc. + +#### `result` [required] *string* + +- Make sure to `html_enc()` input to display characters properly +- **Do not pass custom HTML in this string**; instead [specify](#setting-goodie-display-properties-in-the-frontend) a [template](https://duck.co/duckduckhack/templates_overview). + +### Further Examples + +Here are some more Goodies that make use of simple, structured responses: + +- [URLDecode](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/URLDecode.pm#L49-L55) -The properties you can return in your `structured_answer` hash are documented in the [Instant Answer Display Reference](https://duck.co/duckduckhack/display_reference). This document provides an in-depth overview of all that the Instant Answer framework allows you to set. + ![goodie urldecode](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fgoodie_url_decode.png&f=1) -In some scenarios, you may also want to handle the AnswerBar's events (for example, to stop media playing when the user hides your Instant Answer). These [events](https://duck.co/duckduckhack/display_reference#events) are covered at the end of the reference. +- [GUID](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/GUID.pm#L46-L52) -### Where Display Properties Can Be Set + ![goodie guid](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fgoodie_guid.png&f=1) + +- [Calculator](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Calculator.pm) + + ![goodie calculator](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fgoodie_calculator.png&f=1) + +## Setting Display Properties in a Goodie -Most display properties can be set in either the Goodie's Perl (server code) or JavaScript (frontend). Some Display properties, by their nature, can only [be set in the frontend](#setting-goodie-display-properties-in-the-frontend). +*If your Goodie is very simple, consider passing a [structured response](#easy-structured-responses) instead.* -Here is a quick summary: +When developing a Goodie, display options can be set either in your backend (Perl) or frontend (JavaScript) code. Most display properties can be set in either. Some Display properties, by their nature, can only [be set in the frontend](#setting-goodie-display-properties-in-the-frontend). + +Here is a quick summary of the break down of [display options](#): @@ -113,9 +163,24 @@ Here is a quick summary: [Events](https://duck.co/duckduckhack/display_reference#events)| |✓ --> -## Setting Display Properties in a Goodie's Perl +## Setting Display Options on the Backend + +*If your Goodie is very simple, consider passing a [structured response](#easy-structured-responses) instead.* + +Most options can be defined in the Goodie's backend, the Perl file. These options are set by returning as a hash called `structured_answer` when the Perl file finishes running. This hash is returned alongside the `$plaintext` string version of your Goodie result, used for the API): + +```perl +return $plaintext, + structured_answer => { + ... + }; +``` + +For an example of how this works, take a look at the final return statement of the [BPM to ms](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/BPMToMs.pm) Goodie Perl file. -The following is a code summary of how the options covered in the [Instant Answer Display Reference](https://duck.co/duckduckhack/display_reference) are returned in your Goodie Perl file. +Specifying options in Perl is the most straightforward method, but there are several optional properties that cannot be specified on the server-side. These must be [specified in the Goodie's frontend](#setting-goodie-display-properties-in-the-frontend), in a JavaScript file. + +The following is a code summary of how the options covered in the [Instant Answer Display Reference](https://duck.co/duckduckhack/display_reference) are returned in your Goodie Perl file: ```perl return $plaintext, @@ -167,7 +232,7 @@ return $plaintext, For more information on each property and its usage, visit the [Instant Answer Display Reference](https://duck.co/duckduckhack/display_reference). -## Setting Goodie Display Properties in the Frontend +## Setting Goodie Display Options on the Frontend While most display properties can be set in a Goodie's Perl file, others by their nature must be specified in the frontend part of the code. These are: @@ -267,66 +332,4 @@ You might use Goodie JavaScript to create an in-browser game related to certain Feel free to reach out and ask us any questions, over at [open@duckduckgo.com](mailto:open@duckduckgo.com) -## Easy Structured Responses - -Many Goodies are simple operations that return a string response. For example, the [Flip Text Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/FlipText.pm): - -![flip text goodie](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fflip_text_goodie.png&f=1) - -Displaying such Goodies is easy. Instead of setting display properties, simply return three properties: - -- Input (what the user typed in, perhaps highlighting how it was parsed) -- Operation (the term for what happened) -- Result (the final answer) - -For example, for the Flip Text Goodie: - -![flip text goodie diagram](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fdiagrams%2Fflip_text_goodie_diagram.png&f=1) - -### Passing Structured Responses - -Here is the return statement of the [Flip Text Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/FlipText.pm): - -```perl -my $result = upside_down($input); - -return $result, # text-only Goodie result, for the API - structured_answer => { - input => [html_enc($input)], - operation => 'Flip text', - result => html_enc($result), - }; -``` - -The following properties are returned in the `structured_answer` hash: - -#### `input` [required, but can be empty] *array* - -- Make sure to `html_enc()` any user input to prevent [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) -- To avoid displaying an input, pass an empty array: `[]` (see the [GUID Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/GUID.pm#L49)) - -#### `operation` [required] *string* - -- Stated as a command, e.g. "Calculate", "Flip text", "URL decode", etc. - -#### `result` [required] *string* - -- Make sure to `html_enc()` input to display characters properly -- **Do not pass custom HTML in this string**; instead [specify](#setting-goodie-display-properties-in-the-frontend) a [template](https://duck.co/duckduckhack/templates_overview). - -### Further Examples - -Here are some more Goodies that make use of simple, structured responses: - -- [URLDecode](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/URLDecode.pm#L49-L55) - - ![goodie urldecode](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fgoodie_url_decode.png&f=1) - -- [GUID](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/GUID.pm#L46-L52) - - ![goodie guid](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fgoodie_guid.png&f=1) - -- [Calculator](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Calculator.pm) - - ![goodie calculator](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fgoodie_calculator.png&f=1) diff --git a/duckduckhack/spice/spice_displaying.md b/duckduckhack/frontend-reference/setting-spice-display.md similarity index 77% rename from duckduckhack/spice/spice_displaying.md rename to duckduckhack/frontend-reference/setting-spice-display.md index b4b91a76b..22bc9be6a 100644 --- a/duckduckhack/spice/spice_displaying.md +++ b/duckduckhack/frontend-reference/setting-spice-display.md @@ -1,4 +1,6 @@ -# Adding Your Spice to the DuckDuckGo AnswerBar +# Displaying Your Spice Instant Answer + +The final step in every Instant Answer is displaying it to the user. All Instant Answers display in the "AnswerBar", which is the area above the organic search results. The way each Instant Answer is displayed and behaves is determined by a set of [display options](#) and [events](https://duck.co/duckduckhack/display_reference#events). This document shows you how to set these options for a Spice. Once your Instant Answer has been triggered, and the API request has returned a response to the client, the final step is to display your results onscreen. @@ -6,9 +8,9 @@ Once your Instant Answer has been triggered, and the API request has returned a ## Contents of the Spice Frontend Callback -The Spice frontend callback function, [covered in the Spice Basic Tutorial](https://duck.co/duckduckhack/spice_basic_tutorial#npm-spice-frontend-javascript), contains the code which displays your Spice. +The Spice frontend (Javascript) contains a callback which receives the results of the API call, and then displays your Spice. For an example of how the Spice frontend callback works, check out the [forum lookup](#) walkthrough. -The most important part of this callback, and often the only part, is calling [`Spice.add()`](#codespiceaddcode-overview). This function is powerful and gives you a lot of control over your results' appearance, context, and user interactions. +The most important part of this callback, and often the only part, is calling [`Spice.add()`](#codespiceaddcode-overview). This function gives you a lot of control over your results' appearance, context, and user interactions. ## Available Options diff --git a/duckduckhack/instant-answer-display/subtemplates.md b/duckduckhack/frontend-reference/subtemplates.md similarity index 100% rename from duckduckhack/instant-answer-display/subtemplates.md rename to duckduckhack/frontend-reference/subtemplates.md diff --git a/duckduckhack/instant-answer-display/template_groups.md b/duckduckhack/frontend-reference/template-groups.md similarity index 100% rename from duckduckhack/instant-answer-display/template_groups.md rename to duckduckhack/frontend-reference/template-groups.md diff --git a/duckduckhack/instant-answer-display/templates_overview.md b/duckduckhack/frontend-reference/templates-overview.md similarity index 100% rename from duckduckhack/instant-answer-display/templates_overview.md rename to duckduckhack/frontend-reference/templates-overview.md diff --git a/duckduckhack/instant-answer-display/templates_reference.md b/duckduckhack/frontend-reference/templates-reference.md similarity index 100% rename from duckduckhack/instant-answer-display/templates_reference.md rename to duckduckhack/frontend-reference/templates-reference.md diff --git a/duckduckhack/function-hacks/calculation.md b/duckduckhack/function-hacks/calculation.md new file mode 100644 index 000000000..5e57d3a17 --- /dev/null +++ b/duckduckhack/function-hacks/calculation.md @@ -0,0 +1,332 @@ +# How to Make a Quick Calculation Tool + +Some of the most delightful Instant Answers are easy calculation tools that work right from the search bar - such as [conversions](https://duckduckgo.com/?q=convert%205%20oz%20to%20grams&ia=answer), [permutations](https://duckduckgo.com/?q=16+permutation+3&ia=answer), or [aspect ratios](https://duckduckgo.com/?q=aspect+ratio+4%3A3+640%3A%3F&ia=answer). If you're thinking about building an Instant Answer that performs a quick calculation, this walkthrough is a great example. + +We're going to build the [Greatest Common Factor](https://duck.co/ia/view/greatest_common_factor) Instant Answer. See it in action by searching for ["12 76 greatest common factor"](https://duckduckgo.com/?q=12+76+greatest+common+factor&ia=answer): + +![](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fgcf.png) + +## How It Works + +When a user searches anything containing words such as "gcf", "greatest common factor", etc. in the query, DuckDuckGo will trigger this Instant Answer. + +When the Instant Answer is triggered, DuckDuckGo executes Perl code on the server to calculate the greatest common factor. First, it checks that there are two or more numbers present. Then, it performs the calculation. Finally, it returns the information to display to the user. + +Let's code it. + +## Anatomy of this Instant Answer + +Because this Instant Answer executes as Perl code on the server, and doesn't require an external source of data, it's called a "Goodie" Instant Answer. All Goodie Instant Answers are kept together in the [Goodie repository](#) on Github. + +A Goodie can be a combination of several backend and frontend files, each handling a different aspect of the process. In our case, however, we can get away with no frontend files, because we take advantage of the simple [structured answer display](#). + +Backend files: + +File | Purpose | Location +-----|---------|--------- +[`GreatestCommonFactor.pm`](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/GreatestCommonFactor.pm) | Specifies the query triggers, calculation, and the metadata (such as attribution, name, and so on). | Perl files are placed in the [`zeroclickinfo-spice/lib/DDG/Goodie`](https://github.com/duckduckgo/zeroclickinfo-goodies/tree/master/lib/DDG/Goodie) directory. +[`GreatestCommonFactor.t`](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/t/GreatestCommonFactor.t) | A test file; it asserts that specific search queries will trigger (or not trigger) this Instant Answer, and what responses to expect | Test files are placed in the [`zeroclickinfo-goodies/t`](https://github.com/duckduckgo/zeroclickinfo-goodies/tree/master/t) directory. + +Frontend files: none for this goodie - using [structured answers](#) + +That's it - just one code file and one test file is all we need. Next, we'll go line by line and build it together from scratch. + +## Set Up Your Development Environment + +Before we begin coding, we'll need to set up our development environment. There are three main steps: + +1. Fork the [Spice Repository](#) on Github.com. ([How?](#)) +2. Fork the [DuckDuckGo environment](#) on Codio.com (our tools). ([How?](#)) +3. Clone your Github fork onto the Codio environment. ([How?](#)) + +If this is your first time developing an Instant Answer, check out our [detailed, step-by-step guide](#) to getting your development environment set up. + +## Create a New Instant Answer + +In Codio, load the terminal, and change into the Goodie repository's home directory, `zeroclickinfo-goodies`: + +[Screenshot of clicking terminal] + +``` +[01:07 PM codio@border-carlo workspace ]$ cd zeroclickinfo-goodies +``` + +The `duckpan` tool helps make and test Instant Answers. To create new Goodie boilerplate, run **`duckpan new`**: + +``` +[01:08 PM codio@border-carlo zeroclickinfo-goodies {master}]$ duckpan new +Please enter a name for your Instant Answer : +``` + +Type `Greatest Common Factors` (since *Greatest Common Factor* already exists in the repository, we'll add an 's' for this tutorial). The tool will do the rest: + +``` +Please enter a name for your Instant Answer : Greatest Common Factors +Created file: lib/DDG/Goodie/GreatestCommonFactors.pm +Created file: t/GreatestCommonFactors.t +Successfully created Goodie: GreatestCommonFactors +``` + +That's convenient: The files have each been named - and located - according to the project's conventions. Internally, each file contains correct boilerplate to save us time. + +## `GreatestCommonFactors.pm` + +Let's open up `HackerNewz.pm`. + +Navigate using the Codio file tree on the left, and double click on the file, in the `lib/DDG/Spice/` directory. It'll be full of comments and sample code we can change as we please. + +### Settings and Metadata + +Each Instant Answer is a Perl package, so we start by declaring the package namespace in CamelCase format. This was done automatically for us when we ran the `duckpan new` command: + +```perl +package DDG::Goodie::GreatestCommonFactors; +``` + +Next, change the comments to contain a short abstract. Easy enough: + +```perl +# ABSTRACT: Returns the greatest common factor of the two numbers entered +``` + +Now we'll import the Goodie class (as well as tell the Perl compiler to be strict) - also already done for us: + +```perl +use DDG::Goodie; +use strict; +``` + +We'll specify the `answer_type` (filled in already), and caching. Since the same query will always return the same result, we'll leave caching on: + +```perl +zci answer_type => "greatest_common_factors"; +zci is_cached => 1; +``` + +Now for the Metadata. Because there's so many Instant Answers, metadata helps us organize, describe, and attribute your contribution. They are also used to automatically generate [Instant Answer Pages](https://duck.co/ia) - plus give you credit right on DuckDuckGo.com. + +For example, these are the Metadata values used in the live *GreatestCommonFactor* answer. You can learn more in the [metadata reference](#). + +```perl +primary_example_queries 'GCF 121 11'; +secondary_example_queries '99 9 greatest common factor'; +description 'returns the greatest common factor of the two entered numbers'; +name 'GreatestCommonFactor'; +topics 'math'; +category 'calculations'; +attribution github => [ 'https://github.com/austinheimark', 'Austin Heimark' ]; +``` + +### Triggers + +Triggers tell DuckDuckGo when to display our Instant Answer. Replace the boilerplate trigger code to the following: + +```perl +triggers startend => 'greatest common factor', 'gcf', 'greatest common divisor', 'gcd'; +``` + +This tells DuckDuckGo that if any of these strings occurs at the *start or end* of any user's search query, it should run this Instant Answer (specifically, the code in this Instant Answer's `handle` function). + +### Handle Function + +The `handle` function is the meat of our Goodie Instant Answer - where the functionality lives. It + +```perl +handle remainder => sub { + + # Everything else... + +}; +``` + +Our `handle` function accomplishes three things: + +1. Filter for queries that can be acted upon +2. Calculate the greatest common factors +3. Display the result + +Step 1 is to return immediately *unless* we have two or more numbers to calculate: + +```perl +handle remainder => sub { + + return unless /^\s*\d+(?:(?:\s|,)+\d+)*\s*$/; + + # Everything else... + +}; +``` + +Within our `handle` function, we have a [default variable](http://perlmaven.com/the-default-variable-of-perl) - which means it's implied in statements like the above regular expression match. + +In our `handle` function, our default variable takes on the value of `remainder`. The `remainder` refers to the rest of the query after removing our matched triggers (the remainder of 'greatest common factor 9, 81' would be '9, 81'). + +Let's split the numbers up into an array, and sort them in ascending order: + +```perl +handle remainder => sub { + + return unless /^\s*\d+(?:(?:\s|,)+\d+)*\s*$/; + + my @numbers = grep(/^\d/, split /(?:\s|,)+/); + @numbers = sort { $a <=> $b } @numbers; + + # Everything else... + +}; +``` + +Next we'll calculate the greatest common factor. Notice we'll place a subroutine outside the handle function: + +```perl +handle remainder => sub { + + return unless /^\s*\d+(?:(?:\s|,)+\d+)*\s*$/; + + my @numbers = grep(/^\d/, split /(?:\s|,)+/); + @numbers = sort { $a <=> $b } @numbers; + + my $result = shift @numbers; + foreach (@numbers) { + $result = gcf($result, $_) + } + + # Everything else... + +}; + +sub gcf { + my ($x, $y) = @_; + ($x, $y) = ($y, $x % $y) while $y; + return $x; +} +``` + +Finally let's display the result as a [structured answer](#). We'll format the numbers nicely, and specify what we got (`input`), what we did (`operation`), and what we got (`result`): + +```perl +handle remainder => sub { + + # Everything else... + + my $formatted_numbers = join(', ', @numbers); + $formatted_numbers =~ s/, ([^,]*)$/ and $1/; + + return "Greatest common factor of $formatted_numbers is $result.", + structured_answer => { + input => [$formatted_numbers], + operation => 'Greatest common factor', + result => $result + }; +}; +``` + +If you'd like to display your result using HTML templates and JS interactions, learn more about [displaying Goodie results](#). + +There's one final line of code on our backend. Because this is a Perl package, it must return `1` at the end to indicate successful loading: + +```perl +1; +``` + +## `GreatestCommonFactors.t` + +Creating a test file for your Instant Answer is a critical requirement for [submitting](#) your Instant Answer. You can learn more in the [Test File Reference](#). + +In this case, `duckpan new` created a test file for us, under `t/GreatestCommonFactors.t`. + +```perl +#!/usr/bin/env perl + +use strict; +use warnings; +use Test::More; +use DDG::Test::Goodie; + +zci answer_type => "greatest_common_factors"; +zci is_cached => 1; + +ddg_goodie_test( + + # Tests... + +); + +done_testing; +``` + +We'll specify several test queries to make sure they trigger our Instant Answer. You can see the full set used [on Github](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/t/GreatestCommonFactor.t): + +```perl +#!/usr/bin/env perl + +use strict; +use warnings; +use Test::More; +use DDG::Test::Goodie; + +zci answer_type => "greatest_common_factor"; +zci is_cached => 1; + +ddg_goodie_test( + [qw( DDG::Goodie::GreatestCommonFactor )], + 'gcf 9 81' => test_zci( + 'Greatest common factor of 9 and 81 is 9.', + structured_answer => { + input => ['9 and 81'], + operation => 'Greatest common factor', + result => 9 + } + ), + '1000 100 greatest common factor' => test_zci( + 'Greatest common factor of 100 and 1000 is 100.', + structured_answer => { + input => ['100 and 1000'], + operation => 'Greatest common factor', + result => 100 + } + ), + 'GCF 12 76' => test_zci( + 'Greatest common factor of 12 and 76 is 4.', + structured_answer => { + input => ['12 and 76'], + operation => 'Greatest common factor', + result => 4 + } + ), + # Etc... +); + +done_testing; +``` + +## Interactively Test Our Instant Answer + +Inside Codio, we can preview the behavior of all Instant Answers on a local test server. + +In Codio, load the terminal, and make sure you are in the `zeroclickinfo-goodies` main directory. If not, change into it. + +Enter the **`duckpan server`** command and press Enter. + +``` +[04:10 PM codio@border-carlo zeroclickinfo-goodies {master}]$ duckpan server + +``` + +The terminal should print some text and let you know that the server is listening on port 5000. + +``` +Starting up webserver... + +You can stop the webserver with Ctrl-C + +HTTP::Server::PSGI: Accepting connections at http://0:5000/ +``` + +Click the "**DuckPAN Server**" button at the top of the screen. A new browser tab should open and you should see the DuckDuckGo Homepage. Type your query to see the results (actual search results will be placeholders.) + +[Screenshot] + +That's it! Want to create an Instant Answer to go live on DuckDuckGo.com? Learn more about [submitting your idea](#). + +Have questions? [Email us](mailto:open@duckduckgo.com) or [say hello on Slack!](mailto:QuackSlack@duckduckgo.com?subject=AddMe) \ No newline at end of file diff --git a/duckduckhack/function-hacks/tool.md b/duckduckhack/function-hacks/tool.md new file mode 100644 index 000000000..e528ee1de --- /dev/null +++ b/duckduckhack/function-hacks/tool.md @@ -0,0 +1,3 @@ +# How to Make a Tool Function + +(Timer) \ No newline at end of file diff --git a/duckduckhack/function-hacks/transformation.md b/duckduckhack/function-hacks/transformation.md new file mode 100644 index 000000000..5494b00a8 --- /dev/null +++ b/duckduckhack/function-hacks/transformation.md @@ -0,0 +1 @@ +# How to Make a Transformation Tool \ No newline at end of file diff --git a/duckduckhack/getting-started/contributing.md b/duckduckhack/getting-started/contributing.md deleted file mode 100644 index b55fb517a..000000000 --- a/duckduckhack/getting-started/contributing.md +++ /dev/null @@ -1,83 +0,0 @@ -# How Can I Contribute? - -There are several options for contributing to DuckDuckHack, explained below. If you have any questions at any point, feel free to ask on one of our community channels: - -- [Request invite to our Slack team](mailto:QuackSlack@duckduckgo.com?subject=AddMe) -- [DuckDuckHack mailing list](https://duck.co/redir/?u=https%3A%2F%2Fwww.listbox.com%2Fsubscribe%2F%3Flist_id%3D197814) -- Do not hesitate to email us directly at [open@duckduckgo.com](mailto:open@duckduckgo.com). - -## New? Make Your First Contribution Today - -If this is your first time contributing to [DuckDuckHack](http://www.duckduckhack.com), you have two great ways to quickly make your first commit: - -**1. Make a [Cheat Sheet](https://duck.co/duckduckhack/goodie_cheat_sheets)** - -Cheat sheets are a super-easy way to contribute to the live DuckDuckGo AnswerBar very quickly, by editing a single file. Cheat sheets can be about anything, from Emacs and Vim to Game of Thrones house names or wine pairings. - -**2. Create a simple, complete "Hello World" Goodie with our [Quick Start Tutorial](https://duck.co/duckduckhack/goodie_quickstart)** - -This short tutorial will lead you through all the parts of building a full-loop Goodie. This is a perfect place to start if you have an idea for an original Instant Answer. - -## Create a New Instant Answer - -Once you're comfortable with the workflow and how Goodies work, we're excited to have you create your own original Instant Answer: - -**1. Choose an idea** - -Bring your own idea, or check out the ideas forum - especially [top voted answer ideas](https://duck.co/ideas/status/3?table_lnKRpLENwO2NUmZUyukQpw_sort=votes). - -Think of the searches you do most often: - -- What has always frustrated you about the results? -- What do you wish "just worked?" -- Is there a website or source of data that would help answer most/all of those searches? - -For additional inspiration, we recommend browsing examples of [live Instant Answers](https://duck.co/ia). - -**2. Plan your implementation** - -The first step is to research and plan your Instant Answer. Consider [the best way to implement](https://duck.co/duckduckhack/determine_your_instant_answer_type) your idea, and review the [docs and guidelines](https://duck.co/duckduckhack/ddh-intro) that apply. - -**3. Involve us** - -Before you start coding, [let us know your plans](mailto:open@duckduckgo.com). By involving us early we can provide guidance and potentially save you a lot of time and effort. Email us at [open@duckduckgo.com](mailto:open@duckduckgo.com) with what idea you're working on and how you're thinking of going about it. - -In addition, we'll promptly set up a [central Instant Answer page](http://www.duck.co/ia) on the community platform so others can know you're working on it and how they can help you. - -This sample will help you craft your message: - -```text -To: open@duckduckgo.com -Subject: Awesome Instant Answer Idea - -I'd like to make an Instant Answer to help people searching for ! -When someone searches for , it would be cool if they could see . -I'm going to accomplish this with a type Instant Answer. -This is the data source: . -This is the related Duck.co idea: . -This is my Github username: . - -Thanks! - -PS: DuckDuckGo is awesome! -``` - -## Improve an Existing Instant Answer - -Another great way to contribute is to improve an existing, live Instant Answer. It's a great way to get further acquainted with Instant Answers, as well as get implementation ideas. (Many contributors report completing their first fix within two hours of forking the repository!) - -**1. Choose a "low-hanging fruit"** - -We've made sure to identify these throughout our repositories for new contributors. - -- [Goodie Low Hanging Fruit](https://github.com/duckduckgo/zeroclickinfo-goodies/issues?q=is%3Aopen+is%3Aissue+label%3A%22Low-Hanging+Fruit%22) ([Goodie docs](https://duck.co/duckduckhack/goodie_overview)) -- [Spice Low Hanging Fruit](https://github.com/duckduckgo/zeroclickinfo-spice/issues?q=is%3Aopen+is%3Aissue+label%3A%22Low-Hanging+Fruit%22) ([Spice docs](https://duck.co/duckduckhack/spice_overview)) - -**2. Dive in** - -Go ahead and comment on any issues you're interested in helping with. Let us know what you're thinking and if you'd like any help or guidance. - -As always, feel free to [ask us anything](mailto:open@duckduckgo.com), and don't forget the handy [Instant Answer documentation](https://duck.co/duckduckhack/ddh-intro). - - - diff --git a/duckduckhack/getting-started/ddh-intro.md b/duckduckhack/getting-started/ddh-intro.md deleted file mode 100644 index 09658c7f7..000000000 --- a/duckduckhack/getting-started/ddh-intro.md +++ /dev/null @@ -1,24 +0,0 @@ -# Welcome to DuckDuckHack! - -We are a community of DuckDuckGo users who help improve the search engine with, "Instant Answers". To get started, please [request an invite to our Slack team](mailto:QuackSlack@duckduckgo.com?subject=AddMe). You may join multiple Slack channels to discuss your Instant Answer ideas with others who may have the same interests! - -- [Request Slack invite](mailto:QuackSlack@duckduckgo.com?subject=AddMe) - -You may also wish to join our DuckDuckHack Dev's email list (low traffic): - -- [DuckDuckHack developer mailing list](https://www.listbox.com/subscribe/?list_id=197814) - - -## What are Instant Answers? - -Instant Answers help you find what you're looking for in few or zero clicks. They're placed above ads and regular search results, and they're created/maintained by you (the community). Some Instant Answers are built from pure code and others require external sources (API requests), databases, or key-value stores. The possibilities are endless but the point is to provide a perfect result for every search. - -![App search Instant Answer example](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fapp_search_example.png&f=1) - -In the above example, [Quixey](http://quixey.com/) was a source that our own DuckDuckHack Community suggested for mobile app search. Now, any time someone searches for apps on DuckDuckGo, we request information directly from Quixey to help answer the search. - -To see just how simple it is to contribute an Instant Answer, check out David Farrell's PerlTricks post: [Writing Instant Answers is Easy](http://perltricks.com/article/169/2015/4/20/Writing-DuckDuckGo-instant-answers-is-easy). - -In these docs, we'll show you how to build Instant Answers that can do this and more. Start by reading about [ways to contribute](https://duck.co/duckduckhack/contributing). - - diff --git a/duckduckhack/getting-started/determine_your_instant_answer_type.md b/duckduckhack/getting-started/determine_your_instant_answer_type.md deleted file mode 100644 index c9705b50d..000000000 --- a/duckduckhack/getting-started/determine_your_instant_answer_type.md +++ /dev/null @@ -1,19 +0,0 @@ -# Determine Your Instant Answer Type - -The first step to planning your Instant Answer is deciding what type it is. Instant Answer type depends on how the result is obtained. - -Most Instant Answers rely on some type of data source (e.g. database, text file, API) to provide their answer. Others however, are able to provide their answer through the use of pure code, like a pre-existing Perl package on CPAN or simply a script you write (e.g. a string manipulation function). - -**Before you start coding, let us know your plans, and what Instant Answer type you'd like to use.** By involving us early we can provide guidance and potentially save you a lot of time and effort. Email us at [open@duckduckgo.com](mailto:open@duckduckgo.com) with what idea you're working on and how you're thinking of going about it. - -The following flowchart will help you to think about what type of Instant Answer you'll be creating, which is based on your Instant Answer's data source: - -![Instant Answer type flow chart](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Finstant_answer_flowchart.png&f=1) - - - -If you were able to determine your Instant Answer type, then you're ready to [setup your development environment](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/getting-started/setup_dev_environment.md)! - ------- - -If the right Instant Answer type is still not obvious, don't worry, the DuckDuckGo Community is here for you! Please visit our [Instant Answer Ideas Forum](https://dukgo.com/ideas) and post a new thread for your Instant Answer. Be sure to describe the Instant Answer you have in mind, and don't forget to indicate where you think the data should come from. Someone from the community or the DuckDuckGo staff will be able to help you determine the best course of action for you. diff --git a/duckduckhack/goodie/goodie_advanced_handle_functions.md b/duckduckhack/goodie/goodie_advanced_handle_functions.md deleted file mode 100644 index 028be4fdd..000000000 --- a/duckduckhack/goodie/goodie_advanced_handle_functions.md +++ /dev/null @@ -1,65 +0,0 @@ -# Advanced Handle Functions - -## Further Qualifying the Query - -Trigger words are coarse filters; they may send you queries you cannot handle. Your Instant Answer should return nothing in these cases. As such, you generally need to further qualify the query in your code. - -There are many techniques for doing this qualification. One of the most popular is to `return` as soon as a query can be disqualified. - -As an example, the [Base Goodie's](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Base.pm) has a `return` statement paired with an `unless` right on the first line of its `handle` function: - -```perl -handle remainder => sub { - return unless /^([0-9]+)\s*(?:(?:in|as)\s+)?(hex|hexadecimal|octal|oct|binary|base\s*([0-9]+))$/; - ... -} -``` - -## Using Files in the Share Directory - -Goodies can use simple text or html input files for display or processing. These files can be read once and reused to answer many queries without cluttering up your source code. - -The `share` function gives each Instant Answer access to a subdirectory of the repository's `share` directory. The subdirectory for your Instant Answer is based on its Perl package name which is transformed from CamelCase to underscore_separated_words. - - - -For example the [Passphrase Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Passphrase.pm) uses the `share` directory to hold data for processing purposes: - -```perl -my @words = share('words.txt')->slurp; -``` - -Here the `share` function grabs the `words.txt` file, found in `zeroclickinfo-goodies/share/goodie/passphrase/`. The returned object's `slurp` method is called which pushes each line of the file into the `@words` array. - -In another case, the [PrivateNetwork Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/PrivateNetwork.pm) uses its `share` directory to hold files for display purposes: - -```perl -my $text = scalar share('private_network.txt')->slurp, -my $html = scalar share('private_network.html')->slurp; - -handle sub { - $text, html => $html; -}; -``` - -Here each file is grabbed from `share/goodie/private_network/` and `slurp`ed in a `scalar` context. This returns the entire file as a **string** and assigns it to the appropriate variable. In the `handle` function, `$text` and `$html` are returned to display as plain text or HTML Instant Answer. - -One further consideration is whether your data file contains non-ASCII characters. If so, you will want to prepare the file with UTF-8 encoding. The [Shortcut Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Shortcut.pm) uses a UTF-8 data file: - -```perl -my @shortcuts = share('shortcuts.csv')->slurp(iomode => '<:encoding(UTF-8)'); -``` - -As with the Passphrase Goodie example above, each line becomes an entry in the resulting array. With this `iomode` set, the UTF-8 characters used to denote system-specific keys will be handled properly throughout the rest of the processing. - -## Generating Data Files - -In some cases, you may need to generate the data files you will `slurp` from the share directory. If so, please put the required generation scripts in the Goodie's `share` directory. While **shell scripts are preferred**, your scripts can be written in the language of your choice. - -As an example, the [CurrencyIn Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CurrencyIn.pm) uses a [combination of Shell and Python scripts](https://github.com/duckduckgo/zeroclickinfo-goodies/tree/master/share/goodie/currency_in) to generate its input data, `currency.txt`. - -## Stylesheets - -Goodies can load custom stylesheets by adding a `.css` file to their `share` directory, with a filename identical to the parent directory. Goodie stylesheets will be automatically included along with the Goodie's output when triggered. For example, the [RegexCheatSheet Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/RegexCheatSheet.pm) uses the directory `share/goodie/regex_cheat_sheet` which contains a [custom stylesheet](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/share/goodie/regex_cheat_sheet/regex_cheat_sheet.css). - -**Note:** Custom stylesheets should be used sparingly and only when absolutely necessary. \ No newline at end of file diff --git a/duckduckhack/goodie/goodie_basic_tutorial.md b/duckduckhack/goodie/goodie_basic_tutorial.md deleted file mode 100644 index aa1cb03c6..000000000 --- a/duckduckhack/goodie/goodie_basic_tutorial.md +++ /dev/null @@ -1,176 +0,0 @@ -## Basic Goodie Tutorial - -**If you've already completed the [Quick Start](https://duck.co/duckduckhack/goodie_quickstart)**, you are well prepared for this section. Here you will learn more about all the things you can do with Goodie. While many of the steps will be familiar, we recommend starting from step one and creating a new Goodie from scratch. - -This tutorial will show you how to build a Goodie Instant Answer from scratch. If you've already completed the [Quick Start](https://duck.co/duckduckhack/goodie_quickstart), this tutorial is a great next step for getting more comfortable with creating Goodies. - -_Stuck on something? Got a question? Shoot us an email at **open@duckduckgo.com** and we'll jump at the chance to help._ - -## Let Us Know What You're Working On - -**Before you start coding your new Instant Answer, let us know your plans.** By involving us early we can provide guidance and potentially save you a lot of time and effort. - -Email us at [open@duckduckgo.com](mailto:open@duckduckgo.com) with what idea you're working on and how you're thinking of going about it. - -## Automatically Generate Your Goodie Files - -The following tutorial will walk through each file and line of code necessary to build and test the example Goodie. However, for building your *own* Instant Answer, we've created a tool that **automatically creates the necessary boilerplate files**, with all of the correct naming conventions. - -The `duckpan new` tool will create the following Goodie files for you automatically, with the correct paths and naming conventions inside each file: - -- The backend Perl file with the right name, in the `DDG/Goodie/` directory -- The test file, in the `t/` testing directory - -*Currently the `duckpan new` command does not automatically generate any [Goodie frontend files](https://duck.co/duckduckhack/goodie_displaying#setting-goodie-display-properties-in-the-frontend)*. - -This allows you to focus on what makes your Goodie unique. To use this tool, follow these instructions: - -1. After [setting up your environment](https://duck.co/duckduckhack/setup_dev_environment), open your Terminal and enter the root directory of your local repository, `zeroclick-goodies\`. -2. At the command line, type `duckpan new` and hit enter. -3. When prompted, enter what you want to call your Goodie. - - *For example, `volume conversion`. (White spaces and character casing are automatically formatted.)* - -4. You will see a list of the boilerplate files created for you, each with the proper naming conventions already done: - - ``` - [10:08 PM ... zeroclickinfo-goodies {master}]$ duckpan new - Please enter a name for your Instant Answer : volume conversion - Created file: lib/DDG/Goodie/VolumeConversion.pm - Created file: t/VolumeConversion.t - Successfully created Goodie: VolumeConversion - ``` - -Congratulations! You've breezed through a lot of typing, files, and naming conventions. In the following tutorial, we'll explain each line of code and how to customize it to your Instant Answer idea. - -## The Chars Goodie - -In this tutorial, we'll be making a Goodie Instant Answer that checks the number of characters in a given search query. The end result, [chars.pm](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Chars.pm) works [like this](https://duckduckgo.com/?q=chars+How+many+characters+are+in+this+sentence%3F) and will contain the following: - -```perl -package DDG::Goodie::Chars; -# ABSTRACT: Give the number of characters (length) of the query. - -use DDG::Goodie; - -triggers start => 'chars'; - -handle remainder => sub { - return 'Chars: ' . length $_ if $_; - return; -}; -1; -``` - -Let's go through the Chars Goodie line by line. - -## Naming our Goodie Package - -To begin, open your favourite text editor like [gedit](http://projects.gnome.org/gedit/), notepad or [emacs](http://www.gnu.org/software/emacs/) and type the following: - -```perl -package DDG::Goodie::Chars; -# ABSTRACT: Give the number of characters (length) of the query. -``` - - - -Each Instant Answer is a [Perl package](https://duckduckgo.com/?q=perl+package), so we start by declaring the package namespace. For brand new development, you would change **Chars** to the name of the Instant Answer (written in [CamelCase](https://duckduckgo.com/?q=camelcase) format). - -The second line is a special comment line that is used for documentation purposes. - -## Import the Goodie Class - -Next, type the following [use statement](https://duckduckgo.com/?q=perl+use) to import [the magic behind](https://github.com/duckduckgo/duckduckgo/tree/master/lib/DDG) our Instant Answer system. - -```perl -use DDG::Goodie; -``` - -**Note:** Right after the above line, you should include any Perl modules that you'll be leveraging to help generate the answer. **Make sure** you add those modules to the `dist.ini` file in this repository. If you're not using any additional modules, carry on! - -## Define the Trigger Word(s) - -On the next line, type: - -```perl -triggers start => 'chars'; -``` - -**triggers** are keywords/phrases that tell us when to make the Instant Answer run. When a particular *trigger word* (or phrase) appears in a search query, the DuckDuckGo engine knows that *triggering* the Instant Answer may *handle* the query. - -In this case there is one trigger word: "**chars**". - -Let's say someone searched "**chars this is a test**". **chars** is the *first* word, so it would trigger our Goodie because the **start** keyword says, "Make sure the *trigger word* is at the *start* of the query." - -There are several other keywords like **start** which will be covered shortly. The **=>** symbol is there to separate the trigger words from the keywords (for readability). - -## Define the Handle Function - -Moving on, enter this on the next line: - -```perl -handle remainder => sub { -``` - -Once triggers are specified, we define how to *handle* the query. `handle` is another keyword, similar to **triggers**. - -You can *handle* different parts of the search query, but the most common is the **remainder**, which refers to the remainder of the query, after the first matched trigger word/phrase has been removed. - - - -For example, if the query was "**chars this is a test**", the trigger would be *chars* and the remainder would be *this is a test*. - -Now let's add a few more lines to complete the handle function: - -```perl -handle remainder => sub { - return 'Chars: ' . length $_ if $_; - return; -}; -``` - -This function (the part within the **{}**, after **sub**) is the most important part of the Goodie. It defines the Instant Answer that will be displayed at the top of the [search results page](https://duckduckgo.com/?q=chars+this+is+a+test). - -Whatever you are handling is passed to the function in the **$\_** variable ( **$\_** is a special default variable in Perl that is commonly used to store temporary values). For example, if you searched DuckDuckGo for *"chars this is a test"*, the value of **$\_** will be *"this is a test"*, i.e. the remainder. - -Let's take a closer look at the first line of the function: - -```perl -return 'Chars: ' . length $_ if $_; -``` - -The heart of the function is just this one line. The **remainder** is in the **$\_** variable as discussed. If it is not blank ( **if $\_** ), we return the number of chars using Perl's built-in [length function](https://duckduckgo.com/?q=perl+length). - -Perl has a lot of built-in functions, as well as thousands of modules available [via CPAN](https://metacpan.org/). You can leverage these modules when making Goodies, similar to how the [Roman Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Roman.pm) uses the [Roman module](https://metacpan.org/module/Roman). - -If we are unable to provide a good Instant Answer, we simply **return nothing**. That's exactly what the second line in the function does. - -```perl -return; -``` - -This line is only run if **$\_** contained nothing, because otherwise the line before it would return something and end the function execution. - - -## Return True at EOF - -Finally, all Perl packages that load correctly should [return a true value](http://stackoverflow.com/questions/5293246/why-the-1-at-the-end-of-each-perl-package) so add a 1 on the very last line, and make sure to also add a newline at the end of the file. - -```perl -1; - -``` - -And that's it! At this point you have a working Goodie Instant Answer. - -## Recap -The Instant Answer system works like this at the highest level: - -- We break the query (search terms) down into separate words, which is a process that happens in the background. - -- We see if any of those words or groups of words are **triggers** for any Instant Answers. These **triggers** are defined by the developer when creating an Instant Answer. In the example we used above, the trigger word is "**chars**". - -- If a Goodie is triggered, we run its `handle` function. - -- If the Goodie's handle function outputs an Instant Answer via a **return** statement, we display it at the top of the SERP (search engine results pages). diff --git a/duckduckhack/goodie/goodie_overview.md b/duckduckhack/goodie/goodie_overview.md deleted file mode 100644 index 20475d3cf..000000000 --- a/duckduckhack/goodie/goodie_overview.md +++ /dev/null @@ -1,37 +0,0 @@ -## Goodie Instant Answers - -**If you have questions, [request an invite to our Slack team](mailto:QuackSlack@duckduckgo.com?subject=AddMe) and we'll be happy to chat!** - -Goodies are pure-code Instant Answers. They are essentially a Perl function that is evaluated on our servers, and do not make external HTTP requests to other services. - -Goodies can be extremely simple, such as the [Uppercase](https://duckduckgo.com/?q=uppercase+duckduckgo+instant+answers) Goodie, which simply uses the perl `uc` function to uppercase a string. - -Goodies can also be extremely complex and powerful such as the [Calculator](https://duckduckgo.com/?q=%28879+*+14%29+%2F+12) or [Timezone Converter](https://duckduckgo.com/?q=4pm+EST+to+GMT) Goodies. Contributors have used Goodies to do additional cool things like [generate passwords](https://duckduckgo.com/?q=password+15+strong&ia=answer) and [create QR codes](https://duckduckgo.com/?q=qr+duckduckhack.com&ia=answer). - -### Goodie Quick Start - -If you're new to DuckDuckHack or Perl, writing a simple Goodie Instant Answer is probably the quickest and easiest way to get started with DuckDuckHack. The Goodie [Quick Start Tutorial](https://duck.co/duckduckhack/goodie_quickstart) was created to help you dive right in. - -The [Goodie Quick Start](https://duck.co/duckduckhack/goodie_quickstart) is a hands-on, simple way to understand the Goodie framework, going from zero to a working Instant Answer. It's the best preparation for contributing to DuckDuckHack. - -### Create a Goodie Cheat Sheet - -A popular use of Goodies is creating cheat sheet Instant Answers - for example, a cheat sheet for [vim](https://duckduckgo.com/?q=vim+cheat+sheet&ia=answer) or [tmux](https://duckduckgo.com/?q=tmux+cheat+sheet&ia=answer). We've made it exceptionally quick and easy to [contribute a Cheat Sheet](https://duck.co/duckduckhack/goodie_cheat_sheets) by adding a single file to the repository. - -![vim cheat sheet](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fvim_cheat_sheet.png&f=1) - -Goodie Cheat Sheets are the quickest way to make a live contribution, and a great place to get started. Learn more about [adding your Goodie Cheat Sheet](https://duck.co/duckduckhack/goodie_cheat_sheets). - -## Important Goodie Criteria - -- Goodies ***cannot* make HTTP requests**. If it's better served by an external API, it should probably be a [Spice Instant Answer](https://duck.co/duckduckhack/spice_overview). -- Goodies should return an answer in **less than 10ms**. Execution times between 10-50ms require review by the community. Goodies which run longer than 50ms will not be accepted. -- Goodies should use **less than 1MB** in memory. Memory usage greater than 1MB will require review by the community. - -Most Goodies shouldn't have a problem, but if you think your idea might run into these constraints, we're happy to help you think through it. Feel free to email us at [open@duckduckgo.com](mailto:open@duckduckgo.com). - -## Let Us Know What You're Working On - -**Before you start coding your new Instant Answer, let us know your plans.** By involving us early we can provide guidance and potentially save you a lot of time and effort. - -Email us at [open@duckduckgo.com](mailto:open@duckduckgo.com) with what idea you're working on and how you're thinking of going about it. diff --git a/duckduckhack/goodie/goodie_quickstart.md b/duckduckhack/goodie/goodie_quickstart.md deleted file mode 100644 index 24fd57402..000000000 --- a/duckduckhack/goodie/goodie_quickstart.md +++ /dev/null @@ -1,366 +0,0 @@ -# My First Goodie Instant Answer! - -**If you are new to DuckDuckHack, you're in the right place.** This Goodie Quick Start is meant to show you the basics of DuckDuckHack by building a simple Instant Answer that will look like [this](https://duckduckgo.com/?q=zekiel+duckduckhack&ia=answer): - -![IsAwesome Goodie](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fzekiel_isawesome.png&f=1) - -(Another easy way to contribute is by adding a [Cheat Sheet](https://duck.co/duckduckhack/goodie_cheat_sheets) to DuckDuckGo search results. This is a quick, direct way to contribute a live Instant Answer.) - -If you're already familiar with how Instant Answers are built, feel free to move along to the [Basic Tutorial](https://duck.co/duckduckhack/goodie_basic_tutorial). - -## Set Up Your Development Environment - -By this point you should have already **[set up your development environment](https://duck.co/duckduckhack/setup_dev_environment)** to be ready to code. If you need help with that (or anything else), please shoot us an email at: [open@duckduckgo.com](mailto:open@duckduckgo.com) and we'll jump on the chance to help! - -## Creating Your First Goodie - -In this tutorial we'll be making a super-simple Goodie by changing some template files. - -We'll be using the **DuckPAN tool** to generate this boilerplate code for us. The DuckPAN tool is an application we built to help DuckDuckHack developers save time. Aside from generating boilerplate code, you'll also use it to run and test your Instant Answer contribution. - -1. Click the "**DuckPAN New Goodie**" button on the command bar. The Terminal should prompt you to enter a name for your Instant Answer. -2. Type **`IsAwesome::GitHubUsername`** (replacing `GitHubUsername` with your actual GitHub username), then press "**Enter**". From this point on, whenever you see, ***GitHubUsername***, replace it with your actual GitHub username. - - ``` - [04:31 PM codio@buffalo-pixel zeroclickinfo-goodies {master}]$ cd ~/workspace/zeroclickinfo-goodies/ && duckpan new - Please enter a name for your Instant Answer: IsAwesome::GitHubUsername - ``` - - *Usually you'll be typing in just a regular name like `Calculator` with no prefix, but because so many community members are creating `IsAwesome` projects, we wanted to keep them together in their own directory. Hence the `IsAwesome::` prefix.* - -3. DuckPAN should print some text, confirming that your first Goodie was created: - - ``` - [04:31 PM codio@buffalo-pixel zeroclickinfo-goodies {master}]$ cd ~/workspace/zeroclickinfo-goodies/ && duckpan new duckpan new - Please enter a name for your Instant Answer : IsAwesome::GitHubUsername - - Created file: ./lib/DDG/Goodie/IsAwesome/GitHubUsername.pm - - - Created file: ./t/IsAwesome/GitHubUsername.t - - - Successfully created Goodie: IsAwesome::GitHubUsername - ``` - - DuckPAN created two files: a code file, and a test file, each in their proper locations in the repository. That's all it takes to make a Goodie. - - Currently, these two generated files only contain boilerplate code and comments, which saves us a lot of time. Next, we'll customize the files to our liking and get your Goodie working. - -4. Let's start by editing your code file first. Press "**Ctrl+O**" (Cmd+O on a Mac), then type "**GitHubUsername.pm**" and press "**Enter**". This will open the file for editing in Codio's text editor. (Vim, Emacs and Nano are also available). - - It should look like this: - - ```perl - package DDG::Goodie::IsAwesome::GitHubUsername; - # ABSTRACT: Write an abstract here - # Start at https://duck.co/duckduckhack/goodie_overview if you are new - # to Instant Answer development - - use DDG::Goodie; - use strict; - - zci answer_type => "is_awesome_git_hub_username"; - zci is_cached => 1; - - # Metadata. See https://duck.co/duckduckhack/metadata for help in filling out this section. - name "IsAwesome GitHubUsername"; - description "Succinct explanation of what this Instant Answer does"; - primary_example_queries "first example query", "second example query"; - secondary_example_queries "optional -- demonstrate any additional triggers"; - # Uncomment and complete: https://duck.co/duckduckhack/metadata#category - # category ""; - # Uncomment and complete: https://duck.co/duckduckhack/metadata#topics - # topics ""; - code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/GitHubUsername.pm"; - attribution github => ["GitHubAccount", "Friendly Name"], - twitter => "twitterhandle"; - - # Triggers - triggers any => "triggerWord", "trigger phrase"; - - # Handle statement - handle remainder => sub { - - # optional - regex guard - # return unless qr/^\w+/; - - return unless $_; # Guard against "no answer" - - return $_; - }; - - 1; - ``` - -5. Change the **`trigger`** (line 26) to this: - - ```perl - triggers start => "duckduckhack githubusername"; - ``` - - This tells the Instant Answer system to trigger your Goodie when the query **starts** with the phrase "**duckduckhack githubusername**". - - **Note:** Triggers must be entered in **lowercase**. If your username has uppercase letters, **don't worry**, a lowercased trigger will always work because we compare the *lowercased query* against the trigger. - -6. Change the **`handle`** statement (lines 29-37) to this: - - ```perl - handle remainder => sub { - - return "GitHubUsername is awesome and has successfully completed the DuckDuckHack Goodie tutorial!"; - }; - ``` - - This will make your Goodie tell everyone that you're totally awesome! - - **Note:** Do ***not*** remove the `1;` from the end of the file. This is required because Perl modules must return a true value. - -7. Switch back to your Terminal by clicking on the "**Terminal**" tab. -8. In the `zeroclickinfo-goodies` directory type **`duckpan server`** and press "**Enter**". The Terminal should print some text and let you know that the server is listening on port 5000. - - ``` - Starting up webserver... - - You can stop the webserver with Ctrl-C - - HTTP::Server::PSGI: Accepting connections at http://0:5000/ - ``` - - **Note:** If you modify **GitHubUsername.pm** (or any other Perl files) while DuckPAN Server is *running*, you **must restart the server** for the changes to take effect. In the Terminal, press "**Ctrl+C**" to shut down the DuckPAN Server. Type **`duckpan server`** and press "**Enter**" to start it again. - -9. Click the "**DuckPAN Server**" button at the top of the screen. A new browser tab should open and you should see the DuckDuckGo Homepage. Type "**duckduckhack GitHubUsername**" and press "**Enter**". -10. You should see a DuckDuckGo Instant Answer displaying the text, "**GitHubUsername is awesome and has successfully completed the DuckDuckHack Goodie tutorial!**". This is the text we told your Goodie to **`return`**! -11. In the Terminal, press "**Ctrl+C**" to shut down the DuckPAN Server. The Terminal should return to a regular command prompt. - - ``` - Starting up webserver... - - You can stop the webserver with Ctrl-C - - HTTP::Server::PSGI: Accepting connections at http://0:5000/ - ^C - [04:32 PM codio@buffalo-pixel zeroclickinfo-goodies {master}]$ - ``` - -**Congrats!** Your first Goodie is working! You specified its **`trigger`** and told it what to **`return`**. You're almost done! Now you should write a test to make sure your Goodie only triggers when we want it to. - -## Writing Your First Goodie Test File - -1. Let's edit your corresponding test file. Press "**Ctrl+O**" (Cmd+O on a Mac), then type "**GitHubUsername.t**" and press "**Enter**". This will open the file for editing in Codio's text editor. - - It should look like this: - - ```perl - #!/usr/bin/env perl - - use strict; - use warnings; - use Test::More; - use DDG::Test::Goodie; - - zci answer_type => "is_awesome_git_hub_username"; - zci is_cached => 1; - - ddg_goodie_test( - [qw( DDG::Goodie::IsAwesome::GitHubUsername )], - # At a minimum, be sure to include tests for all: - # - primary_example_queries - # - secondary_example_queries - 'example query' => test_zci('query'), - # Try to include some examples of queries on which it might - # appear that your answer will trigger, but does not. - 'bad example query' => undef, - ); - - done_testing; - ``` - - _Make sure to change **all** instances of `GitHubUsername` in the file to your user name._ - -2. Change the **`ddg_goodie_test`** function (lines 11-20) to this: - - ```perl - ddg_goodie_test( - [qw( - DDG::Goodie::IsAwesome::GitHubUsername - )], - 'duckduckhack GitHubUsername' => test_zci('GitHubUsername is awesome and has successfully completed the DuckDuckHack Goodie tutorial!'), - 'duckduckhack GitHubUsername is awesome' => undef, - ); - ``` - - This test file now ensure two things: - - 1. For the query "**duckduckhack GitHubUsername**", your Goodie returns "GitHubUsername is awesome and has successfully completed the DuckDuckHack Goodie tutorial!". - 2. For the query "**duckduckhack GitHubUsername is awesome**", your Goodie returns **`undef`**, meaning it won't display any result for that query. - - *Can you guess what the `test_zci()` function does? ZCI stands for Zero Click Info. The `test_zci()` function verifies what is being returned specifically in the Zero Click Info section of the search page.* - - -3. Switch back to your Terminal by clicking on the "**Terminal**" tab. -4. Type **`prove -Ilib t/IsAwesome/GitHubUsername.t`** and press "**Enter**". The prompt should print the results of the test. - - ``` - [04:33 PM codio@buffalo-pixel zeroclickinfo-goodies {master}]$ prove -Ilib t/IsAwesome/GitHubUsername.t - t/IsAwesome/GitHubUsername.t .. 1/? - # Failed test 'Checking for not matching on duckduckhack GitHubUsername is awesome' - # at /home/codio/perl5/perls/perl-5.18.2/lib/site_perl/5.18.2/DDG/Test/Block.pm line 62. - # got: 'DDG::ZeroClickInfo=HASH(0x2e5b628)' - # expected: undef - # Looks like you failed 1 test of 2. - t/IsAwesome/GitHubUsername.t .. Dubious, test returned 1 (wstat 256, 0x100) - Failed 1/2 subtests - - Test Summary Report - ------------------- - t/IsAwesome/GitHubUsername.t (Wstat: 256 Tests: 2 Failed: 1) - Failed test: 2 - Non-zero exit status: 1 - Files=1, Tests=2, 0 wallclock secs ( 0.02 usr 0.02 sys + 0.14 cusr 0.04 csys = 0.22 CPU) - Result: FAIL - ``` - - Uh oh! We failed a test! The output says a `DDG::ZeroClickInfo` object was returned, but the test was expecting `undef`. Let's update our code to make this test pass. - -4. Switch back to your text editor by clicking the tab if it's still open, or open the "**GitHubUsername.pm**" file with "**Ctrl+O**" (Cmd+O on a Mac). -5. Update the **`handle`** statement to this: - - ```perl - handle remainder => sub { - return if $_; - return "GitHubUsername is awesome and has successfully completed the DuckDuckHack Goodie tutorial!"; - }; - ``` - - Now your Goodie will return nothing (i.e. `undef`) if `$_` has a value. Within our `handle` function, `$_` is a special variable that takes on the value of **`remainder`**. The `remainder` refers to the rest of the query after removing our matched **`trigger`**. So for the query "duckduckhack GitHubUsername is awesome", the value of `$_` will be: - - ``` - "duckduckhack GitHubUsername is awesome" - "duckduckhack GitHubUsername" = `is awesome` - ``` - -6. Switch back to your Terminal by clicking the "**Terminal**" tab. -7. Type **`prove -Ilib t/IsAwesome/GitHubUsername.t`** and press "**Enter**". The prompt should print the results of the test again. - - ``` - [04:34 PM codio@buffalo-pixel zeroclickinfo-goodies {master}]$ prove -Ilib t/IsAwesome/GitHubUsername.t - t/IsAwesome/GitHubUsername.t .. ok - All tests successful. - Files=1, Tests=2, 0 wallclock secs ( 0.02 usr 0.01 sys + 0.15 cusr 0.03 csys = 0.21 CPU) - Result: PASS - ``` - - **Success!** The test passes, meaning that your Goodie will only `return` an answer when our query `start`s with the `trigger` "**duckduckhack GitHubUsername**" and has no `remainder` after that. - - _Still not passing? Make sure you changed **all** instances of `GitHubUsername` in the file to your user name._ - -You've written and tested your first Goodie! Feels great, doesn't it? - -**While we can no longer accept 'IsAwesome' submissions as pull requests, you're ready to contribute bigger things:** - -- Tackle the [Basic Tutorial](https://duck.co/duckduckhack/goodie_basic_tutorial). You will find many things familiar there and be well on your way to advanced Goodie functionality. -- Work on an approved idea on [the DuckDuckGo Community Portal](https://duck.co/ideas/status/3?table_lnKRpLENwO2NUmZUyukQpw_sort=votes) -- Create a [cheat sheet](https://duck.co/duckduckhack/goodie_cheat_sheets) (peruse the [cheat sheet ideas](https://duck.co/duckduckhack/goodie_cheat_sheets#cheat-sheet-ideas)) -- Own an issue from [Goodie Low Hanging Fruit](https://github.com/duckduckgo/zeroclickinfo-goodies/issues?q=is%3Aopen+is%3Aissue+label%3A%22Low-Hanging+Fruit%22) - -**As always, feel free to email us with any questions - any time - at [open@duckduckgo.com](mailto:open@duckduckgo.com). Happy hacking!** - - diff --git a/duckduckhack/goodie/goodie_triggers.md b/duckduckhack/goodie/goodie_triggers.md deleted file mode 100644 index 65051e62c..000000000 --- a/duckduckhack/goodie/goodie_triggers.md +++ /dev/null @@ -1,92 +0,0 @@ -# Triggers - -There are two types of triggers, **words** and **regex**. We insist that you use word triggers whenever possible as they are simpler and faster. - -## Word Triggers - -### Usage - -```perl -triggers => -``` - - - -#### Examples - -```perl -triggers start => "trigger my instant answer", "trigger myIA", "myIA"; -``` - -or - -```perl -@triggers = qw(these are separate triggers for my instant answer); -triggers any => @triggers; -``` - -or - -```perl -triggers start => "starting phrase of query"; -triggers end => "ending phrase of query"; -``` - -### Trigger Locations - -- `start` — Word exists at the start of the query -- `end` — Word exists at the end of the query -- `startend` — Word is at the beginning or end of the query -- `any` — Word is anywhere in the query - -**Note:** You can combine several trigger statements if, for example, you want certain words or phrases to be **startend** but others to be **start**. The [Average Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Average.pm#L5) demonstrates the usage of multiple Word trigger statements. - -## Regex Triggers - -### Usage - -```perl -triggers => -``` - - - -#### Examples - -```perl -triggers query_lc => qr/trigger (?:my|your|our) instant answer/; -``` - -or - -```perl -my $regex = qr/^this is an? instant answer regexp$/; -triggers query_raw => $regex; -``` - -#### Query Formats - -- `query_raw` — The query in its original form, with whitespace and case preserved -- `query` — Uniformly whitespaced version of `query_raw` -- `query_lc` — Lowercase version of `query` -- `query_nowhitespace` — `query` with all whitespace removed -- `query_clean` — `query_lc`, but with whitespace and non-alphanumeric ascii removed - -**Note:** You **cannot** combine the use of **Regex Triggers** with **Word Triggers**. - -## Regex Guards - -We much prefer you use **trigger words** when possible because they are faster on the backend. In some cases however, **regular expressions** are necessary, e.g., you need to trigger on sub-words. In this case we suggest you consider using a **word trigger** and supplement it with a **regex guard**. A regex guard is a return clause immediately inside the `handle` function. - -A good example of this is the Base64 goodie. In this case we want to trigger on queries with the form "base64 encode/decode \". Here's an excerpt from [Base64.pm](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Base64.pm) which shows how this case is handled using a word trigger, with a regex guard: - -```perl -triggers startend => "base64"; - -handle remainder => sub { - return unless $_ =~ /^(encode|decode|)\s*(.*)$/i; -``` - -## Triggers in Multiple Languages - -We have plans to make it possible to trigger Instant Answers in many different languages. Until an internationalization mechanism is place, to uphold maintainability and consistency, **we cannot accept pull requests that add languages directly in the code.** \ No newline at end of file diff --git a/duckduckhack/longtail/longtail_basic_tutorial.md b/duckduckhack/longtail/longtail_basic_tutorial.md deleted file mode 100644 index 1926404ba..000000000 --- a/duckduckhack/longtail/longtail_basic_tutorial.md +++ /dev/null @@ -1,3 +0,0 @@ -# Longtail Basic Tutorial - -(This section is coming soon! Know what should go here? Then **please** [contribute to the documentation](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/CONTRIBUTING.md)!) \ No newline at end of file diff --git a/duckduckhack/longtail/longtail_overview.md b/duckduckhack/longtail/longtail_overview.md deleted file mode 100644 index 9284c4aca..000000000 --- a/duckduckhack/longtail/longtail_overview.md +++ /dev/null @@ -1,41 +0,0 @@ -# Longtail Instant Answers - -**If you have questions, [request an invite to our Slack team](mailto:QuackSlack@duckduckgo.com?subject=AddMe) and we'll be happy to chat!** - -Longtails are database-backed, full text search, Instant Answers. For every query DuckDuckGo receives, each Longtail's database of articles is searched and any matching articles are used to display a paragraph of text, highlighting the portion of the article which matches the query. Developing a Longtail Instant Answer entails writing a program that generates an XML data file. This XML file describes each article, as well as some other important information discussed below. The program may be written in Perl, Python, JavaScript, or Ruby, and if necessary, will be run periodically to keep the database current. - -## Structure - -Longtails consist of two primary files. The first is a metadata file that describes the Instant Answer you are building. Its structure is identical to the Fathead metadata file, described [here](https://github.com/duckduckgo/zeroclickinfo-fathead#meta-file). The second, which can be generated using a language of your choosing, contains the data set in a format ready for us to deploy: - -```XML - - - - - - - - - - - - - - - - - - - - - -1 - - - - - -``` - -(This section is still growing! Know what should go here? Then **please** [contribute to the documentation](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/CONTRIBUTING.md)!) diff --git a/duckduckhack/resources/code_styleguide.md b/duckduckhack/resources/code_styleguide.md deleted file mode 100644 index bfb7b4586..000000000 --- a/duckduckhack/resources/code_styleguide.md +++ /dev/null @@ -1,272 +0,0 @@ -# Code Style Guide - -As a largely open-source project, we like to keep our code as consistent as our Instant Answers. This means all code should be formatted the same way and should look and feel as though it has all been written by the same person. - -This document outlines some language specific guidelines for formatting your code and also highlights best practices, and things to avoid. In any large open-source project, maintainability is of utmost importance, so a style guide is necessary to help our contributors write clean, readable and maintainable code. - -## General - -- **Indent with 4 spaces** (soft tabs) - - All DuckDuckHack code should be indented with four spaces. Be sure to configure your text editor to insert four spaces when you press the tab button - this is referred to as a "soft-tab". If you are correcting the indentation of a file, please submit that change in a separate pull request. It is important that code reviewers are able to easily differentiate between functional and stylistic changes. - -- **Document your code** - - Well-documented code helps others understand what you've written. It's likely that someone else will read your code and might even need to change it at some point in the future. Comments should primarily document the intent of the code. Reviewers are much more effective when they know exactly what you were trying to do. Meaningful variable names also help to document your intent. - -- **Writing meaningful commits** - - Commit messages should be concise and informative. If the specific commit fixes a bug on GitHub, note that by saying `fixes #123`, where `123` is the issue number. Doing this will automatically close the specified issue when your pull request is merged. - - Usually pull requests only deal with a single Instant Answer. If however your pull request modifies more than one Instant Answer, please preface your commit messages with the name of the IA modified by your commit: - - For example, if your pull request updates the Movies, InTheaters and Kwixer IA's: - - - Commit 1: `Movies: updated title font color to match mockup`. - - Commit 2: `InTheaters: updated title text, typo fix`. - - Commit 3: `Movies, InTheaters, Kwixer: change title to h5 tag`. - -## JavaScript - -**We generally adhere to [Crockford's Code Conventions](http://javascript.crockford.com/code.html)**. Most importantly: - -- Use semicolons; - -- Use the ["One True Brace Style"](https://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS) (opening brace on the same line) - - ```javascript - // Bad - if ( ... ) - { - ... - } - else - { - ... - } - - // Good - if ( ... ) { - ... - } else { - ... - } - ``` - -- Use `{}` instead of `new Object()`, and `[]` instead of `new Array()`. - - ```javascript - // Bad - var obj = new Object(); - var arr = new Array(); - - // Good - var obj = {}; - var arr = []; - ``` - -- Use `===` instead of `==`, and `!==` instead of `!=`. [Why?](http://stackoverflow.com/a/359509/1998450) - - ```javascript - // Bad - if (foo == bar) { ... } - if (foo != bar) { ... } - - // Good - if (foo === bar) { ... } - if (foo !== bar) { ... } - ``` - -- Declare variables with var, chaining multiple declarations -- one per line: - - ```javascript - - // Bad - var foo = 1; - var bar = true; - var baz = "string"; - - // good - var foo = 1, - bar = true, - baz = "string"; - - // when initializing undefined variables - var foo, bar, baz; - ``` - - Note: We're using ECMAScript's [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FFunctions_and_function_scope%2FStrict_mode), so you'll *need* to declare every variable with `var`. - -- Avoid trailing commas - - We support all modern browsers, including IE 9, which breaks when it reaches a trailing comma in objects. It also treats trailing commas in arrays [differently than you might expect](http://www.akawebdesign.com/2011/06/23/the-curious-case-of-trailing-commas-in-ie/). - - ```javascript - // Bad - var foo = { - a: 'b', - c: 42, //<-- trailing comma - }; - - // Good - var foo = { - a: 'b', - c: 42 //<-- no trailing comma - }; - ``` - -- Use [`$.map()`](http://api.jquery.com/jQuery.map/) and [`$.each()`](http://api.jquery.com/jQuery.each/) instead of `Array.prototype.map()` and `Array.prototype.forEach()`, again for IE support. - -- Avoid modifying object prototypes - - Do not modify the prototypes of objects which are defined outside of your code. For example, modifications to `Array.prototype` or `Spice` will affect the global scope and may cause problems. In general, we advocate the use of local, private functions instead. - -- Define default properties when the object is created: - - ```javascript - // Bad - var bar = {}; - bar.a = 'b'; - bar.c = 42; - - // Good - var foo = { - a: 'b', - c: 42 - }; - ``` - -- Store jQuery selectors: - - If you need to re-use a jQuery selector (e.g. `$('#myDiv')`), store it in a variable for speed and efficiency. Otherwise, jQuery will need to traverse the DOM each time you use the same selector. - - ```javascript - // Bad - // Traverse the DOM and find '#text_element'... - $('#text_element').show(); - // ... now do all that work again! - $('#text_element').html('abc'); - - // Good - // Traverse the DOM and find '#text_element', then store it in memory - // Convention is to prefix variables with a '$' when they hold a jQuery object - var $text_element = $('#text_element'); - $text_element.show(); - $text_element.html('abc'); - - // Better - // jQuery supports method chaining! - $('#text_element').show().html('abc'); - - ``` - -## Handlebars - -Handlebars templates and Handlebars helpers should be easy to read and understand. Please: - -- Put nested elements on new lines: - - ```html - - - - - - ``` - -- Define helper functions with `Spice.registerHelper`, instead of `Handlebars.registerHelper`: - - ```javascript - // Bad - Handlebars.registerHelper("spice_name_do_something", function(){ ... }); - - // Good - Spice.registerHelper("spice_name_do_something", function(){ ... }); - ``` - -- Namespace your helper functions: - - Handlebars helpers are all created in the same scope, so any two helpers with the same name will collide (we plan to fix this). This can be avoided by prepending your helpers with the name of your Spice IA. - - ```javascript - // Bad - Spice.registerHelper("do_something", function(){ ... }); - - // Good - Spice.registerHelper("spice_name_do_something", function(){ ... }); - ``` - ------- - -The easiest way to verify your code meets our style guide is to test it with [JSHint](http://jshint.com/). - -## CSS - -- All CSS should be "namespaced" with the container element. - - For Spices, use `.zci--spicename`, and for Goodies, use `.zci--answer`: - - ```css - /* Stopwatch Spice */ - .zci--stopwatch .spice-pane { - ... - } - - /* Calendar Goodie */ - .zci--answer table.calendar { - ... - } - ``` - -- Put multiple selectors on new lines for each rule: - - ```css - /* Bad */ - .zci--stopwatch .spice-pane, .zci--stopwatch .spice-pane-right { - ... - } - - /* Good */ - .zci--stopwatch .spice-pane, - .zci--stopwatch .spice-pane-right { - ... - } - ``` - -- Avoid the use of vendor prefixes and experimental features. We strive for a uniform experience on all current browsers and your IA *must* work across them. - - When in doubt, [CanIUse](http://caniuse.com/) is a good resource for determining if prefixes are needed. We support IE 9+, and recent versions of Chrome, Firefox, Safari, and Opera. - - ```css - /* Bad */ - .element { - -webkit-border-radius: 45px; - -o-border-radius: 45px; - -moz-border-radius: 45px; - -khtml-border-radius: 45px; - border-radius: 45px; - } - - /* Good */ - .element { - border-radius: 45px; - } - ``` - -- Avoid using inline CSS. Custom CSS should be placed in a file (`/share/{goodie,spice}/my_ia/my_ia.css`) to be automatically included in the Instant Answer response. - -## Perl - -(This section is coming soon! Know what should go here? Then **please** [contribute to the documentation](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/CONTRIBUTING.md)!) diff --git a/duckduckhack/resources/common_pitfalls.md b/duckduckhack/resources/common_pitfalls.md deleted file mode 100644 index 7191cdb8c..000000000 --- a/duckduckhack/resources/common_pitfalls.md +++ /dev/null @@ -1,7 +0,0 @@ -# Common Pitfalls - -(This section is coming soon! Know what should go here? Then **please** [contribute to the documentation](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/CONTRIBUTING.md)!) - -### Defining Perl Variables and Functions - -- inside vs outside the `handle` function diff --git a/duckduckhack/resources/faq.md b/duckduckhack/resources/faq.md deleted file mode 100644 index 3b90c08cb..000000000 --- a/duckduckhack/resources/faq.md +++ /dev/null @@ -1,170 +0,0 @@ -# DuckDuckHack FAQ - -## General FAQ - -### Why should I make Instant Answers? - -We hope you will consider making DuckDuckGo Instant Answers to: - -- Improve results in areas you personally search and care about, e.g., [programming documentation](https://duckduckgo.com/?q=perl+split), [gaming](https://duckduckgo.com/?q=roll+3d12+%2B+4) or [entertainment](https://duckduckgo.com/?q=xkcd). -- Increase usage of your own projects, e.g., data and [APIs](https://duckduckgo.com/?q=cost+of+living+nyc+philadelphia). -- Attribution [on our site](https://duckduckgo.com/goodies.html) and [Twitter](https://twitter.com/duckduckhack) (working on more). -- See your code live on a [growing](https://duckduckgo.com/traffic.html) search engine! -- Learn something new. - -### What if I'm not a coder at all? - -If you don't code at all, please check out our [Instant Answers Ideas Forum](https://duck.co/ideas) where you can suggest and comment on Instant Answer ideas. For instance, identifying the best sources to draw from is extremely important but developers may not know what they are. Similarly, you can submit [issues about current Instant Answers](https://github.com/duckduckgo/duckduckgo/issues?direction=desc&sort=created&state=open). Both of these activities are very valuable and will help direct community efforts. - -If you're a business and want your data to be utilized, adding your service to the [Instant Answers Ideas Forum](https://duck.co/ideas) is a great way for your API to get picked up by a developer and integrated into the search engine. - -### Can you help me? - -Of course! Here are the easiest ways to contact someone who can help answer your questions: - -- Write us publicly on the [discussion list](https://www.listbox.com/subscribe/?list_id=197814). -- Write us privately at open@duckduckgo.com. - -### What if I don't know Perl? - -If you don't know Perl, that's OK! Some Instant Answer types ([Fathead](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/fathead/fathead_overview.md), [Longtail](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/longtail/longtail_overview.md)) don't require the use of Perl. Also, if you know PHP, Ruby, or Python you should be able to write a [Goodie](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/duckduckhack/goodie/goodie_overview.md) in Perl pretty easily using [this awesome cheat sheet](http://hyperpolyglot.org/scripting). - -### Do you have any Instant Answer ideas? - -Yup! We maintain [a growing list](https://duck.co/ideas). There are also improvement ideas for [Goodies](https://github.com/duckduckgo/zeroclickinfo-goodies/issues), [Spice](https://github.com/duckduckgo/zeroclickinfo-spice/issues), [Fathead](https://github.com/duckduckgo/zeroclickinfo-fathead/issues) and [Longtail](https://github.com/duckduckgo/zeroclickinfo-longtail/issues). - -### How do I note that I've started on something? - -In your initial pull request, please note the link on the [Ideas Forum](https://duck.co/ideas). We'll move it to the "in process" bucket for you. - -### Where I can report Instant Answer bugs? - -Submit a GitHub issue in the [appropriate repository](http://github.com/duckduckgo). - -### What if there are Instant Answer conflicts? - -Instant answer sources often compete to answer the same searches. In a lot of cases, the experience can be blended together so that the user is shown answers from more than one source. Our long-term vision for Instant Answers involves multiple sources used in that way. - -There are times, though, where one source does a drastically better job of answering a particular query set. In those cases, the source used for those queries should be the source most capable of delivering the best possible user experience. Our community evaluates those in a few ways: - -- Consistent performance (is the service reliable?) -- Speed (does the service return results fast enough?) -- Quality (does the service answer the queries better than any other service?) - -If you think you have a source that is better, let's talk about it on the [DuckDuckHack e-mail list](https://www.listbox.com/subscribe/?list_id=197814). - -### Why isn't my Instant Answer in the [DuckDuckGo Instant Answers API](https://api.duckduckgo.com)? - -If your Instant Answer is spice or longtail, sometimes we can't expose it through the API for licensing reasons, but our over-arching goal is to make all of our Instant Answers available on their own. - -### Can I add triggers for my language? - -We have plans to make it possible to trigger Instant Answers in many different languages. Until an internationalization mechanism is place, to uphold maintainability and consistency, **we cannot accept pull requests that add languages directly in the code.** - -### Can I do something more complicated? - -Maybe. There are a bunch more internal interfaces we haven't exposed yet, and we'd love to hear your ideas to influence that roadmap. - -### Can I create adult Instant Answers (i.e. NSFW)? - -No. - -### What's the roadmap? - -Here's what we're working on (in roughly in this order): - -- better testing/file structure for spice Instant Answers. -- better JS interface for spice Instant Answer callback functions. -- better attribution. -- embedding Instant Answers. -- better testing/file structure for fathead Instant Answers. -- more defined structure for longtail Instant Answers. -- better testing for longtail Instant Answers. - -### Are there other open source projects? - -Yes! Check out the other repositories in [our GitHub account](https://github.com/duckduckgo). You can email open@duckduckgo.com if you have any questions on those. - -### Can I get the Instant Answers through an API? - -Yes! Check out the [DuckDuckGo API](https://api.duckduckgo.com). Our goal is to make as many Instant Answers as possible -available through this interface. Fathead and goodie Instant Answers are automatically syndicated through the API, and Spice and Longtail are selectively (due to licensing complications) mixed in. - -### Can I talk to you about a partnership idea? - -Sure -- check out [our partnerships page](https://duck.co/help/company/partnerships). - -## Goodie FAQ - -### Can Goodie Instant Answers make network requests? - -No. If you are trying to use an API, you should consider creating a Spice Instant Answer instead. - -### Can Goodie Instant Answers include the user's query string? - -Yes. **However**, they must be handled *very* carefully. User-supplied strings create a lot of potential for [cross-site scripting attacks](https://duckduckgo.com/Cross-site_scripting?ia=about). While the platform attempts to mitigate these issues in pure ASCII responses, HTML responses should **never** include a raw query string. It is safest to return only data which is generated by your Goodie itself. - -## Spice FAQ - -### I want to use 'X' API, but it doesn't have an endpoint for 'Y'. What should I do? - -Email them! - If you explain what it's for, they might be willing to create and endpoint for you! If not, it's probably best to find another API. - -### The API provides both HTTP and HTTPS endpoints; which should I use? - -We prefer to use **HTTP endpoints** because the reduced connection setup time allows us to provide faster answers to the user. Note that the end-user's query will still be secure in transit, because it is proxied ( e.g. https://duckduckgo.com/js/spice/movie/mib ) through an HTTPS connection to the DuckDuckGo servers. - -### Can I use an API that returns XML? - -Sorry, but **not right now**. XML support is coming soon. -**Note:** If an API supports both JSON and XML, we *strongly encourage* you to use **JSON**. - -### Can I use an API that returns HTML or a String? - -Sorry, but **no**. We currently don't support HTML or plain text API's. - -### Can I use the 'X', 'Y' or 'Z' JavaScript library? - -Probably not. Maybe, if it is very small, but we prefer that no third party, extra libraries are used. ***Please*** ask us first before writing an Instant Answer that is **dependent** on an extra library - we don't want you to waste your time and energy on something we can't accept! - -### Can I use Coffeescript? - -No. - -### What about... - -Nope. Just use JavaScript, please and thanks :) - -## Fathead FAQ - -### How can I test my output file? - -Unfortunately, there is no way for contributors to do so. But if you've gotten that far, we want to hear about it! Please open a pull request and we'll help you through the testing process. - -### What can go in a result abstract? - -A result abstract can be either plain text (generally one readable sentence, ending in a period), or HTML. Special care needs to be taken when abstracts contain HTML. Please let us know ahead of time if you are planning to use HTML. - -(This section is still growing! Know what should go here? Then **please** [contribute to the documentation](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/CONTRIBUTING.md)!) - -## Longtail FAQ - -(This section is coming soon! Know what should go here? Then **please** [contribute to the documentation](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/CONTRIBUTING.md)!) - -## DuckPAN FAQ - -### How do I install a missing Perl dependency? - -Any Perl module (.pm file) that has external Perl dependencies will load them with a `use` statement. Typically these statements are located near the top of the file. For example, the Factors Goodie (`lib/DDG/Goodie/Factors.pm`) loads the module `Math::Prime::Util`. If this is not installed on your system, DuckPAN will not be able to use the Factors Goodie and you will likely see an error or warning. - -In order to install any missing dependencies you can use cpan or cpanm like so: - -```perl -cpan install Math::Prime::Util -# or -cpanm Math::Prime::Util -``` - -Alternatively, if you would like to install all the dependencies for the repo (e.g. zeroclickinfo-goodies), you can run `duckpan installdeps`. Please note that installing all the dependencies will take **several minutes** to install as there are many dependencies. - -![dependency](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckpan%2Fassets%2Fdependency.png&f=1) diff --git a/duckduckhack/resources/how_to_qa.md b/duckduckhack/resources/how_to_qa.md deleted file mode 100644 index ef93138e7..000000000 --- a/duckduckhack/resources/how_to_qa.md +++ /dev/null @@ -1,130 +0,0 @@ -# How to QA an Instant Answer - -Our goal is to have a great Instant Answer for every search. When a developer submits a new Instant Answer, it must first be thoroughly reviewed by our community and internal staff before going live on DuckDuckGo. -Please use the following guide when QA-ing Instant Answers: - -## Everyone (Reviewing Quality): - -### What's an Instant Answer made of? - -1. Some code -2. A data source (like a website) - -### How do they work? - -Instant Answers are a bundle of code that make information from one source available directly on DuckDuckGo. Anyone can suggest them and anyone can create them! -Check out some examples of what we mean: - -- [Definition search:](https://duckduckgo.com/?q=define+hello&ia=definition) - - [Code](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/share/spice/dictionary/definition/dictionary_definition.js) - - [Data Source](https://www.wordnik.com/) - -- [Weather search:](https://duckduckgo.com/?q=weather+in+chicago&ia=weather) - - [Code](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/Forecast.pm) - - [Data Source](https://developer.forecast.io/) - -### What we're looking for in QA - -**Function:** Is the Instant Answer actually useful? -Instant Answers should *always* be unambiguously **better** than organic links. If it doesn't add value to the page, then it should not be approved. - -- **Example test:** Search for something that will trigger the Instant Answer and compare it to the organic links. The user should benefit from having the Instant Answer available. In the best case scenario, the Instant Answer should give the user all the information they need, so they don't even have to click a link. At the very least, the Instant Answer should offer some different and more valuable information than the links. - - -**Relevancy:** Does the Instant Answer always provide relevant information? -Instant Answers should only show information that is correct and relevant to the user's search. If an Instant Answer is capable of returning irrelevant information (e.g., "free gaming apps" should *only* show free apps, "the dark knight movie" should ensure both words, "dark" and "knight" are in the resulting movie's title), then the relevancy must be improved before the Instant Answer is accepted and goes live. - -- **Example test:** Search for something that will trigger the Instant Answer and compare the information provided to the original source's website (if one exists) or another credible source. For example, if the Instant Answer performs arithmetic operations, you could verify that its calculations are correct using your calculator at home. If it provides movie information, you could verify that it's correct using Wikipedia or IMDb. - - -**Triggering:** Does the Instant Answer trigger when it shouldn't? Are there any queries that *should* trigger the Instant Answer, but don't? -Goodie and Spice Instant Answers use a list of, "trigger words" (or phrases) that signal DuckDuckGo to use that Instant Answer when those triggers appear in a search query. If they are too generic, it will cause the Instant Answer to be shown in cases which are inappropriate. For example, "app" is a very generic word which occurs in many queries that aren't necessarily an app search. Instant Answers that use generic triggers should always further qualify the query to make sure it should return an answer. On the other hand, if triggers are too specific, it can lead users to believe that an Instant Answer doesn't exist for that topic or query space, reducing the value in searching with DuckDuckGo. - -- **Example test:** Can you think of any queries that should or shouldn't be triggering this Instant Answer? Let's say we have an Instant Answer that shows movies, and it triggers with the term, "movie". The query, "movie Thor" would trigger this Instant Answer, but other queries, such as, "watch Thor", "Thor movies", "film Thor" or "Thor film" should also trigger this Instant Answer. - - -**Adult Content:** -Is the Instant Answer effectively preventing adult words or inappropriate content from showing? There shouldn't be any adult imagery or profanity in Instant Answers, by default. If an Instant Answer is capable of displaying profanity or questionable adult humor, it should not be approved (if it's vulgar or distasteful), or it can be set to only show when safe search is off. If ever in doubt, please ask community leaders or DDG staff for help. - -- **Example test:** Check if the Instant Answer is capable of producing profanity or adult imagery by searching for relevant (profane) keywords or risqué content. If so, the Instant Answer should block all instances of adult language and adult imagery. If not, you've found a bug! - - -**Design:** -Can we minimize the space used? How does it look on smaller screens? Can you break the design? For example, try using non-UTF8 characters or long search queries. Do the design and layout make sense given the type of information? Does it look and feel like other Instant Answers? (It should!) Spotting design bugs and improvements can be tricky, since everyone's eye for design is a bit different, but you can refer to live Instant Answers as an example. - -- **Example test:** Test the Instant Answer with a few different queries. Make sure the most important information is easy to identify and understand. The information shouldn't be too crowded or too sparse. Try previewing the Instant Answer on a mobile device (phone, tablet) and check if the design breaks or if too much vertical space is used. Ideally, an Instant Answer on mobile screens should ***not*** push organic links off the page. If this is the case, look for ways to either increase the information density or reduce the information shown — this is a great way to determine what information is absolutely necessary and deserves to be shown. - - -**Conflicts:** -Does it conflict with other Instant Answers? We wouldn't want to step on the query space of other Instant Answers. - -- **Example test:** Check to see if an Instant Answer already exists for the new Instant Answer's triggers. For example, [a search for "Bill Murray"](https://duckduckgo.com/?q=bill+murray) currently shows Wikipedia, so if the new Instant Answer will show for searches like, "Bill Murray," then it should be noted to the developer in case one Instant Answer is better than the other. - - -## Developers (Reviewing Code): - -**High Level (Perl & JavaScript)** - -- Is this the right Instant Answer type? Would it be better to implement this as another IA type? - - - If this should really be another IA type (e.g., JavaScript heavy Goodie should probably be a Spice) it's best to note this as early in the process as possible, since the Instant Answer will need to be re-implemented and a new pull-request submitted. - -- What other data is available from this source? (check the API response for anything really useful that's not being displayed). - -- Is this the best layout/design to display all the data? (For Spice, consider the various templates) - -- Does the API often return irrelevant results? If so, consider using the `isRelevant()` function or, if that is already in use, improve the client-side relevancy checking by making it more strict. - -**Regarding the code...** - -- The code should well organized. If things don't make sense, make comments and ask questions! It's better to go overboard with your feedback than to hold back. - - - For Perl modules, metadata comes first, then static variables and function definitions, then the `handle` function last. - -- The code should be easy to understand and follow. Other developers must be able to understand the code reasonably well, in case fixes or improvements need to be made. - - - Non-trivial functions and processes should be well documented. - - - The code should be written to optimize performance and efficiency while maintaining simplicity and legibility (where applicable and without causing performance problems). - - - Inefficient code should be noted and improvements should be suggested. - -- Are the caching parameters correct? Should we *not* cache the API responses? (they are cached by default) - - - Consider `is_cached` and `proxy_cache_valid`. - -- If an Instant Answer is capable of displaying profanity or questionable adult humor, make sure the `is_unsafe` flag is set. - -**Low Level (Perl)** - -- Static variables (e.g., lists, hashes, regular expressions) and helper functions should not be declared inside `handle` functions (this will cause them to be redefined each time the Instant Answer is triggered). The same applies when reading files — the file handler should be outside the `handle` function. - -- Long lists of trigger words/phrases should be moved to a `triggers.txt` file (in the share directory) and `slurp()` should be used to convert them into a list. - -- API Keys should not be merged in, the proper placeholder syntax should be used `{{ENV{DDG_SPICE__APIKEY}}}` - - - DDG staff should also be notified so we can get our own API Key. - -- Check if existing CPAN libraries can replace some functions. We can use libraries for a variety of things and you can find them all on [MetaCPAN](https://metacpan.org/). - -**Low Level (JavaScript)** - -- Semicolons must be used; Point out any missing semicolons; - -- Cross-browser compatibility is very important! Use jQuery methods where applicable to ensure this. We also support IE9+, and it's good to be aware of these. Search for the method on MDN if you're unsure. - - - e.g., `Array.prototype.forEach()` should be replaced with `$.each()`. - -- JavaScript that runs before the rendering of the Spice (`Spice.render()`) should be used to error-check, massage, and organize the data that will be passed on to the template. - -- Template helpers should be used to format output and decide what should be shown. - -- HTML should not be created or inserted into the DOM before or after rendering. For example, don't use jQuery to build your display, that's what templates are for :) - -- Templates and sub-templates should be used when dealing with HTML, not jQuery. - -- Don't declare any global variables. JavaScript has function scope, so anything outside of a function is global (or part of the `window` object). Also, variables that were not declared with `var` are globals, too. Check out the [JavaScript Garden](http://bonsaiden.github.io/JavaScript-Garden/#function.general) for more information about this. - - - On this note, make sure [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FFunctions_and_function_scope%2FStrict_mode) is enabled. This helps catch the usual pitfalls in JS. - -- `if` statements should always use curly braces, `{}` diff --git a/duckduckhack/resources/other_development_environments.md b/duckduckhack/resources/other-dev-environments.md similarity index 98% rename from duckduckhack/resources/other_development_environments.md rename to duckduckhack/resources/other-dev-environments.md index d55d0bc82..4de2447c6 100644 --- a/duckduckhack/resources/other_development_environments.md +++ b/duckduckhack/resources/other-dev-environments.md @@ -1,6 +1,6 @@ # Other Development Environments -For the vast majority of contributors, the best, simplest, and quickest way to develop on DuckDuckHack is to use the **free, web-based Codio environment**. Instructions on setting up Codio can be found in the section on [setting up your development environment](https://duck.co/duckduckhack/setup_dev_environment). +For the vast majority of contributors, the best, simplest, and quickest way to develop on DuckDuckHack is to use the **free, web-based Codio environment**. Instructions on setting up Codio can be found in the section on [setting up your development environment](#). However, some developers prefer working on either a local environment or a virtual machine. This guide addresses setting up DuckPAN on environments other than Codio. @@ -10,7 +10,7 @@ DuckPAN is an application built to aid DuckDuckHack developers. It is mainly use ## Overview of Environments -Aside from the preferred [Codio web-based environment](https://duck.co/duckduckhack/setup_dev_environment), there are three other environments you can set up: +Aside from the preferred [Codio web-based environment](#), there are three other environments you can set up: - [**Install DuckPAN locally**](#installing-duckpan-locally). This **requires Linux or Mac OS X**. We suggest you install [Ubuntu](http://www.ubuntu.com/download). - The [**DuckDuckHack Virtual Machine Image**](#duckduckhack-development-virtual-machine) diff --git a/duckduckhack/resources/video-tutorials.md b/duckduckhack/resources/video-tutorials.md deleted file mode 100644 index b386d2693..000000000 --- a/duckduckhack/resources/video-tutorials.md +++ /dev/null @@ -1,21 +0,0 @@ -# Video Tutorials - -We have some video tutorials (screencasts) which accompany our DuckDuckHack documentation. - -## Setting up your environment - -Watch a visual guide to setting up GitHub and Codio, the easiest way to start making DuckDuckGo Instant Answers. - -[Watch video on Vimeo](https://vimeo.com/132712266) - -![https://vimeo.com/132712266](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fscreencast_environment-setup.jpg&f=1) - -## Multiple API endpoints - -Learn how to handle multiple API endpoints when developing a DuckDuckGo Instant Answer. - -[Watch video on Vimeo](https://vimeo.com/137152536) - -![https://vimeo.com/137152536](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fscreencast_multiple-endpoints.jpg&f=1) - - diff --git a/duckduckhack/spice/spice_basic_tutorial.md b/duckduckhack/spice/spice_basic_tutorial.md deleted file mode 100644 index 2f08d05b9..000000000 --- a/duckduckhack/spice/spice_basic_tutorial.md +++ /dev/null @@ -1,472 +0,0 @@ -# Basic Spice Tutorial - -In this tutorial, we'll be making a Spice Instant Answer that lets you search for Node.js packages, using the [Node Packaged Modules API](http://registry.npmjs.org/uglify-js/latest). The end result works [like this](https://next.duckduckgo.com/?q=npm+http-server). - -First, however, you'll learn an easy tool that automates much of the details. - -## Let Us Know What You're Working On - -**Before you start coding your new Instant Answer, let us know your plans.** By involving us early we can provide guidance and potentially save you a lot of time and effort. - -Email us at [open@duckduckgo.com](mailto:open@duckduckgo.com) with what idea you're working on and how you're thinking of going about it. - -## Automatically Generate Your Spice Files - -The following tutorial will walk through each file and line of code necessary to build and test the example Spice. However, for building your *own* Instant Answer, we've created a tool that **automatically creates the necessary boilerplate files**, with all of the correct naming conventions. - -The `duckpan new` tool will create the following Spice files for you automatically, with the correct paths and naming conventions inside each file: - -- The backend Perl file with the right name, in the `DDG/Spice/` directory -- The frontend handlebars and JavaScript files, in the `share/spice/` directory -- The test file, in the `t/` testing directory - -This allows you to focus on what makes your Spice unique. To use this tool, follow these instructions: - -1. After [setting up your environment](https://duck.co/duckduckhack/setup_dev_environment), open your Terminal and enter the root directory of your local repository, `zeroclick-spice\`. -2. At the command line, type `duckpan new` and hit enter. -3. When prompted, enter what you want to call your Spice Instant Answer. - - *For example, `ron swanson quotes`. White spaces and character casing are automatically formatted.* - -4. You will see a list of the boilerplate files created for you, each with the proper naming conventions already done: - - ``` - [09:22 PM ... zeroclickinfo-spice {master}]$ duckpan new - Please enter a name for your Instant Answer : ron swanson quotes - Created file: lib/DDG/Spice/RonSwansonQuotes.pm - Created file: share/spice/ron_swanson_quotes/ron_swanson_quotes.handlebars - Created file: share/spice/ron_swanson_quotes/ron_swanson_quotes.js - Created file: t/RonSwansonQuotes.t - Successfully created Spice: RonSwansonQuotes - ``` - -Congratulations! You've breezed through a lot of typing, files, and naming conventions. In the following tutorial, we'll explain each line of code and how to customize it to your Instant Answer idea. - -## NPM Spice - Backend (Perl) - -We'll start building our Spice on the backend, by first creating a Perl file, [NPM.pm](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/Npm.pm). - -Our Perl file is located in [zeroclickinfo-spice/lib/DDG/Spice/](https://github.com/duckduckgo/zeroclickinfo-spice/tree/master/lib/DDG/Spice) and will end up containing the following: - -```perl -package DDG::Spice::Npm; -# ABSTRACT: Returns package information from npm package manager's registry. - -use strict; -use DDG::Spice; - -primary_example_queries "npm underscore"; -description "Shows an NPM package"; -name "NPM"; -code_url "https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/Npm.pm"; -icon_url "/i/npmjs.org.ico"; -topics "sysadmin", "programming"; -category "programming"; -attribution github => ['https://github.com/remixz', 'zachbruggeman'], - twitter => ['https://twitter.com/zachbruggeman', 'zachbruggeman']; - -triggers startend => 'npm', 'nodejs'; -triggers start => 'npm install'; - -spice to => 'http://registry.npmjs.org/$1/latest'; -spice wrap_jsonp_callback => 1; - -handle remainder_lc => sub { - return $_ if $_; - return; -}; - -1; -``` - -Let's go through line by line. - -## Naming our Spice Package - -To begin, open your favorite text editor like [gedit](http://projects.gnome.org/gedit/), notepad or [emacs](http://www.gnu.org/software/emacs/) and type the following: - -```perl -package DDG::Spice::Npm; -# ABSTRACT: Returns package information from npm package manager's registry. -``` - - - -Each Instant Answer is a [Perl package](https://duckduckgo.com/?q=perl+package), so we start by declaring the package namespace. For a new Spice (or any new Instant Answer), you would change **npm** to the name of the Instant Answer (written in [CamelCase](https://duckduckgo.com/?q=camelcase) format). - -The second line is a special comment line that is used for documentation purposes. - -## Import the Spice Class - -Next, type the following [use statement](https://duckduckgo.com/?q=perl+use) to import [the magic behind](https://github.com/duckduckgo/duckduckgo/tree/master/lib/DDG) our Instant Answer system. - -```perl -use DDG::Spice; -``` - -**Note:** Right after the above line, you should include any Perl modules that you'll be leveraging to help generate the answer. **Make sure** you add those modules to the `dist.ini` file in this repository. If you're not using any additional modules, carry on! - -## Define the Meta Properties - -The meta properties assist in the display, classification, and attribution of your Instant Answer. Some of these properties are self-explanatory; all are detailed in the [metadata section](https://duck.co/duckduckhack/metadata). - -```perl -primary_example_queries "npm underscore"; -description "Shows an NPM package"; -name "NPM"; -code_url "https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/Npm.pm"; -icon_url "/i/npmjs.org.ico"; -topics "sysadmin", "programming"; -category "programming"; -attribution github => ['https://github.com/remixz', 'zachbruggeman'], - twitter => ['https://twitter.com/zachbruggeman', 'zachbruggeman']; -``` - -For now, feel free to copy, paste, and edit these values into your file. - -## Define the Trigger Word(s) - -On the next line, type: - -```perl -triggers startend => 'npm', 'nodejs'; -triggers start => 'npm install'; -``` - -**triggers** are keywords/phrases that tell us when to make the Instant Answer run. When a particular *trigger word* (or phrase) is part of a search query, it tells DuckDuckGo to *trigger* the Instant Answer(s) that have indicated they should trigger for the given word (or phrase). - - - -Let's say someone searched "**npm uglify-js**". "**npm**" is the *first* word, so it would trigger our Spice because the **startend** keyword says, "Make sure these *trigger words* are at the *start*, or the *end*, of the query." - -For the second trigger line, `triggers start` defines phrases that can be found at the start of a query. The **=>** symbol is there to separate the trigger words from the keywords (for readability). More on triggers can be found in the [triggers overview](https://duck.co/duckduckhack/spice_triggers). - -### Triggers in Multiple Languages - -We have plans to make it possible to trigger Instant Answers in many different languages. Until an internationalization mechanism is place, to uphold maintainability and consistency, **we cannot accept pull requests that add languages directly in the code.** - -## Define the API Call - -On the next line enter: - -```perl -spice to => 'http://registry.npmjs.org/$1/latest'; -``` - -The **spice to** attribute indicates the API endpoint we'll be using, which means a `GET` request will be made to this URL. The URL has a **$1** placeholder, which will eventually be replaced with whatever string our `handle` function (which we'll define shortly) returns. Generally, Spice Instant Answers use a search endpoint for a given API and so, we'll need to pass along our search term(s) in the API request. We do this by returning the desired search terms in the `handle` function and then the **$1** placeholder, in the `spice to` URL, will be replaced accordingly. - -Please note that we prefer to use **HTTP** endpoints, even when an HTTPS endpoint is available. The faster connections make for a better user experience. User security and privacy is not violated because all requests are proxied through an HTTPS connection to the DuckDuckGo servers. - - - -Using the previous example, if we wanted to search for "**uglify-js**" with the NPM API, we'd need to replace `$1` with `uglify-js` which would result in this URL: . If you follow that link, you'll notice the API returns a JSON object containing all the data pertaining to the "uglify-js" NPM Package. - -## Indicate our Callback Function - -In most cases, APIs allow for a "callback" parameter, which usually looks like this: - -```perl -http://www.api.awesome.com/?q=&callback= -``` - -This parameter is used to wrap the JSON object being returned, in a JavaScript function call to the function named in the `callback` parameter. Unfortunately, the NPM API doesn't allow for this parameter to be specified, so we force this manually by using the **spice wrap\_jsonp\_callback** function. Enter this text on the next line to do this: - -```perl -spice wrap_jsonp_callback => 1; -``` - - - -Now, when the JSON is returned by the API, it will be wrapped in a call to our Spice's JavaScript callback function, which we'll define later. - ------- - -**If the API *did* support the callback parameter**, our `spice to` would look like this: - -```perl -spice to => 'http://registry.npmjs.org/$1/latest?callback={{callback}}'; -``` - -Where `{{callback}}` is another special placeholder which will be **automatically** replaced with the correct Spice callback function name. - -**Note:** Not every API uses the word "callback" for their callback parameter. Some use "jsonp" (i.e.`&jsonp=myCallbackFn`), but these names are simply convention. Therefore it's best to read the documentation for the API you're using to ensure the correct callback parameter name is used. - -## Define the Handle Function - -Moving on, enter this on the next line: - -```perl -handle remainder => sub { -``` - -Once triggers are specified, we define how to *handle* the query. `handle` is another keyword, similar to **triggers**. - -You can *handle* different parts of the search query, but the most common is the **remainder**, which refers to the remainder of the query, after the first matched trigger word/phrase has been removed. - - - -For example, if the query was "**npm uglify-js**", the trigger would be *npm* and the **remainder** would be *uglify-js*. - -Now let's add a few more lines to complete the handle function: - -```perl -handle remainder => sub { - return $_ if $_; - return; -}; -``` - -This function (the part within the **{}**, after **sub**) is a very important part of the Spice. It defines what our Spice Instant Answer will replace the **$1** placeholder with. - -Whatever you are *handling* is passed to the `handle` function in the **$\_** variable ( **$\_** is a special default variable in Perl that is commonly used to store temporary values). For example, if you searched DuckDuckGo for *"npm uglify-js"*, the value of **$\_** will be *"uglify-js"*, i.e. the **remainder** of the query. - -Let's take a closer look at the first line of the function: - -```perl -return $_ if $_; -``` - -The heart of the function is just this one line. The **remainder** is in the **$\_** variable as discussed. If it is not blank ( **if $\_** ), we return the **$\_** variable, which as we just saw, contains the **remainder** of the query. - -If we are unable to provide a good Instant Answer, we simply **return nothing**. That's exactly what the second line in the function does: - -```perl -return; -``` - -This line is only run if **$\_** contained nothing, because otherwise the line before it would return something and end the function execution. - -If for example you searched DuckDuckGo for "npm", this would trigger our NPM Spice, and then in the `handle` function the value of **$\_**, the **remainder** of the query, would be blank (because "npm" is the trigger word and would have been stripped out) and so our handle function would **return nothing**, meaning no API call would be made, and no result will be displayed. - -## Return True at EOF - -Finally, all Perl packages that load correctly should [return a true value](http://stackoverflow.com/questions/5293246/why-the-1-at-the-end-of-each-perl-package) so add a `1;` on the very last line. - -```perl -1; -``` - -And that's it! At this point you have written a functional backend for a Spice Instant Answer. Now, the frontend components need to written so that we can display our Instant Answer result on the page. - -## Recap (So Far) - -The Instant Answer system works like this at the highest level: - -- First, DuckDuckGo breaks down the query (search terms) into separate words. - -- Then we see if any of those words or groups of words are **triggers** for any Instant Answers. These **triggers** are defined by the developer when creating an Instant Answer. In the example we used above, the trigger word is "**npm**". - -- If a Spice is triggered, we run its `handle` function. - -- If the Spice's `handle` function returns a value, it is used to replace our **$1** placeholder in the **spice to** URL, and then a request is made to that URL. When the API responds with a JSON object, it is wrapped in a function call, making the JSON object the input to our JavaScript callback function. - - ------- - -# NPM Spice - Frontend (JavaScript) - -As mentioned, every Instant Answer requires a Spice callback function. For the *NPM* Instant Answer, the callback function will be named `ddg_spice_npm()` and will be defined in the [npm.js](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/share/spice/npm/npm.js) file. - -The name of the callback function is determined by the **Npm.pm** Perl module we created above, which specifies the name of the package, `DDG::Spice::Npm`. The portion after `DDG::Spice::` is lower cased and converted from camel case to underscore separated (i.e. `DDG::Spice::CamelCase` -> `ddg_spice_camel_case`) in order to create the name of our callback function. - -This generated name is what the previous `{{callback}}` placeholder will be replaced with. Similarly, if we instead had to use `spice wrap_jsonp_callback`, that will also wrap the JSON returned by the API with a function call to this generated callback name. - -In practice: - -```perl -{{callback}} => ddg_spice_npm -``` - -or - -``` -{ JSON_API_RESPONSE: 1, ... } => ddg_spice_npm( { JSON_API_RESPONSE: 1, ... } ); -``` - -The final [npm.js](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/share/spice/npm/npm.js) will be located in [zeroclickinfo-spice/share/spice/npm/](https://github.com/duckduckgo/zeroclickinfo-spice/tree/master/share/spice/npm) and end up as follows: - -```javascript -(function (env) { - "use strict"; - env.ddg_spice_npm = function(api_result){ - - if (!api_result || api_result.error) { - return Spice.failed('npm'); - } - - Spice.add({ - id: "npm", - name: "Software", - data: api_result, - meta: { - sourceName: "npmjs", - sourceUrl: 'http://npmjs.org/package/' + api_result.name - }, - normalize: function(item) { - ... - }, - - templates: { - group: 'text', - options: { - content: Spice.npm.content, - moreAt: true - } - } - }); - }; -}(this)); -``` - -Let's go through each line. - -## Invoke an Immediate Function - -###### npm.js (continued) - -```javascript -(function (env) { - "use strict"; -``` - - - -We begin by invoking an anonymous, immediately-executing function, which takes an object as input (i.e. the environment). This is better known as the JavaScript "[Module Pattern](http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript)" and it ensures that any variables or functions created within our anonymous function do not affect the global scope. It also allows us to explicitly import anything from the outside environment, which we use to pass along the global scope into our `env` variable (you'll see this at the end), so we can still access and modify the global scope as needed. After creating our module, we then turn on JavaScript's [Strict Mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FFunctions_and_function_scope%2FStrict_mode) which also offers various benefits. You don't really need to understand the purpose of these first two lines to create a Spice Instant Answer; however, they're both necessary and must be included when defining a Spice callback function. - -## Define our Callback Function - -```javascript - env.ddg_spice_npm = function (api_result) { -``` - - - -We now define our callback function, `ddg_spice_npm`, using a function expression and set it as a property of the `env` object that was passed into our anonymous function. The name we specify here ***must*** match the name of our Spice package, which we discussed above. We also specify that the callback function takes one input parameter, which we have named `api_result`. All Spice callback functions should look like this. The name for the input should **always** be called `api_result`. This is part of our naming convention and helps to standardize the JavaScript code. - -## Validate our API Response - -###### npm.js (continued) - -```javascript - if (api_result.error) { - return Spice.failed('npm'); - } -``` - - - -Here we specify that if the `error` property in the API result is defined (meaning there are no results to use) we `return` a call to `Spice.failed('npm')`, which stops the execution of the function and as a result, won't display an Instant Answer. It's important to use `Spice.failed();` because this lets the rest of the Spice system know our Spice isn't going to display so that other relevant answers can be given an opportunity to display. - -In other cases, because most APIs return an array of results, a similar check should be made to see if the results array has a `length`. This way, if no results were returned from the API we can stop and display nothing. - -Moving on, the next part is very important, it defines how the Spice result should look and specifies which parts of the API result are important. This piece of code tells DuckDuckGo to show the Instant Answer: - -## Display our Spice Result (and Specify some Details) - -###### npm.js (continued) - -```javascript - ... - Spice.add({ - id: "npm", - name: "Software", - data: api_result, - meta: { - sourceName: "npmjs", - sourceUrl: 'http://npmjs.org/package/' + api_result.name - }, - normalize: function(item) { - ... - }, - - templates: { - group: 'text', - options: { - content: Spice.npm.content, - moreAt: true - } - } - }); -``` - -Here we make a call to the `Spice.add()` function, which operates on an input object that we normally define inside the function call. - -Full documentation of these properties can be found at the [display reference](https://duck.co/duckduckhack/display_reference). A few notes in particular: - -- [**`data`**](https://duck.co/duckduckhack/display_reference#codedatacode-emobjectem-required) is the object or array that is passed along (item by item, if iterable) to the handlebars template. In most cases the `data` parameter should be set to `api_result` so all the data returned from the API is accessible to the template. -- [**`normalize`**](https://duck.co/duckduckhack/display_reference#codenormalizecode-emfunctionem-optional) is an optional function that is useful for "pre-processing" each item in `data` before it is used by the template. -- [**`templates`**](https://duck.co/duckduckhack/display_reference#codetemplatescode-emobjectem-required) specifies the template group, templates, and other display options to apply when rendering the results to the DuckDuckGo AnswerBar. Here, we use the [Text Template Group](https://duck.co/duckduckhack/template_groups#text-template-group). - -## Global Import - -```javascript - } -}(this)); -``` - -Lastly, we close our callback function expression, as well as our module, and we "import" the global scope, via `this`, as the input to our module. This means within the module, we can access the global scope using the `env` variable defined at the very beginning. - -## Defining Templates - -At this point, the rendering of the Spice Instant Answer changes context from JavaScript to Handlebars.js. As mentioned, our `Spice.add()` call specifies our template group and the **context** for the template. - -We specified the [Text Template Group](https://duck.co/duckduckhack/template_groups#text-template-group). This is a preset, which automatically picks the `text_item` and `text_detail` for multiple and single results, respectively. Practically speaking, the NPM Spice by its nature can only return one result at a time - hence just `text_detail`. - -### Creating a Custom Sub-Template - -The [`text_detail` template](https://duck.co/duckduckhack/templates_reference#codetextdetailcode-template) has an ability to pass a `content` sub-template. We pass it a custom handlebars template that we created for this Instant Answer. It is located at [`spice/npm/content.handlebars`](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/share/spice/npm/) and is thus compiled into a handlebars function titled `Spice.npm.content` ([explained here](https://duck.co/duckduckhack/subtemplates#custom-subtemplates)). - -Let's quickly look at the file [`content.handlebars`](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/share/spice/npm/content.handlebars) to see how it displays the Instant Answer result. Detailed information can be found in the [sub-templates section](https://duck.co/duckduckhack/subtemplates). - -```html -
$ npm install {{name}}
-``` - -As you can see, we access the `name` property of each item. This is possible because it was present in the `data` property, to which we directly passed this `api_result`. - -```json -{ - - "name": "http-server", - "version": "0.6.1", - "author": ... - ... -} -``` - -With this sub-template added, our NPM Spice frontend is complete. - -## All Done! - -And that's it, you're done! You now have a working Spice Instant Answer! The backend (Perl) tells DuckDuckGo when to trigger our Spice and the frontend (JavaScript & Handlebars) tells DuckDuckGo what to put on the page. Before moving on, lets do a quick recap... - -## What We've Accomplished - -We've created **one** file in the Spice lib directory, `lib/DDG/Spice/`: - -- `Npm.pm` - defines the API to use and the triggers for our Spice - -We've created **two** files in the Spice share directory, `share/spice/npm/`: - -- `npm.js` - delegates the API's response and calls `Spice.add()` -- `content.handlebars` - specifies the Instant Answer's HTML structure and determines which properties of the API response are placed into the HTML result - -To recap, the Spice Instant Answer system works like so: - -- First, DuckDuckGo breaks down the query (search terms) into separate words. - -- Then we see if any of those words or groups of words are **triggers** for any Instant Answers. These **triggers** are defined by the developer when creating an Instant Answer. In the example we used above, the trigger word is "**npm**". - -- If a Spice is triggered, we run its `handle` function. - -- If the Spice's `handle` function returns a value, it is used to replace our **$1** placeholder in the **spice to** URL, and then a request is made to that URL. When the API responds with a JSON object, it is wrapped in a function call, making the JSON object the input to our JavaScript callback function. - -- Our JavaScript callback function takes the API result (JSON Object), and passes it along to our `Spice.add()` call which specifies, using the elements of the JSON object, which parts are to be used in creating the Instant Answer "More at" link and most importantly, passes along our JSON object to our Spice's Handlebars template. - -- The template then defines, using HTML and variables, what the actual content for the Spice result will be - -- This content is then created and rendered onto the page by the Spice system's backend. - -## Next Steps - -Congratulations! You have now written your first Spice Instant Answer. Now, let's move on to testing, so we can make sure our Instant Answer functions as expected! diff --git a/duckduckhack/spice/spice_frontend_walkthroughs.md b/duckduckhack/spice/spice_frontend_walkthroughs.md deleted file mode 100644 index df828b49e..000000000 --- a/duckduckhack/spice/spice_frontend_walkthroughs.md +++ /dev/null @@ -1,1010 +0,0 @@ -# Spice Frontend Walkthoughs - - - -## Alternative.To Walkthrough (Simple) - -The [Alternative.To Instant Answer](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/share/spice/alternative_to/alternative_to.js) is very similar to NPM in that it's also relatively simple. However, it returns multiple items, and so it produces a tile view, where each tile represents a single result item. Let's take a look at the code and see how this is done: - - -###### alternative_to.js - -```javascript -(function(env) { - "use strict"; - - env.ddg_spice_alternative_to = function(api_result) { - - if (!api_result || !api_result.Items) { - return Spice.failed('alternative_to'); - } - - Spice.add({ - id: 'alternative_to', - name: 'Software', - data: api_result.Items, - signal: 'high', - meta: { - searchTerm: api_result.Name, - itemType: 'Alternatives', - sourceUrl: 'http://alternativeto.net/', - sourceName: 'AlternativeTo' - }, - normalize: function(item) { - return { - ShortDescription: DDG.strip_html(DDG.strip_href(item.ShortDescription)), - url: item.Url, - icon: item.IconUrl, - title: item.Name, - description: item.ShortDescription - }; - }, - templates: { - group: 'icon', - options: { - footer: Spice.alternative_to.footer - } - } - }); - }; - - Handlebars.registerHelper("AlternativeTo_getPlatform", function (platforms) { - return (platforms.length > 1) ? "Multiplatform" : platforms[0]; - }); - -}(this)); -``` - -Just like the NPM Spice, Alternative.To uses `Spice.add()` with most of the same properties. However, it also uses a few new properties as well. The biggest difference between the two Spices though, is that **the AlternativeTo API returns an array of items, which is given to `data`,** rather than a single item like the NPM API. - -This means first and foremost, that we'll be dealing with a tile view, so that each item can be separately displayed. In order to do that, we must specify an `item` template which determines the content of each tile. Let's begin by taking a look at the new `Spice.add()` properties used by Alternative.To: - -- `searchTerm` is used to indicate the term that was searched, stripped of unimportant words (e.g., "cat videos" would be "cat") or more formally, this is known as the *[noun adjunct](https://en.wikipedia.org/wiki/Noun_adjunct)*. The `searchTerm` will be used for the MetaBar wording where it reads, for example, "Showing 10 **iTunes** Alternatives", where "iTunes" is the `searchTerm` for the query "alternatives to iTunes". - -- `itemType` is the type of item being shown (e.g., Videos, Images, Alternatives) and this is also used in the MetaBar. In the previous example, "Showing 10 **iTunes** Alternatives", "Alternatives" is the `itemType`. - -- `normalize` allows you to normalize an item before it is passed on to the template. You can add or modify properties of the item that are used by your templates. In our case, we're using `normalize` to modify the `ShortDescription` property of each item, by removing HTML content from it and we also use it to add a few properties to our `item`, which our template will use. - - **Note:** This function uses jQuery's `$.extend()` method, so it will modify your `data` object by adding any returned properties that don't already exist, or simply overwrite the ones that do. - -- `templates` is used to specify the template `group` and any other templates that are being used. Template `options` may also be provided to enable or disable template components used by the chosen `group`. In our case we've specified that we're using the `icon` template group and in the `options` block, we've specified the sub-template to be used for the `footer` component. - - The `icon` template has a few features including a `title`, `icon` and `description`. It also has an optional `footer` feature, which is actually a sub-template. We've created a **footer.handlebars** template and placed in the AlternativeTo Spice's share directory, **/share/spice/alternative_to**. We specify in the `options` block that the template to be used for the `footer` feature is the template we've created by referencing its name: `Spice.alternative_to.footer`. - ------- - -Now, let's take a look at the Footer Handlebars template: - -###### footer.handlebars (in /share/spice/alternative_to/) - -```html -
- {{Votes}} likes{{#if Platforms}} • {{AlternativeTo_getPlatform Platforms}}{{/if}} -
-``` - -As you can see, this is some fairly simple HTML, which contains a few Handlebars expressions referencing properties of `data` as well as some [Handlebars helpers](https://duck.co/duckduckhack/handlebars_helpers) functions (i.e. `if` and `AlternativeTo_getPlatform`). - -These helpers are really JavaScript functions, which operate on input and return their content to the template. - -The `if` helper is a **block helper**, which acts like a normal `if` statement. When the specified variable exists, the code contained within the block, `{{#if}}...{{/if}}`, is executed. In our case, we use it to make sure the `Platforms` property is defined for the current item (remember we're looping over each item and applying this template) and if so, we add a bullet point, ` • ` and the result of `{{AlternativeTo_getPlatform Platforms}}` to the page. - -You may have noticed that `AlternativeTo_getPlatform` is actually defined alongside our `ddg_spice_alternative_to` callback function. Let's take a quick look at it: - -###### alternative_to.js - -```javascript - Handlebars.registerHelper("AlternativeTo_getPlatform", function (platforms) { - return (platforms.length > 1) ? "Multiplatform" : platforms[0]; - }); -``` - -This code demonstrates how Handlebars helpers are created and made available to the templates. Using the `Handlebars.register()` method, you are able to specify the name of the helper you are registering, as well as the function it executes. The `AlternativeTo_getPlatform` helper is very simple: it takes an array as input and depending on the length, returns either the first element in the array, or if more than one element exists, returns the string "Multiplatforms". - -The AlternativeTo Spice also uses a little bit of CSS to further customize and perfect the layout of the tiles. Let's take a look at the CSS: - -###### alternative_to.css - -```css -.tile--alternative_to .tile__icon { - float: right; - margin-top: 0; - margin-right: 0; -} - -.tile--alternative_to .tile__body { - height: 13em; -} - -.tile--alternative_to .tile__footer { - bottom: 0.6em; -} -``` - -As you can see, this Spice requires very little CSS. This layout is a bit unique and so we opted to modify the layout of the content slightly. In most case the use of templates will alleviate the need to write custom CSS, however sometimes it is necessary and can be used sparingly. - -The most important thing to note is that we have prefaced each of our CSS rules with the class `.tile--alternative_to`. Each Spice Instant Answer is wrapped in a `
` that has a class called `.zci--` where `` matches the Spice's package name. As well, when a tile view is used, *each tile* is wrapped in a `div` that has a class called `.tile--`. These allow us to **namespace** all the CSS rules for each individual Spice and their tiles. The is very important because DuckDuckGo simultaneously loads and triggers multiple Spice Instant Answers (depending on the query) and so namespaceing the CSS is necessary to ensure that none of our Spices' CSS rules affect other elements on the page. If your Spice requires any CSS, it **must** only target child elements of `.zci--` for the detail area and/or `.tile--` for the tiles. - - - - - - - diff --git a/duckduckhack/spice/spice_overview.md b/duckduckhack/spice/spice_overview.md deleted file mode 100644 index 21c8e2910..000000000 --- a/duckduckhack/spice/spice_overview.md +++ /dev/null @@ -1,64 +0,0 @@ -## Spice Overview - -**If you have questions, [request an invite to our Slack team](mailto:QuackSlack@duckduckgo.com?subject=AddMe) and we'll be happy to chat!** - -Spice Instant Answers are triggered by a backend Perl component that then calls the JSON API of an upstream service. The API response is wrapped in a JavaScript function call. You, the Instant Answer author, define this callback function and handle the API's response on the client side, generating the display from the data returned by the API. - -## Spice API Criteria - -With millions of search queries triggering Instant Answers every day, it's important to choose APIs wisely. There are several criteria for APIs used in Spice Instant Answers. - -First, the technical constraints: - -- APIs called by Spice Instant Answers **must use the JSON or JSONP formats**. We do not support the use of XML (it's coming soon though!), HTML, or plain text responses. -- APIs should respond to requests in **less than one second** (< 1s). -- Avoid **static JSON files**. These often have large payloads and require heavy local processing. Not sure? [Talk to us about your idea](mailto:open@duckduckgo.com). - -Next, API reliability: - -- APIs used should be **reliable**. Pick sources that will be most likely be around and accurate for the foreseeable future. -- APIs created by contributors solely for the purpose of an Instant Answer cannot be accepted. - -Finally, credibility: - -- APIs used should represent the **most credible source** for the information. This means it should draw upon the preferred data source of the relevant community. Look for posts and sources like [these](https://duck.co/forum/thread/37/great-resources-for-instant-answer-ideas) which have been suggested by others. -- APIs must have the appropriate permissions or rights to serve their information. - -If you're not sure about whether the API you'd like to use fits these criteria, we're happy to help figure it out. Email us over at [open@duckduckgo.com](mailto:open@duckduckgo.com). - -## Spice Frontend - -The Spice frontend is the code that is triggered by the Perl backend for your spice Instant Answer. It mainly consists of a function (the Spice "callback" function) that takes a JSON formatted, API response as its input and uses the data to display a Spice result at the top of the DuckDuckGo search results page. - -## Spice Templates - -In order to display the result, the Spice callback function needs to specify a template, which will determine how the result looks. There are several built-in templates to choose from and you are able to choose whichever template works best for the given data and desired output. - -## Third-Party Libraries - -Aside from HTML and CSS, the Spice frontend also utilizes the following third-party libraries: - -- [jQuery](https://jquery.org) v1.10.2 -- and [Handlebars](http://handlebarsjs.com) v1.3.0 - -If you're not already familiar with Handlebars, *please* read the [Handlebars documentationn](http://handlebarsjs.com) before continuing on. Don't worry if you don't fully understand how to use Handlebars, the examples will explain everything. You should, at the very least, familiarize yourself with Handlebars concepts and terminology before moving on. It should only take a few minutes to read! - - - -Likewise, using jQuery is not required for making a Spice Instant Answer. But, it does offer certain benefits, such as cross-browser compatible implementations of various JavaScript functions. For example, jQuery's `$.each()` should be used in place of the native `Array.prototype.forEach()`, as it does **not** work in IE 6,7,8. - -Later, we will walk you through several examples, ranging from simple to complex, which will explain how to use templates and make your Instant Answers look awesome :) - -## Spice Files - -A typical Spice Instant Answer requires several files to function properly. -- The Perl files go in the **lib** directory: `lib/DDG/Spice/InstantAnswerName.pm` -- The frontend files (JS, Handlebars, CSS) discussed later go in the **share** directory: `share/spice/instant_answer_name/` - -**\*\*Note** : The file and folder names must adhere to our naming conventions (covered later) in order for everything to function properly. - -## Let Us Know What You're Working On - -**Before you start coding your new Instant Answer, let us know your plans.** By involving us early we can provide guidance and potentially save you a lot of time and effort. - -Email us at [open@duckduckgo.com](mailto:open@duckduckgo.com) with what idea you're working on and how you're thinking of going about it. diff --git a/duckduckhack/spice/spice_perl_api.md b/duckduckhack/spice/spice_perl_api.md deleted file mode 100644 index 32b6fa7c4..000000000 --- a/duckduckhack/spice/spice_perl_api.md +++ /dev/null @@ -1,7 +0,0 @@ -# Spice Perl API Reference - -(This section is coming soon! Know what should go here? Then **please** [contribute to the documentation](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/CONTRIBUTING.md)!) - -### share() - -(Know what should go here? Then **please** [contribute to the documentation](https://github.com/duckduckgo/duckduckgo-documentation/blob/master/CONTRIBUTING.md)!) diff --git a/duckduckhack/submitting-your-instant-answer/preparing_for_a_pull_request.md b/duckduckhack/submitting-your-instant-answer/preparing_for_a_pull_request.md deleted file mode 100644 index 92acae5a3..000000000 --- a/duckduckhack/submitting-your-instant-answer/preparing_for_a_pull_request.md +++ /dev/null @@ -1,29 +0,0 @@ -# Preparing for Pull Request - -The culmination of your contribution comes in the form of a pull request to the Github repository for your Instant Answer type. - -Before proceeding, make sure you've done the following steps. - -- [Contacted us](mailto:open@duckduckgo.com) to let us know what you're working on - - By involving us, we can provide guidance and potentially save you a lot of time and effort. Email us at [open@duckduckgo.com](mailto:open@duckduckgo.com) with what idea you're working on and how you're thinking of going about it. - -- Determined your Instant Answer type (we will have helped you determine this, in addition to this [helpful guide](https://duck.co/duckduckhack/determine_your_instant_answer_type)) -- Forked the [correct repository](https://duck.co/duckduckhack/setup_dev_environment) on Github -- Written and committed the code for your Instant Answer -- Make sure you comment your code well so that it's easier for other people to maintain. -- [Manually tested](https://duck.co/duckduckhack/testing_triggers) your Instant Answer -- Written a comprehensive automatic [test file](https://duck.co/duckduckhack/test_files) for your Instant Answer -- Manually verified that everything works. -- Make sure your [tab name](https://duck.co/duckduckhack/display_reference#codenamecode-emstringem-required) is well-chosen and consistent with the guidelines -- If you wrote custom CSS, did you namespace the CSS? Every Instant Answer should be contained in a `
` with a unique id. This id should be used to target your styles and avoid overwriting any global styles. -- Confirmed the Instant Answer adheres to the [code style guide](https://duck.co/duckduckhack/code_styleguide) -- Added yourself to the attribution (if updating an existing Instant Answer). -- Can this Instant Answer return unsafe content (bad words, etc.) - - Did you set `is_unsafe` to true? -- Can this Instant Answer return an HTML response? - - Have you guaranteed that the response does not contain unsanitized user-supplied strings (e.g., the query string) which could lead to [cross-site scripting attacks](https://duckduckgo.com/Cross-site_scripting?ia=about)? - -## Proceed to Adding Metadata - -Checked everything off the list? Congratulations - you are now ready to [add metadata](https://duck.co/duckduckhack/metadata) to your Instant Answer which will help us to understand it a little better. \ No newline at end of file diff --git a/duckduckhack/submitting/checklist.md b/duckduckhack/submitting/checklist.md new file mode 100644 index 000000000..0aa1eb186 --- /dev/null +++ b/duckduckhack/submitting/checklist.md @@ -0,0 +1,94 @@ +# Instant Answer Checklist + +All Instant Answers currently live on DDG adhere to x... we've tried to outline what we believe make for the best experience. These are below... + +In order to be as transparent and simple as possible, we've collected these requirements into a checklist you can review when planning - and before submitting - your Instant Answer. We do our best to keep this list as quick and relevant as we can. + +We'd love your help in evolving these standards! Have an opinion or suggestion on how to make these better? Definitely let us know. + +If you're not sure, or want help satisfying this checklist, we'd love to help! We have a great community [on Slack](mailto:QuackSlack@duckduckgo.com?subject=AddMe) where you can find someone to give detailed feedback. You can also [send an email](mailto:open@duckduckgo.com). + +## What queries will your Instant Answer show up on? + +- **Instant Answers should answer more than a single query, and preferably more than ten.** + + For example, if your idea is to answer "What is the speed of light?" with a constant, that's only one query for an Instant Answer. Instead, consider a "Physics Constants Instant Answer" to cover many common constants (Speed of sound, Planck's constant, and so on...). Similarly, "When is Christmas?" would be better grouped under a "Holiday Dates Instant Answer." + + If a wider Instant Answer already exists that should encompass your queries, consider improving it. + +## Do you plan to use an external data source? + +- **API queries must be free.** + + While paid APIs are not allowed, many APIs are happy to offer DuckDuckGo free access because of attribution and exposure. Consider reaching out and asking. + +- **APIs must return in JSON format.** + + Currently we can only support JSON responses. + +- **APIs must be accessible via GET requests, and authorized via URL parameters.** + + Currently we cannot support POST requests. We also cannot authorize requests via OAuth or HTTP Headers. + +- **APIs must respond in under 1 second, and with less than 200kb of data.** + + If you're not sure about this, we're happy to help. + +## Are you using static (hardcoded) information? + +- **All information should remain accurate in the long term.** + + Any information likely to change should come from an API, not a data file. For example, an Instant Answer that answers "Who is the president of the United States?" should not be hardcoded. + +## Anything you wouldn't show or say to your mother? + +- **Crude language is allowed.** + + For example, Urban Dictionary definitions are allowed. Just make sure to set the [`is_unsafe`](#) attribute. + +- **No adult content.** + + Adult content is not allowed, neither in handling of queries, or results returned. + +## How are you displaying your results? + +- **All new Instant Answers should use [built-in Template Groups](#).** + + For reasons of maintainability, we cannot accept submissions using the Base template unless they've received prior permission. There are many ways to customize the built-in templates, including [options](#), [variants](#), and [sub-templates](#). + +- **Tab names ([`name` display property](#)) should belong to the list of topics.** + + See our list of topics [topics and their guidelines](#). + +## What did you include in your code? + +- **All JavaScript methods should be supported on IE9+, Chrome, Firefox, and Safari.** + + You can check this [handy table for reference](http://kangax.github.io/compat-table/es5/). + +- **No external requests can be made in Perl.** + + Any CPAN modules that make web requests are not allowed. + +## How are you testing your code? + +- **All example queries should be covered in a [test file](#).** + +- **All test files should include negative cases - queries that *should not* trigger.** + +## User Experience + +- **All interactions should work on both desktop and mobile.** + + Avoid interactions that only work on desktop - e.g., hover. + +- **Instant Answers should look good across all platforms.** + + Test how your Instant Answer looks on Chrome, Safari, Firefox, IE 9, IE Edge, as well as Android, iOS, and the iPad. + +- **Instants Answers should look good across [DuckDuckGo Themes](#).** + + Test your Instant Answer with the Dark Theme in particular. + + If you normally use a theme, make sure to test it on the the default theme. + diff --git a/duckduckhack/submitting-your-instant-answer/submission_and_review.md b/duckduckhack/submitting/pull-request.md similarity index 60% rename from duckduckhack/submitting-your-instant-answer/submission_and_review.md rename to duckduckhack/submitting/pull-request.md index 3e41d507e..fa0a5511c 100644 --- a/duckduckhack/submitting-your-instant-answer/submission_and_review.md +++ b/duckduckhack/submitting/pull-request.md @@ -1,11 +1,12 @@ -# Making a Pull Request +# Submitting a Pull Request -Now that you have completed the [pull request checklist](https://duck.co/duckduckhack/preparing_for_a_pull_request) and added [Metadata](https://dukgo.com/duckduckhack/metadata) to your Instant Answer, you are ready to submit a pull request. +The culmination of your contribution comes in the form of a pull request to the Github repository for your Instant Answer type. A pull request is a great, transparent way to review and discuss potential changes to a repository. +The following are step-by-step instructions for submitting your changes as a pull request. ## Committing Your Changes -The first step in submitting your changes is to commit your changes to your local repository. If you've already done this, you can skip to [submitting a pull request](#submitting-a-pull-request), below. +The first step is to commit your changes to your local repository. *If you've already done this, you can skip to [submitting a pull request](#submitting-a-pull-request), below.* Here is how to commit your code in the Codio environment: @@ -55,12 +56,9 @@ If you had created a branch for your current contribution then type **`git push ``` - ## Submitting a Pull Request -Clarity is super important in any open source project - especially one with lots of contributors. A pull request is a great, transparent way to review new changes. - -Here's how to submit a pull request to DuckDuckHack: +*The steps below are all you need, but it's worth mentioning that GitHub provides [excellent instructions](https://help.github.com/articles/creating-a-pull-request) as well.* 1. Open a **new browser tab** and go to your remote repository. For example, for Goodies: **`https://github.com/YOUR_GITHUB_USERNAME/zeroclickinfo-goodies`**. @@ -110,41 +108,6 @@ Here's how to submit a pull request to DuckDuckHack: 7. Finally, click **"Create Pull Request"**! -Congratulations! You can now sit back and relax. A Community Leader or DDG staff member will review your Goodie in turn, give you any feedback (if necessary) and merge it in. - -Once it's merged, it will be deployed to the site and should be live within a few days. **We'll be sure to let you know once it's live!** - -*For more information on how to create a pull request, GitHub provides excellent [instructions](https://help.github.com/articles/creating-a-pull-request).* - -## Review Process - -When you submit a pull request, a community leader will assign themselves to your pull request and work with you to get your Instant Answer live on DuckDuckGo. - -All comments and suggestions for your pull request should be discussed publicly in the pull request on Github, so that others can follow the progression. Of course, you can always contact anyone from the community or team if you have questions. - -In addition to the main [open@duckduckgo.com](mailto:open@duckduckgo.com) way to reach us, here are some community leaders who are available to help out: - - - -- [@bradcater](https://github.com/bradcater) -- [@loganom](https://github.com/loganom) -- [@mattr555](https://github.com/mattr555) -- [@mintsoft](https://github.com/mintsoft) -- [@TomBebbington](https://github.com/TomBebbington) -- [@killerfish](https://github.com/killerfish) -- [@MrChrisW](https://github.com/mrchrisw) -- [@javathunderman](https://github.com/javathunderman) - -Here's how the whole process looks, after you submit your pull request: - -1. A community leader (an experienced member of the community) will work with you on your pull request. - - The community leader will ping [@abeyang](https://github.com/abeyang) (or [@chrismorast](https://github.com/chrismorast)) for any design/interaction feedback. - - The community leader will test and give feedback about your code. - - You discuss and incorporate any applicable feedback. -2. When the community leader feels that the Instant Answer is ready to go live, they ping [@jagtalon](https://github.com/jagtalon) so that he can set it up on a testing server. -3. Final review by the internal team. -4. Your pull request gets merged in and goes live! - -## Thank you! +Congratulations! A Community Leader or DDG staff member will review your contribution in turn. -We're excited to have you in our community and we can't wait to see your Instant Answer on DuckDuckGo.com. Your contributions are helping our search engine get better every day. We owe you our deepest gratitude. +Once your pull request is merged into the repository, it should be live on DuckDuckGo.com within a few days. Next, read about [maintaining your Instant Answer](#) over time. \ No newline at end of file diff --git a/duckduckhack/submitting/submitting-overview.md b/duckduckhack/submitting/submitting-overview.md new file mode 100644 index 000000000..f5e7d04f6 --- /dev/null +++ b/duckduckhack/submitting/submitting-overview.md @@ -0,0 +1,34 @@ +# Going Live + +The most exciting part of hacking on an Instant Answer is launching on DuckDuckGo.com for everyone to enjoy. The search engine serves millions of queries *a day* all around the world, across hundreds of types of devices. + +As you can imagine, the community has set a few requirements in place. In this section, we'll walk you through the process of going live on DuckDuckGo.com. + +Launching an Instant Answer isn't trivial, but it's very doable - and rewarding. For our part, we'll be as transparent as possible about everything you need to know. + +## The Process + +Once you've thought of and planned an idea that can meet the [Instant Answer Checklist](#), the steps for submission are as follows: + +1. Get your idea approved [by reaching out to the community](mailto:QuackSlack@duckduckgo.com?subject=AddMe) ([or email](mailto:open@duckduckgo.com)) +2. Build and test it, with the help of [walkthroughs](#) and [live examples](https://duck.co/ia) +3. [Submit a pull request](#) that meets the [Instant Answer Checklist](#) + +Get started by [letting us know](mailto:open@duckduckgo.com) what you're working on. By involving us, we can provide guidance and potentially save you a lot of time and effort. + +## Feedback and Support + +You don't have to figure it all out on your own. We have a great community [on Slack](mailto:QuackSlack@duckduckgo.com?subject=AddMe), as well as at [Meetups](http://duckduckgo.meetup.com) around the globe. If you need help, or want to think out loud, don't hesitate to join in - we're excited to help newcomers. You can always [send an email](mailto:open@duckduckgo.com) if you prefer. + +**Say hello long before your pull request.** The feedback on pull request submissions is mainly pass/fail based on the [Instant Answer Checklist](#), so it's best to have someone in the community help review your work ahead of time for detailed help and feedback. + +## Maintaining Your Contribution + +(Talk about our process and standards from a high level. Set the expectation of submitting an IA, reviewing, getting feedback, and maintaining long term.) + +(This section should talk about regularly checking on their IA and IA page, prodding the community for feedback/improvements, and owning the query_space long term.) + + + + + diff --git a/duckduckhack/testing/test_files.md b/duckduckhack/testing-reference/test-files.md similarity index 100% rename from duckduckhack/testing/test_files.md rename to duckduckhack/testing-reference/test-files.md diff --git a/duckduckhack/testing/testing_html.md b/duckduckhack/testing-reference/testing-html.md similarity index 100% rename from duckduckhack/testing/testing_html.md rename to duckduckhack/testing-reference/testing-html.md diff --git a/duckduckhack/testing/testing_location_language_apis.md b/duckduckhack/testing-reference/testing-location-language-apis.md similarity index 100% rename from duckduckhack/testing/testing_location_language_apis.md rename to duckduckhack/testing-reference/testing-location-language-apis.md diff --git a/duckduckhack/testing/testing_triggers.md b/duckduckhack/testing-reference/testing-triggers.md similarity index 100% rename from duckduckhack/testing/testing_triggers.md rename to duckduckhack/testing-reference/testing-triggers.md diff --git a/duckduckhack/welcome/ddh-intro.md b/duckduckhack/welcome/ddh-intro.md new file mode 100755 index 000000000..856ebc530 --- /dev/null +++ b/duckduckhack/welcome/ddh-intro.md @@ -0,0 +1,89 @@ +# Welcome to DuckDuckHack + +If you could hack your search engine to put the best information first, what would you do? **Instant Answers are an opportunity to create your own results.** We're an open source community with members [around the globe](http://duckduckgo.meetup.com/). Welcome! + +**Get started right away with a [Tutorial](#).** + +*[Email us](mailto:open@duckduckgo.com) at any time with questions, or join us [over on Slack](mailto:QuackSlack@duckduckgo.com?subject=AddMe).* + +## Create Your Own Results + +Instant Answers allow you to improve search in an area you care about. Your Instant Answer will appear above ads and search results. They have the potential to show up on [millions of searches](https://duckduckgo.com/traffic.html) every day. + +Instant Answers can do a lot of things. They can be quite dynamic... + +![](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fparking_ny.png) + +...or simply convenient: + +![](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fsales_tax.png) + +Some are just cool: + +![](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fheads_tails.png) + +Many are absolutely delightful and unexpected: + +![](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fbpm_ms.png) + +Many are super practical... + +![](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fair_quality.png) + +...in ways we never imagined: + +![](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fblue_pill.png) + +Some Instant Answers are built from pure code: + +![](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Furl_encode.png) + +Other Instant Answers channel external sources (API requests): + +![App search Instant Answer example](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fapp_search_example.png&f=1) + +The [possibilities are endless](https://duck.co/ideas). **Our community's mission is to cover every topic with a community-generated Instant Answer.** + +## Start Hacking + +Learn to make an Instant Answer by starting with any of our quick tutorials. All of these are self-contained and will get you up and running. Pick whatever looks most interesting! + +**Create a Cheat Sheet:** + +- [Programming Syntax](#) + + [Screenshot] + +**Build a Utility:** + +- [Conversions](#) + + [Screenshot] + +**Hack an API-Based Response:** + +- [Forum Lookup](#) + + [Screenshot] + +## Improve an Already Live Instant Answer + +We welcome new contributors to dive in and improve live Instant Answers. It's a great, hands-on way to learn how things work. + +We always make sure to identify "low-hanging fruit" for new community members to work on. You'll want to start by [setting up your development environment](#), and make use of the reference documentation. + +Goodie Instant Answers are self-contained Instant Answers that run on the DuckDuckGo server. + +- [Goodie Low Hanging Fruit](https://github.com/duckduckgo/zeroclickinfo-goodies/issues?q=is%3Aopen+is%3Aissue+label%3A%22Low-Hanging+Fruit%22) + +Spice Instant Answers can make external API calls: + +- [Spice Low Hanging Fruit](https://github.com/duckduckgo/zeroclickinfo-spice/issues?q=is%3Aopen+is%3Aissue+label%3A%22Low-Hanging+Fruit%22) + +Have a [favorite Instant Answer](http://duck.co/ia) that you want to make even better? Feel free to dive in. + +## Talk to the Community + +Want help? Need to think out loud? [Join us over on Slack!](mailto:QuackSlack@duckduckgo.com?subject=AddMe). + +We're a digital community, but real people - we frequently meet up to hack together. Check out our [global meetups](#). \ No newline at end of file diff --git a/duckduckhack/getting-started/setup_dev_environment.md b/duckduckhack/welcome/setup-dev-environment.md similarity index 51% rename from duckduckhack/getting-started/setup_dev_environment.md rename to duckduckhack/welcome/setup-dev-environment.md index 855a3aca3..d8c5771bf 100644 --- a/duckduckhack/getting-started/setup_dev_environment.md +++ b/duckduckhack/welcome/setup-dev-environment.md @@ -1,32 +1,31 @@ # Setting Up Your Development Environment -In order to get moving with Instant Answer development, you'll need to setup your environment. At the very minimum, you will need a GitHub account and the DuckDuckHack developer tool, DuckPAN, to make and submit an Instant Answer. This guide will help you determine the best setup for you. +In order to get moving with Instant Answer development, you'll need to setup your environment. There are three main steps: -Before moving forward, you **must** know which Instant Answer type you will be using. After this section, the documentation will be specific to each Instant Answer type. +1. Fork the Spice Repository on Github.com +2. Fork the DuckDuckGo environment on Codio.com +3. Clone your Github fork onto the Codio environment -## Before you start... +You can also watch a [video screencast of this tutorial](https://vimeo.com/132712266). -We highly recommend that everyone uses Codio, a web-based IDE that simplifies the setup and development process greatly. If you prefer using a local text editor, that's alright, but using Codio is still beneficial because we already have the required software installed and ready to go. This page will show you how to set up Codio and then it's on to creating Instant Answers! You can also watch a [video screencast of this tutorial](https://vimeo.com/132712266). +![https://vimeo.com/132712266](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckduckhack%2Fassets%2Fscreencast_environment-setup.jpg&f=1) -### Alternative Development Environments +## 1. Fork the Spice Repository on Github.com -Codio is the preferred choice of most DuckDuckHack contributors for its ease and speed. However, you may also [develop and test locally](https://duck.co/duckduckhack/other_development_environments), or install a [pre-configured virtual machine](https://duck.co/duckduckhack/other_development_environments). These options do require more time and effort than using Codio. +### Sign up for a GitHub Account -## Sign up for a GitHub Account +*Already have a GitHub Account? Perfect, move to the next step.* -*Already have a GitHub Account? Perfect, move on to [the next step](#sign-up-for-a-codio-account)!* +GitHub is a well known, popular tool that many individuals and companies use to save their code. Many open-source projects (such as DuckDuckHack) are hosted on GitHub and anyone with an account can contribute. GitHub is a great tool that you will likely be using long after this tutorial. -If you're new to programming, GitHub is a well known, popular tool that many individuals and companies use to save their code. Many open-source projects (such as DuckDuckHack) are hosted on GitHub and anyone with an account can contribute. GitHub is a great tool that you will likely be using long after this tutorial. To get started, let's sign up! +To get started, let's sign up: 1. Go to https://github.com/join and enter the required information, then click "**Create an Account**" 2. Click "**Finish Signup**" to continue with a **Free** GitHub account. -**Congrats!** You now have a GitHub account. +## 2. Fork the DuckDuckGo environment on Codio.com - -## Sign up for a Codio Account - -Next, you'll need to get an account for Codio: +### Sign up for a Codio Account *Already have a Codio Account? Perfect, move on to [the next step](#fork-the-duckduckhack-project-on-codio)!* @@ -36,67 +35,78 @@ Next, you'll need to get an account for Codio: 4. Click "**Authorize application**" to continue. 5. In the new screen, enter the required details and click "**Create Account**". -**Congrats!** You now have a Codio account. You'll notice that you didn't need to provide a password, that's because you've logged in to Codio using your GitHub account. As long as you can login to your GitHub account, you can also login to Codio. Now let's get started with setting up your Codio environment! +### Fork the DuckDuckHack Codio Machine -## Join the DuckDuckGo Organization +In this step we'll setup our virtual development computer with all the convenient development tools pre-installed. We've already created this for you on Codio, and all you need to do is make a copy for yourself. -1. **After logging into Codio,** [click this link](https://codio.com/p/signup?orgToken=Ax-OB3tU4sdNAG8axJBYcjNqR04) and you'll be added to our organization, which gives you a professional Codio setup free of charge. You should see a confirmation message at the bottom of your Codio screen after clicking. +1. **After logging into Codio,** [click this link](https://codio.com/p/signup?orgToken=Ax-OB3tU4sdNAG8axJBYcjNqR04) and you'll be added to our organization, which gives you a professional Codio setup free of charge. + You should see a confirmation message at the bottom of your Codio screen after clicking. -## Fork the DuckDuckHack Project on Codio +2. Go to https://codio.com/duckduckgo/duckduckhack and click "**Project**" at the top left corner. -1. Go to https://codio.com/duckduckgo/duckduckhack and click "**Project**" at the top left corner. -2. In the drop-down, select the "**Fork**" option. +3. In the drop-down, select the "**Fork**" option. ![Codio Fork](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckpan%2Fassets%2Fcodio_fork.png&f=1) -3. In the pop-up window, select the "**Box & Project**" option and click "**Continue**". +4. In the pop-up window, select the "**Box & Project**" option and click "**Continue**". ![Codio Fork Both](https://images.duckduckgo.com/iu/?u=https%3A%2F%2Fraw.githubusercontent.com%2Fduckduckgo%2Fduckduckgo-documentation%2Fmaster%2Fduckpan%2Fassets%2Fcodio_fork_both.png&f=1) -4. Wait a minute while the project forks... -5. You should now see a new window with three panes. It should say "**DuckDuckHack**" at the top of the left pane. +5. Wait a minute while the project forks... -**Congrats!** You've now successfully forked the DuckDuckHack environment. Now, let's grab the DuckDuckHack Goodie code (from GitHub.com) and start learning! +6. You should now see a new window with three panes. It should say "**DuckDuckHack**" at the top of the left pane. +Good work! You now have your own machine on Codio with all the DuckDuckHack development tools installed. You'll learn more about these tools in the tutorials. -## Fork the correct DuckDuckHack repository on GitHub +## 3. Clone your Github fork onto your Codio Machine -In order to test out and create your own Instant Answer, you'll need the open-source code that DuckDuckGo hosts on GitHub.com. We're now going to "fork" that code, so you'll have your own personal copy which you can modify. +The final step is to obtain the latest copy of the open-source code powering all Instant Answers. DuckDuckGo hosts this on GitHub.com. We're going to "fork" that code, so you'll have your own personal copy which you can modify and test. -By now you should have [determined the Instant Answer type](https://duck.co/duckduckhack/determine_your_instant_answer_type) you're going to build. - -1. Go to the Instant Answer repository homepage: - - [Goodies](https://github.com/duckduckgo/zeroclickinfo-goodies) (for Cheat Sheets and Instant Answers that are pure code functions) +1. Go to the corresponding Instant Answer repository homepage: + - [Goodies](https://github.com/duckduckgo/zeroclickinfo-goodies) (for Cheat Sheets, and Instant Answers that are pure code functions) - [Spice](https://github.com/duckduckgo/zeroclickinfo-spice) (for Instant Answers that will make API calls) - - [Fathead](https://github.com/duckduckgo/zeroclickinfo-fathead) (for Instant Answers that use key->value based data) - - [Longtail](https://github.com/duckduckgo/zeroclickinfo-longtail) (for Instant Answers that do full text search) + + Don't worry about picking the right one: you can fork either or both of these repositories - just repeat these steps. + 2. Do you see your user portrait or "**Sign up**" and "**Sign in**" buttons in the top right corner? + - **User Portrait**? Perfect. Move on to the next step. - **Sign up/in Buttons**? Click "**Sign In**", then enter your details and click "**Sign In**". - - **A portrait that's not mine**? Whoops! Click the portrait to see the username. If it's yours, you're good to go. If not, click Sign out, and then sign in with your username and password. + - **A portrait that's not mine**? Whoops! Click the portrait to see the username. If it's yours, you're good to go. + +If not, click Sign out, and then sign in with your username and password. + 3. Click "**Fork**", near the top-right corner. + 4. Wait while the repo forks... + 5. You should see a page that looks nearly identical to the repo home page you were just on. The URL should be different though, it should look like **`https://github.com/yourGitHubUsername/zeroclickinfo-xxxxx`**. This is the URL for your personal copy of the DuckDuckHack code. -6. **Keep this URL handy, we'll be using it in a minute!** +**Keep this URL handy, we'll be using it in a minute!** -## Clone your Repository onto your Codio Machine +## Clone your Github Repository onto your Codio Machine -Now we need to "clone" the code from GitHub to your Codio box so you can see it, modify it and run it! +Having forked the code on Github, we want to be able to make changes. We'll need to clone that code to our Codio machine in order to modify and run it. 1. Go to the [Codio projects page](https://codio.com/home/projects). - See a "**Sign In**" screen? Use the "**Sign in via GitHub**" method like you did before (see Step #2 [here](#sign-up-for-a-codio-account)). + 2. Click the "**DuckDuckHack**" project. + 3. You should now see the three-pane window we previously saw. Press **Ctrl+Alt+R** (Cmd+Alt+R on a Mac), this will improve the layout a bit. (You can also click *View->Layouts->Default* from the command bar at the top). + 4. Press **Shift+Alt+T** to open a new Terminal. (You can also click *Tools->Terminal* from the command bar at the top). You should see the right side pane change into a black command prompt. + 5. Type **`git clone .git`** into the Terminal, replacing `` accordingly. It should look something like this for a Goodie: ``` [04:30 PM codio@buffalo-pixel workspace {master}]$ git clone https://github.com/githubusername/zeroclickinfo-goodies.git ``` + + Enter your Github credentials as prompted. - _**If your Github password doesn't work**, you may need to enter a [Personal Access Token](https://github.com/settings/tokens) instead. Simply copy and paste your token from your [Github Settings](https://github.com/settings/tokens). (This happens if you have set up [Github's Two-Factor Authentication](https://github.com/blog/1614-two-factor-authentication) feature.)_ + _**If your Github password doesn't work**, you may need to enter a [Personal Access Token](https://github.com/settings/tokens) instead. Simply copy and paste your token from your [Github Settings](https://github.com/settings/tokens). (No worries - this happens if you have set up [Github's Two-Factor Authentication](https://github.com/blog/1614-two-factor-authentication) feature.)_ 6. Press "**Enter**". You should see the Terminal print out some text that looks like this: @@ -112,11 +122,13 @@ Now we need to "clone" the code from GitHub to your Codio box so you can see it, [04:30 PM codio@buffalo-pixel workspace {master}]$ ``` -7. The file tree on the left side should update. There should be a new "**zeroclickinfo-xxxxx**" directory, where "**xxxxx**" is whichever Instant Answer type you chose: Goodie, Spice, Fathead, or Longtail. +7. The file tree on the left side of Codio should update. You'll see a new "**zeroclickinfo-xxxxx**" directory (where "**xxxxx**" is whichever Instant Answer type you chose: Goodie, Spice, Fathead, or Longtail). 8. Change the current directory of the terminal by typing **`cd zeroclickinfo-xxxxx`**, where "**xxxxx**" is whichever Instant Answer type you chose: Goodie, Spice, Fathead, or Longtail. -9. [Optional, best practice] Create a new branch in your repository for this particular project. This lets you work on several ideas at one time and keep them separate. Create a new branch by typing **`git checkout -b branch_name`**. You can use any branch name you like. You must create a new branch for each of your contributions. You can switch between branches by using **`git checkout branch_name`**. +9. [Best practice, but not required] Create a new branch in your repository for this particular project. This lets you work on several contributions at one time - since you must create a separate branch for each contribution. + + Create a new branch by typing **`git checkout -b branch_name`**. You can use any branch name you like. You can switch between branches by using **`git checkout branch_name`**. **Congrats!** You've now cloned the DuckDuckHack code onto your Codio machine. You're now prepared to code your first Instant Answer!