From 0d3d62aa19faa245266b3312a2cbf57ec737b871 Mon Sep 17 00:00:00 2001 From: Bruno Skvorc Date: Thu, 11 Jun 2015 21:39:05 +0000 Subject: [PATCH 1/2] Adding 0.4 --- .gitignore | 1 + CHANGELOG.md | 5 + README.md | 16 +++ initializr-verekia-4.0.zip | Bin 0 -> 24481 bytes src/Abstracts/Api.php | 2 +- src/Api/Search.php | 157 ++++++++++++++++++++++++++ src/Diffbot.php | 16 +++ src/Entity/SearchInfo.php | 130 +++++++++++++++++++++ src/Factory/Entity.php | 29 +++-- tests/Api/CrawlTest.php | 28 ++--- tests/Api/SearchCustomMocksTest.php | 129 +++++++++++++++++++++ tests/Api/SearchTest.php | 132 ++++++++++++++++++++++ tests/DiffbotTest.php | 9 ++ tests/Factory/EntityTest.php | 10 -- tests/Mocks/Search/15-05-24/test.json | 66 +++++++++++ 15 files changed, 694 insertions(+), 36 deletions(-) create mode 100644 initializr-verekia-4.0.zip create mode 100644 src/Api/Search.php create mode 100644 src/Entity/SearchInfo.php create mode 100644 tests/Api/SearchCustomMocksTest.php create mode 100644 tests/Api/SearchTest.php create mode 100644 tests/Mocks/Search/15-05-24/test.json diff --git a/.gitignore b/.gitignore index 85dc704..ac89a8f 100644 --- a/.gitignore +++ b/.gitignore @@ -33,5 +33,6 @@ build composer.lock docs vendor +public index.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d65b77..604283a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ #Changelog All notable changes will be documented in this file +## 0.4 - June 11th, 2015 + +- [Feature] Added Search API +- [Feature] Added SearchInfo: apart from Entites in a regular EntityIterator as usual, the Search API returns a SearchInfo object, too. See README. + ## 0.3 - May 17th, 2015 ### Internal changes diff --git a/README.md b/README.md index 3804d5f..a01be4b 100644 --- a/README.md +++ b/README.md @@ -312,6 +312,22 @@ $url = $crawl->buildUrl(); $url->call(); ``` +## Search API + +The Search API is used to quickly search across data obtained through Bulk or Crawl API. + +```php +$diffbot = new Diffbot('my_token'); +$search = $diffbot->search('author:"Miles Johnson" AND type:article')->call(); + + +foreach ($search as $article) { + echo $article->getTitle(); +} +``` + +Use Search APIs `setCol` method to target a specific collection only - otherwise, all your token's collections are searched. + ## Testing Just run PHPUnit in the root folder of the cloned project. diff --git a/initializr-verekia-4.0.zip b/initializr-verekia-4.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..8557f5411ac2f93b1ad224d236f8ff6d3f32097b GIT binary patch literal 24481 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 F{{{Aty@LP% literal 0 HcmV?d00001 diff --git a/src/Abstracts/Api.php b/src/Abstracts/Api.php index 27b79fa..45977ba 100644 --- a/src/Abstracts/Api.php +++ b/src/Abstracts/Api.php @@ -97,7 +97,7 @@ public function buildUrl() { $url = rtrim($this->apiUrl, '/').'?'; - if (strcmp($url,'crawl') !== 0) { + if (strcmp($this->url,'crawl') !== 0) { // Add Token $url .= 'token=' . $this->diffbot->getToken(); diff --git a/src/Api/Search.php b/src/Api/Search.php new file mode 100644 index 0000000..2158b7f --- /dev/null +++ b/src/Api/Search.php @@ -0,0 +1,157 @@ +query = $q; + } + + /** + * Name of the collection (Crawlbot or Bulk API job name) to search. + * By default the search will operate on all of your token's collections. + * + * @param null|string $col + * @return $this + */ + public function setCol($col = null) + { + if ($col !== null) { + $this->otherOptions['col'] = $col; + } else { + unset($this->otherOptions['col']); + } + + return $this; + } + + /** + * Number of results to return. Default is 20. To return all results in + * the search, pass num=all. + * @param int $num + * @return $this + */ + public function setNum($num = 20) + { + if (!is_numeric($num) && $num !== self::SEARCH_ALL) { + throw new \InvalidArgumentException( + 'Argument can only be numeric or "all" to return all results.' + ); + } + $this->otherOptions['num'] = $num; + + return $this; + } + + /** + * Ordinal position of first result to return. (First position is 0.) + * Default is 0. + * @param int $start + * @return $this + */ + public function setStart($start = 0) + { + if (!is_numeric($start)) { + throw new \InvalidArgumentException( + 'Argument can only be numeric.' + ); + } + $this->otherOptions['start'] = $start; + + return $this; + } + + /** + * Builds out the URL string that gets requested once `call()` is called + * + * @return string + */ + public function buildUrl() + { + + $url = rtrim($this->apiUrl, '/') . '?'; + + // Add token + $url .= 'token=' . $this->diffbot->getToken(); + + // Add query + $url .= '&query=' . urlencode($this->query); + + // Add other options + foreach ($this->otherOptions as $option => $value) { + $url .= '&' . $option . '=' . $value; + } + + return $url; + } + + /** + * If you pass in `true`, you get back a SearchInfo object related to the + * last call. Keep in mind that passing in true before calling a default + * call() will implicitly call the call(), and then get the SearchInfo. + * + * So: + * + * $searchApi->call() // gets entities + * $searchApi->call(true) // gets SearchInfo about the executed query + * + * @todo: remove error avoidance when issue 12 is fixed: https://github.com/Swader/diffbot-php-client/issues/12 + * @param bool $info + * @return \Swader\Diffbot\Entity\EntityIterator|SearchInfo + */ + public function call($info = false) + { + if (!$info) { + $ei = parent::call(); + + set_error_handler(function() { /* ignore errors */ }); + $arr = $ei->getResponse()->json(['big_int_strings' => true]); + restore_error_handler(); + + unset($arr['request']); + unset($arr['objects']); + + $this->info = new SearchInfo($arr); + + return $ei; + } + + if ($info && !$this->info) { + $this->call(); + } + + return $this->info; + } +} \ No newline at end of file diff --git a/src/Diffbot.php b/src/Diffbot.php index 60da0bb..9d50070 100644 --- a/src/Diffbot.php +++ b/src/Diffbot.php @@ -4,6 +4,7 @@ use Swader\Diffbot\Api\Crawl; use Swader\Diffbot\Api\Custom; +use Swader\Diffbot\Api\Search; use Swader\Diffbot\Exceptions\DiffbotException; use Swader\Diffbot\Api\Product; use Swader\Diffbot\Api\Image; @@ -252,4 +253,19 @@ public function crawl($name = null, Api $api = null) return $api->registerDiffbot($this); } + /** + * Search query. + * @see https://www.diffbot.com/dev/docs/search/#query + * @param string $q + * @return Search + */ + public function search($q) + { + $api = new Search($q); + if (!$this->getHttpClient()) { + $this->setHttpClient(); + $this->setEntityFactory(); + } + return $api->registerDiffbot($this); + } } \ No newline at end of file diff --git a/src/Entity/SearchInfo.php b/src/Entity/SearchInfo.php new file mode 100644 index 0000000..c625891 --- /dev/null +++ b/src/Entity/SearchInfo.php @@ -0,0 +1,130 @@ +data['searchInfo']; + } + + /** + * Current UTC time as timestamp + * @return int + */ + public function getCurrentTimeUTC() + { + return (int)$this->data['currentTimeUTC']; + } + + /** + * Response time in milliseconds. Time it took to process the query on + * Diffbot's end. + * @return int + */ + public function getResponseTimeMS() + { + return (int)$this->data['responseTimeMS']; + } + + /** + * Number of results skipped for any reason + * @todo: find out why results might be omitted + * @return int + */ + public function getNumResultsOmitted() + { + return (int)$this->data['numResultsOmitted']; + } + + /** + * Number of skipped shards + * @todo: find out what shards are + * @return int + */ + public function getNumShardsSkipped() + { + return (int)$this->data['numShardsSkipped']; + } + + /** + * Total number of shards + * @todo: find out what shards are + * @return int + */ + public function getTotalShards() + { + return (int)$this->data['totalShards']; + } + + /** + * Total number of documents in collection. + * Should resemble the total number you got on the crawl job. + * @todo: find out why not identical + * @return int + */ + public function getDocsInCollection() + { + return (int)$this->data['docsInCollection']; + } + + /** + * Number of results that match - NOT the number of *returned* results! + * @return int + */ + public function getHits() + { + return (int)$this->data['hits']; + } + + /** + * Returns an assoc. array containing the following keys and example values: + * + + "fullQuery" => "type:json AND (author:\"Miles Johnson\" AND type:article)", + "queryLanguageAbbr" => "xx", + "queryLanguage" => "Unknown", + "terms" => [ + [ + "termNum" => 0, + "termStr" => "Miles Johnson", + "termFreq" => 2621376, + "termHash48" => 224575481707228, + "termHash64" => 4150001371756911641, + "prefixHash64" => 3732660069076179349 + ], + [ + "termNum" => 1, + "termStr" => "type:json", + "termFreq" => 2621664, + "termHash48" => 272064464231140, + "termHash64" => 9877301297136722857, + "prefixHash64" => 7586288672657224048 + ], + [ + "termNum" => 2, + "termStr" => "type:article", + "termFreq" => 524448, + "termHash48" => 210861560163398, + "termHash64" => 12449358332005671483, + "prefixHash64" => 7586288672657224048 + ] + ] + + * @todo: find out what hashes are, and to what the freq is relative + * @return array + */ + public function getQueryInfo() + { + return (array)$this->data['queryInfo']; + } + +} \ No newline at end of file diff --git a/src/Factory/Entity.php b/src/Factory/Entity.php index 707b386..65b2252 100644 --- a/src/Factory/Entity.php +++ b/src/Factory/Entity.php @@ -21,6 +21,7 @@ class Entity implements EntityFactory * Creates an appropriate Entity from a given Response * If no valid Entity can be found for typo of API, the Wildcard entity is selected * + * @todo: remove error avoidance when issue 12 is fixed: https://github.com/Swader/diffbot-php-client/issues/12 * @param Response $response * @return EntityIterator * @throws DiffbotException @@ -29,7 +30,10 @@ public function createAppropriateIterator(Response $response) { $this->checkResponseFormat($response); - $arr = $response->json(); + + set_error_handler(function() { /* ignore errors */ }); + $arr = $response->json(['big_int_strings' => true]); + restore_error_handler(); $objects = []; foreach ($arr['objects'] as $object) { @@ -47,27 +51,30 @@ public function createAppropriateIterator(Response $response) /** * Makes sure the Diffbot response has all the fields it needs to work properly * + * @todo: remove error avoidance when issue 12 is fixed: https://github.com/Swader/diffbot-php-client/issues/12 * @param Response $response * @throws DiffbotException */ protected function checkResponseFormat(Response $response) { - $arr = $response->json(); + set_error_handler(function() { /* ignore errors */ }); + $arr = $response->json(['big_int_strings' => true]); + restore_error_handler(); if (isset($arr['error'])) { - throw new DiffbotException('Diffbot returned error '.$arr['errorCode'].': '.$arr['error']); + throw new DiffbotException('Diffbot returned error ' . $arr['errorCode'] . ': ' . $arr['error']); } - if (!isset($arr['objects'])) { - throw new DiffbotException('Objects property missing - cannot extract entity values'); - } + $required = [ + 'objects' => 'Objects property missing - cannot extract entity values', + 'request' => 'Request property not found in response!' + ]; - if (!isset($arr['request'])) { - throw new DiffbotException('Request property not found in response!'); + foreach ($required as $k=>$v) { + if (!isset($arr[$k])) { + throw new DiffbotException($v); + } } - if (!isset($arr['request']['api'])) { - throw new DiffbotException('API property not found in request property of response!'); - } } } \ No newline at end of file diff --git a/tests/Api/CrawlTest.php b/tests/Api/CrawlTest.php index 3b11c82..d0af2e1 100644 --- a/tests/Api/CrawlTest.php +++ b/tests/Api/CrawlTest.php @@ -48,7 +48,7 @@ public function testBuildUrlArticleApi() { $api = $this->diffbot->createArticleAPI('crawl')->setDiscussion(false); - $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Farticle%3Ftoken%3Ddemo%26url%3Dcrawl%26discussion%3Dfalse'; + $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Farticle%3F%26discussion%3Dfalse'; $c = $this->diffbot->crawl('sitepoint_01', $api); $c->setSeeds(['http://sitepoint.com']); $this->assertEquals($expected, $c->buildUrl()); @@ -56,7 +56,7 @@ public function testBuildUrlArticleApi() public function testBuildUrlDefaultApi() { - $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; + $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; $c = $this->diffbot->crawl('sitepoint_01'); $c->setSeeds(['http://sitepoint.com']); $this->assertEquals($expected, $c->buildUrl()); @@ -72,7 +72,7 @@ public function testInvalidSeeds() public function testPatternSetters() { - $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&pageProcessPattern=class%3DarticleBody&urlCrawlPattern=%2Fcategory%2Fshoes||%21%2Fauthor%2F||%5Ehttp%3A%2F%2Fwww.diffbot.com||type%3Dproduct%24&urlProcessPattern=%2Fproduct%2Fdetail||%21%3Fcurrency%3Deuro&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; + $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&pageProcessPattern=class%3DarticleBody&urlCrawlPattern=%2Fcategory%2Fshoes||%21%2Fauthor%2F||%5Ehttp%3A%2F%2Fwww.diffbot.com||type%3Dproduct%24&urlProcessPattern=%2Fproduct%2Fdetail||%21%3Fcurrency%3Deuro&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; $c = $this->diffbot->crawl('sitepoint_01'); $c->setSeeds(['http://sitepoint.com']); @@ -90,7 +90,7 @@ public function testPatternSetters() public function testRegexSetters() { - $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&urlCrawlRegEx=/^[a-z0-9_-]{3,16}$/&urlProcessRegEx=/^#?([a-f0-9]{6}|[a-f0-9]{3})$/&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; + $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&urlCrawlRegEx=/^[a-z0-9_-]{3,16}$/&urlProcessRegEx=/^#?([a-f0-9]{6}|[a-f0-9]{3})$/&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; $c = $this->diffbot->crawl('sitepoint_01'); $c->setSeeds(['http://sitepoint.com']); @@ -119,7 +119,7 @@ public function maxHopsProvider() */ public function testMaxHops($input, $urlFragment) { - $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&maxHops=' . $urlFragment . '&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; + $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&maxHops=' . $urlFragment . '&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; $c = $this->diffbot->crawl('sitepoint_01'); $c->setSeeds(['http://sitepoint.com']); @@ -147,7 +147,7 @@ public function maxProvider() */ public function testMax($input, $urlFragment) { - $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&maxToCrawl=' . $urlFragment . '&maxToProcess=' . $urlFragment . '&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; + $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&maxToCrawl=' . $urlFragment . '&maxToProcess=' . $urlFragment . '&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; $c = $this->diffbot->crawl('sitepoint_01'); $c->setSeeds(['http://sitepoint.com']); @@ -182,7 +182,7 @@ public function notifyProviderOk() */ public function testNotify($input, $urlFragment) { - $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&' . $urlFragment . '&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; + $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&' . $urlFragment . '&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; $c = $this->diffbot->crawl('sitepoint_01'); $c->setSeeds(['http://sitepoint.com']); @@ -235,7 +235,7 @@ public function crawlDelayProviderOk() */ public function testCrawlOk($input, $urlFragment) { - $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&crawlDelay=' . $urlFragment . '&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; + $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&crawlDelay=' . $urlFragment . '&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; $c = $this->diffbot->crawl('sitepoint_01'); $c->setSeeds(['http://sitepoint.com']); @@ -285,7 +285,7 @@ public function repeatProviderOk() */ public function testRepeatOk($input, $urlFragment) { - $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&repeat=' . $urlFragment . '&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; + $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&repeat=' . $urlFragment . '&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; $c = $this->diffbot->crawl('sitepoint_01'); $c->setSeeds(['http://sitepoint.com']); @@ -319,8 +319,8 @@ public function testRepeatNotOk($input) public function testOnlyProcessIfNew() { - $expected1 = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&onlyProcessIfNew=1&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; - $expected2 = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&onlyProcessIfNew=0&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; + $expected1 = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&onlyProcessIfNew=1&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; + $expected2 = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&onlyProcessIfNew=0&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; $c = $this->diffbot->crawl('sitepoint_01'); $c->setSeeds(['http://sitepoint.com']); @@ -350,7 +350,7 @@ public function maxRoundsProvider() */ public function testMaxRounds($input, $urlFragment) { - $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&maxRounds=' . $urlFragment . '&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; + $expected = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&maxRounds=' . $urlFragment . '&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; $c = $this->diffbot->crawl('sitepoint_01'); $c->setSeeds(['http://sitepoint.com']); @@ -361,8 +361,8 @@ public function testMaxRounds($input, $urlFragment) public function testObeyRobots() { - $expected1 = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&obeyRobots=1&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; - $expected2 = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&obeyRobots=0&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3Ftoken%3Ddemo%26url%3Dcrawl%26mode%3Dauto'; + $expected1 = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&obeyRobots=1&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; + $expected2 = 'https://api.diffbot.com/v3/crawl?token=demo&name=sitepoint_01&seeds=http%3A%2F%2Fsitepoint.com&obeyRobots=0&apiUrl=https%3A%2F%2Fapi.diffbot.com%2Fv3%2Fanalyze%3F%26mode%3Dauto'; $c = $this->diffbot->crawl('sitepoint_01'); $c->setSeeds(['http://sitepoint.com']); diff --git a/tests/Api/SearchCustomMocksTest.php b/tests/Api/SearchCustomMocksTest.php new file mode 100644 index 0000000..4a805aa --- /dev/null +++ b/tests/Api/SearchCustomMocksTest.php @@ -0,0 +1,129 @@ +setEntityFactory(); + $fakeClient = new Client(); + $diffbot->setHttpClient($fakeClient); + $this->diffbot = $diffbot; + } + + public function resultCountProvider() + { + return [ + [ + [ + 'file' => '15-05-24/test.json', + 'q' => 'author:"Miles Johnson" AND type:article' + ], + ['results' => 8] + ] + ]; + } + + /** + * @dataProvider resultCountProvider + * @param $case + * @param $expectations + */ + public function testResultCount($case, $expectations) + { + $this->diffbot->getHttpClient()->getEmitter()->attach(new Mock( + [file_get_contents($this->mockPrefix . $case['file'])] + )); + + $search = $this->diffbot->search($case['q'])->call(); + + $this->assertEquals($expectations['results'], $search->count()); + } + + public function searchInfoProvider() + { + return [ + [ + [ + 'file' => '15-05-24/test.json', + 'q' => 'author:"Miles Johnson" AND type:article' + ], + [ + 'currentTimeUTC' => 1433609003, + 'responseTimeMS' => 112, + 'numResultsOmitted' => 0, + 'numShardsSkipped' => 0, + 'totalShards' => 32, + 'docsInCollection' => 94592, + 'hits' => 8, + 'queryInfo' => [ + "fullQuery" => "type:json AND (author:\"Miles Johnson\" AND type:article)", + "queryLanguageAbbr" => "xx", + "queryLanguage" => "Unknown", + "terms" => [ + [ + "termNum" => 0, + "termStr" => "Miles Johnson", + "termFreq" => 359328, + "termHash48" => 224575481707228, + "termHash64" => 4150001371756911641, + "prefixHash64" => 3732660069076179349 + ], + [ + "termNum" => 1, + "termStr" => "type:json", + "termFreq" => 524352, + "termHash48" => 272064464231140, + "termHash64" => 9877301297136722857, + "prefixHash64" => 7586288672657224048 + ], + [ + "termNum" => 2, + "termStr" => "type:article", + "termFreq" => 524448, + "termHash48" => 210861560163398, + "termHash64" => 12449358332005671483, + "prefixHash64" => 7586288672657224048 + ] + ] + + ] + ] + ] + ]; + } + + /** + * @dataProvider searchInfoProvider + * @param $case + * @param $expectations + */ + public function testSearchInfo($case, $expectations) + { + $this->markTestSkipped('Bugged due to JSONC: https://github.com/Swader/diffbot-php-client/issues/12'); + $this->diffbot->getHttpClient()->getEmitter()->attach(new Mock( + [file_get_contents($this->mockPrefix . $case['file'])] + )); + + $searchInfo = $this->diffbot->search($case['q'])->call(true); + + foreach ($expectations as $key => $value) { + $method = 'get'.ucfirst($key); + $this->assertEquals($value, $searchInfo->$method()); + } + } +} diff --git a/tests/Api/SearchTest.php b/tests/Api/SearchTest.php new file mode 100644 index 0000000..8fe7ff9 --- /dev/null +++ b/tests/Api/SearchTest.php @@ -0,0 +1,132 @@ +getEmitter()->attach($this->getValidMock()); + + $diffbot->setHttpClient($fakeClient); + $diffbot->setEntityFactory(); + + $this->diffbot = $diffbot; + } + + protected function getValidMock() + { + if (!$this->validMock) { + $this->validMock = new Mock( + [file_get_contents(__DIR__ . '/../Mocks/Search/15-05-24/test.json')] + ); + } + + return $this->validMock; + } + + public function urlFragmentsProvider() + { + return [ + [[], "https://api.diffbot.com/v3/search?token=demo&query=test"], + [ + ['col' => 'foo'], + "https://api.diffbot.com/v3/search?token=demo&query=test&col=foo" + ], + [ + ['col' => null], + "https://api.diffbot.com/v3/search?token=demo&query=test" + ], + [ + ['num' => 10], + "https://api.diffbot.com/v3/search?token=demo&query=test&num=10" + ], + [ + ['start' => 20], + "https://api.diffbot.com/v3/search?token=demo&query=test&start=20" + ], + [ + ['col' => 'foo', 'num' => 10], + "https://api.diffbot.com/v3/search?token=demo&query=test&col=foo&num=10" + ], + [ + ['col' => 'foo', 'num' => 10, 'start' => 20], + "https://api.diffbot.com/v3/search?token=demo&query=test&col=foo&num=10&start=20" + ], + [ + ['col' => 'foo', 'start' => 20], + "https://api.diffbot.com/v3/search?token=demo&query=test&col=foo&start=20" + ], + [ + ['num' => 10, 'start' => 20], + "https://api.diffbot.com/v3/search?token=demo&query=test&num=10&start=20" + ], + ]; + } + + /** + * @dataProvider urlFragmentsProvider + * @param $fragments + * @param $expected + */ + public function testBuildUrl($fragments, $expected) + { + /** @var Search $search */ + $search = $this->diffbot->search('test'); + + if (array_key_exists('col', $fragments)) { + $search->setCol($fragments['col']); + } + + if (array_key_exists('num', $fragments)) { + $search->setNum($fragments['num']); + } + + if (array_key_exists('start', $fragments)) { + $search->setStart($fragments['start']); + } + + $this->assertEquals($expected, $search->buildUrl()); + } + + public function urlFragmentInvalidProvider() + { + return [ + [['num' => 'foo']], + [['start' => 'foo']] + ]; + } + + /** + * @dataProvider urlFragmentInvalidProvider + * @param $fragments + */ + public function testBuildUrlNok($fragments) + { + /** @var Search $search */ + $search = $this->diffbot->search('test'); + + $this->setExpectedException('InvalidArgumentException'); + if (isset($fragments['num'])) { + $search->setNum($fragments['num']); + } + + if (isset($fragments['start'])) { + $search->setStart($fragments['start']); + } + + } +} diff --git a/tests/DiffbotTest.php b/tests/DiffbotTest.php index 22260d5..0533566 100644 --- a/tests/DiffbotTest.php +++ b/tests/DiffbotTest.php @@ -144,4 +144,13 @@ public function testCrawlCreation() ); } + public function testSearchCreation() + { + $bot = new Diffbot('token'); + $api = $bot->search('test'); + $this->assertInstanceOf( + 'Swader\Diffbot\Api\Search', $api + ); + } + } \ No newline at end of file diff --git a/tests/Factory/EntityTest.php b/tests/Factory/EntityTest.php index 69f4e2a..d925727 100644 --- a/tests/Factory/EntityTest.php +++ b/tests/Factory/EntityTest.php @@ -45,16 +45,6 @@ public function testMissingRequestFail() $this->ef->createAppropriateIterator($this->responseOk); } - public function testMissingApiFail() - { - $this->responseOk->setBody(Stream::factory(json_encode([ - 'objects' => 'foo', - 'request' => 'bar' - ]))); - $this->setExpectedException('Swader\Diffbot\Exceptions\DiffbotException'); - $this->ef->createAppropriateIterator($this->responseOk); - } - public function testProductEntityPass() { $this->responseOk->setBody(Stream::factory(json_encode([ diff --git a/tests/Mocks/Search/15-05-24/test.json b/tests/Mocks/Search/15-05-24/test.json new file mode 100644 index 0000000..d2a7e97 --- /dev/null +++ b/tests/Mocks/Search/15-05-24/test.json @@ -0,0 +1,66 @@ +HTTP/1.1 200 OK +Server: nginx/1.6.3 +Date: Sat, 06 Jun 2015 16:42:36 GMT +Content-Type: application/json;charset=utf-8 +Transfer-Encoding: chunked +Connection: keep-alive +Vary: Accept-Encoding +Access-Control-Allow-Origin: * + +{"request":{"num":20,"col":null,"start":0,"token":"xxxxxxxxxx","query":"author:\"Miles Johnson\" AND type:article"}, +"currentTimeUTC":1433609003, +"responseTimeMS":112, +"numResultsOmitted":0, +"numShardsSkipped":0, +"totalShards":32, +"docsInCollection":94592, +"hits":8, +"queryInfo":{ +"fullQuery":"type:json AND (author:\"Miles Johnson\" AND type:article)", +"queryLanguageAbbr":"xx", +"queryLanguage":"Unknown", +"terms":[ +{ +"termNum":0, +"termStr":"Miles Johnson", +"termFreq":359328, +"termHash48":224575481707228, +"termHash64":4150001371756911641, +"prefixHash64":3732660069076179349 +}, +{ +"termNum":1, +"termStr":"type:json", +"termFreq":524352, +"termHash48":272064464231140, +"termHash64":9877301297136722857, +"prefixHash64":7586288672657224048 +}, +{ +"termNum":2, +"termStr":"type:article", +"termFreq":524448, +"termHash48":210861560163398, +"termHash64":12449358332005671483, +"prefixHash64":7586288672657224048 +} +] +}, +"objects":[ +{"tags":[{"id":185108,"count":3,"prevalence":1.5,"label":"Cascading Style Sheets","uri":"http://dbpedia.org/resource/Cascading_Style_Sheets"},{"id":2468048,"count":2,"prevalence":1,"label":"WordPress","type":"Http://wikidata.dbpedia.org/resource/Q386724","uri":"http://dbpedia.org/resource/WordPress"},{"id":885421,"count":1,"prevalence":0.5,"label":"HTML5","uri":"http://dbpedia.org/resource/HTML5"}],"icon":"http://www.sitepoint.com/wp-content/themes/sitepoint/assets/images/apple-touch-icon-144x144-precomposed.png","text":"is a front-end framework that provides a collection of powerful state-based, role-specific user interface components and utility classes for the responsive, mobile, and modern web. It makes use of the latest and greatest in technology \u2014 HTML5 for semantics, CSS3 for animations and styles, Sass for CSS pre\nname this time, so I called it simply \"Devise Demo\": $ rails new DeviseDemo -T Rails 4.2.0 is used, but Devise is compatible with Rails 3 as well. Drop in some gems: Gemfile gem 'devise', '3.4.1' gem 'bootstrap-sass' bootstrap-sass is not relevant to the tutorial, but I\nMarch 26, 2015\n. A kissing cousin of JSFiddle is Codepen.io. It has many of the same features including Sass and Less support, but you have to add external libraries like jQuery via URLs rather than simple checkboxes. Codepen updates the live preview as you type, but JSFiddle requires you to click the \u201cRun\u201d button\nMarch 25, 2015\nthat you handle your CSS with SASS. If you're not familiar with these scripts and methods, this may be a little advanced for you. JointsWP JointsWP is a blank WordPress theme built from a combination of Foundation and WordPress. What I like about this boilerplate is that it makes it easier\nMarch 11, 2015\n\n \"2015 is the year everyone will move away from frameworks.\" I don\u2019t really know about that. I just wanted to link bait all of you. Jokes aside, have you noticed the subtle movement in the front-end scene where everyone is moving away from frameworks? For example, Susy, the Sass grid","pageUrl":"http://www.sitepoint.com/search/sass/page/7/","metaTags":[],"humanLanguage":"en","type":"article","date":"Tue, 24 Mar 2015 00:00:00 GMT","author":"Miles Johnson","title":"Toolkit: A Front-End Framework for the Modern Web","diffbotUri":"article|3|1990642558","nextPages":["http://www.sitepoint.com/search/sass/page/7/?s=sass&type=post&paged=8"],"images":[{"diffbotUri":"image|3|761444823","naturalHeight":710,"primary":true,"naturalWidth":710,"url":"http://www.sitepoint.com/wp-content/themes/sitepoint/assets/images/icon.sitepoint.png"}],"html":"
  • is a front-end framework that provides a collection of powerful state-based, role-specific user interface components and utility classes for the responsive, mobile, and modern web. It makes use of the latest and greatest in technology — HTML5 for semantics, CSS3 for animations and styles, Sass for CSS pre <\/li>\n
  • name this time, so I called it simply "Devise Demo": $ rails new DeviseDemo -T Rails 4.2.0 is used, but Devise is compatible with Rails 3 as well. Drop in some gems: Gemfile gem 'devise', '3.4.1' gem 'bootstrap-sass' bootstrap-sass is not relevant to the tutorial, but I

    March 26, 2015<\/p> <\/li>\n

  • . A kissing cousin of JSFiddle is Codepen.io. It has many of the same features including Sass and Less support, but you have to add external libraries like jQuery via URLs rather than simple checkboxes. Codepen updates the live preview as you type, but JSFiddle requires you to click the “Run” button

    March 25, 2015<\/p> <\/li>\n

  • that you handle your CSS with SASS. If you're not familiar with these scripts and methods, this may be a little advanced for you. JointsWP JointsWP is a blank WordPress theme built from a combination of Foundation and WordPress. What I like about this boilerplate is that it makes it easier

    March 11, 2015<\/p> <\/li>

    "2015 is the year everyone will move away from frameworks." I don’t really know about that. I just wanted to link bait all of you. Jokes aside, have you noticed the subtle movement in the front-end scene where everyone is moving away from frameworks? For example, Susy, the Sass grid<\/p>","numPages":2,"docId":79272968141,"gburl":"http://www.sitepoint.com/search/sass/page/7/-diffbotxyz3863447284","lastCrawlTimeUTC":1432108850,"timestamp":"Wed, 20 May 2015 08:00:50 GMT"} +, +{"tags":[{"id":185108,"count":3,"prevalence":1.5,"label":"Cascading Style Sheets","uri":"http://dbpedia.org/resource/Cascading_Style_Sheets"},{"id":2468048,"count":2,"prevalence":1,"label":"WordPress","type":"Http://wikidata.dbpedia.org/resource/Q386724","uri":"http://dbpedia.org/resource/WordPress"},{"id":885421,"count":1,"prevalence":0.5,"label":"HTML5","uri":"http://dbpedia.org/resource/HTML5"}],"icon":"http://www.sitepoint.com/wp-content/themes/sitepoint/assets/images/apple-touch-icon-144x144-precomposed.png","text":"is a front-end framework that provides a collection of powerful state-based, role-specific user interface components and utility classes for the responsive, mobile, and modern web. It makes use of the latest and greatest in technology \u2014 HTML5 for semantics, CSS3 for animations and styles, Sass for CSS pre\nname this time, so I called it simply \"Devise Demo\": $ rails new DeviseDemo -T Rails 4.2.0 is used, but Devise is compatible with Rails 3 as well. Drop in some gems: Gemfile gem 'devise', '3.4.1' gem 'bootstrap-sass' bootstrap-sass is not relevant to the tutorial, but I\nMarch 26, 2015\n. A kissing cousin of JSFiddle is Codepen.io. It has many of the same features including Sass and Less support, but you have to add external libraries like jQuery via URLs rather than simple checkboxes. Codepen updates the live preview as you type, but JSFiddle requires you to click the \u201cRun\u201d button\nMarch 25, 2015\nthat you handle your CSS with SASS. If you're not familiar with these scripts and methods, this may be a little advanced for you. JointsWP JointsWP is a blank WordPress theme built from a combination of Foundation and WordPress. What I like about this boilerplate is that it makes it easier\nMarch 11, 2015\n\n \"2015 is the year everyone will move away from frameworks.\" I don\u2019t really know about that. I just wanted to link bait all of you. Jokes aside, have you noticed the subtle movement in the front-end scene where everyone is moving away from frameworks? For example, Susy, the Sass grid\n\n. Lobotomized owls Getting deeper with CSS this week, we've been exploring axiomatic CSS and the Lobotomized Owl selector, * + *. Sass has also been covered, with scaling values across breakpoints using Sass, and the self-aware Sass mixin, and we now have a unit tester for Sass, which is going to get\n\n\nfree to use any of the functionality provided by Codepen including Sass, Prefixfree. Backward browser compatibility will be appreciated but is not central to our criteria Present your solutions as CodePen links posted to the comments below. You've got a week to submit your best ideas\nSass yet (shame on you! :). Maybe you want to become more familiar with a back-end technology, to complement your front-end stack. Or maybe it's a new JavaScript library or framework. Whatever it is, I\u2019d like to hear about it. But I\u2019ll go first. What I\u2019ve Neglected Here\u2019s a short list of some\nAugust 4, 2014\n, but the style is lacking. Let's add some nice style using Zurb Foundation. Step 2: Responsive Design with Foundation Foundation is a front-end framework similar to Twitter Bootstrap, it's built on SASS instead of LESS which can make it easier to integrate it into your Rails application\nAugust 1, 2014\nfor a Firefox-only audience. Too Little Too Late? Native CSS variables would have revolutionized our working lives had they been implemented a decade ago. Today, if you want to use variables in your stylesheets, you probably already are. Preprocessors such as Sass, Less, and Stylus provide variable\nJuly 25, 2014\n-organized file right away. It also makes it pretty simple to break this file down into several partials if you'd like to use Sass or another CSS pre-processor to maintain your CSS for your custom theme (that's what I'd do). Or if you prefer, there are some Sass-ready forks of _s available on Github\nJuly 24, 2014\n\n from a Fontello config.json file. SourceDemo 9. Slidebars A jQuery plugin for quickly and easily implementing app-style revealing, overlay and push menus and sidebars into your website. Source + Demo 10. Ridiculously Responsive Social Sharing Buttons RRSSB is built with SASS, so you can easily\n\n instead: gem 'rails', github: 'rails/rails' gem 'rails', '4.0.0' # Use sqlite3 as the database for Active Record gem 'sqlite3' # Use SCSS for stylesheets gem 'sass-rails', '~> 4.0.0' # Use Uglifier as compressor for JavaScript assets gem 'uglifier', '>= 1.3.0' # Use CoffeeScript\n\n\nBack in November I discussed various options for integrating Twitter Bootstrap into a Rails 3.1 app, including using the less-rails-bootstrap and bootstrap-sass gems. Then last month I wrote a follow up tutorial showing how to quickly create a working web site using Twitter Bootstrap\nPart 3 - Database Associations After those three parts, we have now finished most of the main functionality of our To Do List app. In this final part we are going to look at using Sass for the CSS before deploying the app on the Heroku cloud hosting service. A Logo Every self respecting app needs\nSeptember 16, 2011\n\n engines, like ERB and Haml. Sprockets Sprockets is new at Rails 3.1, providing the new asset packaging pipeline for javascript and coffeescript, as well as SASS and CSS. TZInfo TZInfo is a timezone library for Ruby. Well, this post is reaching the point where it can't be read on the train or waiting\n\n. Semantic Grid System\u2014a simple and easy to use page layout tool. It supports Fixed, Fluid, and Responsive layouts. And, by the way, you can use it with Sass and Stylus too (if you still haven't fallen in love with LESS). Centage\u2014if you need full fluidity and flexibility, you should","pageUrl":"http://www.sitepoint.com/?s=sass&type=post&paged=7","metaTags":[],"humanLanguage":"en","type":"article","date":"Tue, 24 Mar 2015 00:00:00 GMT","author":"Miles Johnson","title":"Toolkit: A Front-End Framework for the Modern Web","diffbotUri":"article|3|1094100739","nextPages":["http://www.sitepoint.com/?s=sass&type=post&paged=8","http://www.sitepoint.com/?s=sass&type=post&paged=9","http://www.sitepoint.com/?s=sass&type=post&paged=10","http://www.sitepoint.com/?s=sass&type=post&paged=11","http://www.sitepoint.com/?s=sass&type=post&paged=12","http://www.sitepoint.com/?s=sass&type=post&paged=13","http://www.sitepoint.com/?s=sass&type=post&paged=14","http://www.sitepoint.com/?s=sass&type=post&paged=15"],"images":[{"diffbotUri":"image|3|761444823","naturalHeight":710,"primary":true,"naturalWidth":710,"url":"http://www.sitepoint.com/wp-content/themes/sitepoint/assets/images/icon.sitepoint.png"}],"html":"

  • is a front-end framework that provides a collection of powerful state-based, role-specific user interface components and utility classes for the responsive, mobile, and modern web. It makes use of the latest and greatest in technology — HTML5 for semantics, CSS3 for animations and styles, Sass for CSS pre <\/li>\n
  • name this time, so I called it simply "Devise Demo": $ rails new DeviseDemo -T Rails 4.2.0 is used, but Devise is compatible with Rails 3 as well. Drop in some gems: Gemfile gem 'devise', '3.4.1' gem 'bootstrap-sass' bootstrap-sass is not relevant to the tutorial, but I

    March 26, 2015<\/p> <\/li>\n

  • . A kissing cousin of JSFiddle is Codepen.io. It has many of the same features including Sass and Less support, but you have to add external libraries like jQuery via URLs rather than simple checkboxes. Codepen updates the live preview as you type, but JSFiddle requires you to click the “Run” button

    March 25, 2015<\/p> <\/li>\n

  • that you handle your CSS with SASS. If you're not familiar with these scripts and methods, this may be a little advanced for you. JointsWP JointsWP is a blank WordPress theme built from a combination of Foundation and WordPress. What I like about this boilerplate is that it makes it easier

    March 11, 2015<\/p> <\/li>

    "2015 is the year everyone will move away from frameworks." I don’t really know about that. I just wanted to link bait all of you. Jokes aside, have you noticed the subtle movement in the front-end scene where everyone is moving away from frameworks? For example, Susy, the Sass grid<\/p>

    . Lobotomized owls Getting deeper with CSS this week, we've been exploring axiomatic CSS and the Lobotomized Owl selector, * + *. Sass has also been covered, with scaling values across breakpoints using Sass, and the self-aware Sass mixin, and we now have a unit tester for Sass, which is going to get<\/p>

  • free to use any of the functionality provided by Codepen including Sass, Prefixfree. Backward browser compatibility will be appreciated but is not central to our criteria Present your solutions as CodePen links posted to the comments below. You've got a week to submit your best ideas <\/li>\n
  • Sass yet (shame on you! :). Maybe you want to become more familiar with a back-end technology, to complement your front-end stack. Or maybe it's a new JavaScript library or framework. Whatever it is, I’d like to hear about it. But I’ll go first. What I’ve Neglected Here’s a short list of some

    August 4, 2014<\/p> <\/li>\n

  • , but the style is lacking. Let's add some nice style using Zurb Foundation. Step 2: Responsive Design with Foundation Foundation is a front-end framework similar to Twitter Bootstrap, it's built on SASS instead of LESS which can make it easier to integrate it into your Rails application

    August 1, 2014<\/p> <\/li>\n

  • for a Firefox-only audience. Too Little Too Late? Native CSS variables would have revolutionized our working lives had they been implemented a decade ago. Today, if you want to use variables in your stylesheets, you probably already are. Preprocessors such as Sass, Less, and Stylus provide variable

    July 25, 2014<\/p> <\/li>\n

  • -organized file right away. It also makes it pretty simple to break this file down into several partials if you'd like to use Sass or another CSS pre-processor to maintain your CSS for your custom theme (that's what I'd do). Or if you prefer, there are some Sass-ready forks of _s available on Github

    July 24, 2014<\/p> <\/li>

    from a Fontello config.json file. SourceDemo 9. Slidebars A jQuery plugin for quickly and easily implementing app-style revealing, overlay and push menus and sidebars into your website. Source + Demo 10. Ridiculously Responsive Social Sharing Buttons RRSSB is built with SASS, so you can easily<\/p>

    instead: gem 'rails', github: 'rails/rails' gem 'rails', '4.0.0' # Use sqlite3 as the database for Active Record gem 'sqlite3' # Use SCSS for stylesheets gem 'sass-rails', '~> 4.0.0' # Use Uglifier as compressor for JavaScript assets gem 'uglifier', '>= 1.3.0' # Use CoffeeScript<\/p>

    1. Back in November I discussed various options for integrating Twitter Bootstrap into a Rails 3.1 app, including using the less-rails-bootstrap and bootstrap-sass gems. Then last month I wrote a follow up tutorial showing how to quickly create a working web site using Twitter Bootstrap <\/li>
    2. Part 3 - Database Associations After those three parts, we have now finished most of the main functionality of our To Do List app. In this final part we are going to look at using Sass for the CSS before deploying the app on the Heroku cloud hosting service. A Logo Every self respecting app needs

      September 16, 2011<\/p> <\/li> <\/ol>

      engines, like ERB and Haml. Sprockets Sprockets is new at Rails 3.1, providing the new asset packaging pipeline for javascript and coffeescript, as well as SASS and CSS. TZInfo TZInfo is a timezone library for Ruby. Well, this post is reaching the point where it can't be read on the train or waiting<\/p>

      . Semantic Grid System—a simple and easy to use page layout tool. It supports Fixed, Fluid, and Responsive layouts. And, by the way, you can use it with Sass and Stylus too (if you still haven't fallen in love with LESS). Centage—if you need full fluidity and flexibility, you should<\/p>","numPages":9,"docId":148713717736,"gburl":"http://www.sitepoint.com/?s=sass&type=post&paged=7-diffbotxyz3863447284","lastCrawlTimeUTC":1432105732,"timestamp":"Wed, 20 May 2015 07:08:52 GMT"} +, +{"tags":[{"id":185108,"count":2,"prevalence":0.75,"label":"Cascading Style Sheets","uri":"http://dbpedia.org/resource/Cascading_Style_Sheets"},{"id":1678494,"count":1,"prevalence":0.375,"label":"ECMAScript","type":"Http://www.ontologydesignpatterns.org/ont/dul/DUL.owl#InformationEntity","uri":"http://dbpedia.org/resource/ECMAScript"},{"id":915685,"count":1,"prevalence":0.375,"label":"JavaScript","type":"Http://www.ontologydesignpatterns.org/ont/dul/DUL.owl#InformationEntity","uri":"http://dbpedia.org/resource/JavaScript"},{"id":5232974,"count":1,"prevalence":0.375,"label":"HipHop Virtual Machine","type":"Http://wikidata.dbpedia.org/resource/Q386724","uri":"http://dbpedia.org/resource/HipHop_Virtual_Machine"},{"id":80318,"count":1,"prevalence":0.375,"label":"HTML","uri":"http://dbpedia.org/resource/HTML"}],"icon":"http://www.sitepoint.com/wp-content/themes/sitepoint/assets/images/apple-touch-icon-144x144-precomposed.png","author":"Miles Johnson","text":"Miles is a full-stack web developer who loves to dabble in PHP, Hack, JavaScript, CSS, and HTML, among a handful of other technologies. As the lead developer behind the Titon Project, his primary focus is a fully featured back-end Hack Framework that runs on HHVM, and an ES6 and CSS3 based front-end user interface Toolkit.","title":"Miles Johnson","diffbotUri":"article|3|2048407968","pageUrl":"http://www.sitepoint.com/author/mjohnson/","metaTags":[],"images":[{"diffbotUri":"image|3|761444823","naturalHeight":710,"primary":true,"naturalWidth":710,"url":"http://www.sitepoint.com/wp-content/themes/sitepoint/assets/images/icon.sitepoint.png"}],"humanLanguage":"en","html":"

      Miles is a full-stack web developer who loves to dabble in PHP, Hack, JavaScript, CSS, and HTML, among a handful of other technologies. As the lead developer behind the Titon Project, his primary focus is a fully featured back-end Hack Framework that runs on HHVM, and an ES6 and CSS3 based front-end user interface Toolkit.<\/p>","date":"Tue, 24 Mar 2015 00:00:00 GMT","type":"article","docId":169820131,"gburl":"http://www.sitepoint.com/author/mjohnson/-diffbotxyz705685266","lastCrawlTimeUTC":1432072119,"timestamp":"Tue, 19 May 2015 21:48:39 GMT"} +, +{"tags":[{"id":185108,"count":2,"prevalence":0.75,"label":"Cascading Style Sheets","uri":"http://dbpedia.org/resource/Cascading_Style_Sheets"},{"id":1678494,"count":1,"prevalence":0.375,"label":"ECMAScript","type":"Http://www.ontologydesignpatterns.org/ont/dul/DUL.owl#InformationEntity","uri":"http://dbpedia.org/resource/ECMAScript"},{"id":915685,"count":1,"prevalence":0.375,"label":"JavaScript","type":"Http://www.ontologydesignpatterns.org/ont/dul/DUL.owl#InformationEntity","uri":"http://dbpedia.org/resource/JavaScript"},{"id":5232974,"count":1,"prevalence":0.375,"label":"HipHop Virtual Machine","type":"Http://wikidata.dbpedia.org/resource/Q386724","uri":"http://dbpedia.org/resource/HipHop_Virtual_Machine"},{"id":80318,"count":1,"prevalence":0.375,"label":"HTML","uri":"http://dbpedia.org/resource/HTML"}],"icon":"http://www.sitepoint.com/wp-content/themes/sitepoint/assets/images/apple-touch-icon-144x144-precomposed.png","author":"Miles Johnson","text":"Miles is a full-stack web developer who loves to dabble in PHP, Hack, JavaScript, CSS, and HTML, among a handful of other technologies. As the lead developer behind the Titon Project, his primary focus is a fully featured back-end Hack Framework that runs on HHVM, and an ES6 and CSS3 based front-end user interface Toolkit.","title":"Miles Johnson","diffbotUri":"article|3|2048407968","pageUrl":"http://www.sitepoint.com/author/mjohnson/","metaTags":[],"images":[{"diffbotUri":"image|3|761444823","naturalHeight":710,"primary":true,"naturalWidth":710,"url":"http://www.sitepoint.com/wp-content/themes/sitepoint/assets/images/icon.sitepoint.png"}],"humanLanguage":"en","html":"

      Miles is a full-stack web developer who loves to dabble in PHP, Hack, JavaScript, CSS, and HTML, among a handful of other technologies. As the lead developer behind the Titon Project, his primary focus is a fully featured back-end Hack Framework that runs on HHVM, and an ES6 and CSS3 based front-end user interface Toolkit.<\/p>","date":"Tue, 24 Mar 2015 00:00:00 GMT","type":"article","docId":169820131,"gburl":"http://www.sitepoint.com/author/mjohnson/-diffbotxyz705685266","lastCrawlTimeUTC":1432161978,"timestamp":"Wed, 20 May 2015 22:46:18 GMT"} +, +{"tags":[{"id":915685,"count":11,"prevalence":1.064516129032258,"label":"JavaScript","type":"Http://www.ontologydesignpatterns.org/ont/dul/DUL.owl#InformationEntity","uri":"http://dbpedia.org/resource/JavaScript"},{"id":185108,"count":10,"prevalence":0.967741935483871,"label":"Cascading Style Sheets","uri":"http://dbpedia.org/resource/Cascading_Style_Sheets"},{"id":80318,"count":4,"prevalence":0.3870967741935484,"label":"HTML","uri":"http://dbpedia.org/resource/HTML"},{"id":885421,"count":1,"prevalence":0.0967741935483871,"label":"HTML5","uri":"http://dbpedia.org/resource/HTML5"},{"id":1053273,"count":1,"prevalence":0.0967741935483871,"label":"MooTools","type":"Http://wikidata.dbpedia.org/resource/Q386724","uri":"http://dbpedia.org/resource/MooTools"}],"icon":"http://www.sitepoint.com/wp-content/themes/sitepoint/assets/images/apple-touch-icon-144x144-precomposed.png","author":"Miles Johnson","text":"Titon Toolkit, or simply Toolkit, is a project that I\u2019ve been working on in my free time for the past 4 years. It started out as a MooTools UI framework, which slowly transitioned to jQuery, with plans to be vendorless for 3.0. So why did I write another framework? At its inception, the world of \u201cCSS/JavaScript frameworks\u201d was still young, with Bootstrap and Foundation being about a year old. I was intrigued with the concept of a front-end framework and set out to build my own, with the main selling point being customizability and extensibility.\nSo, what is Toolkit exactly? Toolkit is a front-end framework that provides a collection of powerful state-based, role-specific user interface components and utility classes for the responsive, mobile, and modern web. It makes use of the latest and greatest in technology \u2014 HTML5 for semantics, CSS3 for animations and styles, Sass for CSS pre-processing, Gulp for task and package management, and powerful new browser APIs for the JavaScript layer, just to name a few.\nThe core of Toolkit is based on strict but important design principles, which include responsive design, mobile-first design, semantic markup, progressive enhancement, graceful degradation, continuous integration, and configuration over convention. These principles ultimately shape the decisions behind Toolkit.\nSo, is Toolkit just another front-end UI framework? Yes but, as mentioned, with some key differences: Toolkit was built to be extremely extensible, easily customizable, and efficiently architected.\nLet\u2019s look at some of its unique features.\nDecoupled JavaScript, CSS, and HTML\nA running paradigm in front-end development is tying JavaScript to a fixed CSS structure via class names as well as a fixed HTML structure. Toolkit disagrees with this approach and strives to decouple the CSS, JavaScript, and HTML as much as possible, which opens up the possibility of customizable alternatives. Toolkit mitigates many coupling issues by requiring specific data attributes \u2014 all of which are used for element traversal, look-up, and event binding.\nThe following example uses Toolkit\u2019s carousel component as a proof of concept.\nWith decoupling in place, custom HTML is now possible, which isn\u2019t the case when using alternative frameworks. No longer is the markup tied to the JavaScript component; the JavaScript component is now tied to the markup via data-* attributes. Want to add new markup to a component? Go for it! Want to change the markup to match the project? Feel free! Want to remove component functionality? Remove away! Data attributes provide what we think is a much better level of customization.\nEasier CSS Styling\nTired of having to overwrite styles? Or dealing with bloat? Toolkit sure was. Because of this, the CSS found in Toolkit is extremely lightweight as it only defines the very bare minimum for the component to function correctly \u2014 mainly layout and structural styles. You could say that Toolkit is a themeless and styleless front-end framework. By being themeless, Toolkit is easy to style, and even easier to integrate.\nFurthermore, Toolkit opted out of providing Sass variables for CSS theme customization (e.g. for border size, background color, text size, font family, etc). If you want to style an element, you can do so the old fashioned way, using CSS (or Sass, or Less)! You can also take this a step further by integrating Toolkit as a Compass extension, which allows for Toolkit\u2019s Sass files to be imported into scope and compiled directly in your own Sass files.\nCustomizable CSS Class Names\nAnother pain point found in existing frameworks is CSS class name collision. Toolkit resolves this issue in one of two ways.\nThe first way is through customizable Sass variables that allow most CSS class names to be customized. Using this approach will require compilation of the Sass files, either through a Compass extension, or in the source directly.\nThe second approach allows for global namespacing by prefixing classes. This works wonders when integrating the framework into an existing codebase where collisions are abundant. Enabling namespaces is as easy as modifying a Sass variable and a JavaScript property.\nDo note, however, that namespaces are not applied to state, animation, or behavioral class names.\nExtensible JavaScript\nThe entire JavaScript layer in Toolkit is built around a flexible inheritance based object-oriented class system. Each class manages its own state, events, and behaviors, which allow for complex interactions as each instance is unique. Since this class layer is so flexible, it allows for custom classes to be written, or existing classes to be extended via inheritance.\nOn top of this, each class supports a set of options for customizability. These options can be set globally, through the constructor, or as data attributes. Option groups and even responsive options are built into the core.\nFlexbox Support\nAlthough experimental, Toolkit offers built-in flexbox support through the Flex component. The Flex component shines in the building of layout and grid based structures through the concept of regions and blocks. A region is an object that contains blocks or other regions, while a block is an object that contains content and is aligned within the main and cross axis. Although being analogous to rows and columns, regions and blocks are packaged with additional support for growing, shrinking, ordering, wrapping, nesting, alignment, and responsiveness.\nFeature Packed\nBesides the highlights already mentioned, Toolkit supports an array of features that include:\nIf you\u2019d like to test out some of Toolkit\u2019s components, you can visit this interactive demo page.\nDown the Pipeline\nThe JavaScript ecosphere is constantly evolving with new technology, functionality, and specifications. Toolkit aims to be a part of this evolution by continuously staying in sync with the latest JavaScript developments. The roadmap as it currently stands includes the following breaking, but interesting, changes for the next 3.0 major release, some of which have already started development.\nTarget evergreen browsers and their previous 3 releases.\nRemove jQuery as a dependency and polyfill any functionality not present.\nRewrite the JavaScript layer using ECMAScript 6 functionality like classes, arrow functions, modules, generators, promises, and more.\nIntegrate Babel for ES6 -> ES5 compilation.\nIntegrate template rendering instead of DOM manipulation.\nLook into using webpack as the bundler and build tool.\nAdd Less support by integrating a Sass to Less transpiler.\nRewrite components using a flux-based uni-directional data flow system\nThere\u2019s also some discussion about integrating with external frameworks, but this is currently under RFC.\nPolyfill integration for missing browser features\nCustom web components through Polymer or another service\nReact and Toolkit component integration\nWhy not help my work on Toolkit by offering some advice on the direction it should take? Community feedback and contributions are very much appreciated! I\u2019m always looking for new team members and contributors, so if you\u2019re interested, come chat in #titon on freenode.net.\nIn Closing\nIt\u2019s been a wonderful experience showcasing Toolkit and its features to all of you. I hope you enjoyed it and find as much use out of Toolkit as I have. If you\u2019re looking for any more information on Toolkit, I suggest visiting the official website, our Twitter account, or the GitHub repo. Cheers!","title":"Toolkit: A Front-End Framework for the Modern Web","diffbotUri":"article|3|-839743441","pageUrl":"http://www.sitepoint.com/?p=101740","metaTags":[{"name":"css framework"},{"name":"css toolkit"},{"name":"frontend framework"},{"name":"LouisL"}],"images":[{"title":"Titon Toolkit website","height":487,"diffbotUri":"image|3|-166182215","naturalHeight":500,"width":780,"primary":true,"naturalWidth":800,"url":"http://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2015/03/1426722294titon-toolkit.png"}],"humanLanguage":"en","html":"

      Titon Toolkit<\/a>, or simply Toolkit, is a project that I’ve been working on in my free time for the past 4 years. It started out as a MooTools UI framework, which slowly transitioned to jQuery, with plans to be vendorless for 3.0. So why did I write another framework? At its inception, the world of “CSS/JavaScript frameworks” was still young, with Bootstrap and Foundation being about a year old. I was intrigued with the concept of a front-end framework and set out to build my own, with the main selling point being customizability and extensibility.<\/p>\n

      So, what is Toolkit exactly? Toolkit is a front-end framework that provides a collection of powerful state-based, role-specific user interface components and utility classes for the responsive, mobile, and modern web. It makes use of the latest and greatest in technology — HTML5 for semantics, CSS3 for animations and styles, Sass for CSS pre-processing, Gulp for task and package management, and powerful new browser APIs for the JavaScript layer, just to name a few.<\/p>\n

      \"Titon<\/img><\/a>
      Titon Toolkit website<\/a><\/figcaption><\/figure>\n

      The core of Toolkit is based on strict but important design principles, which include responsive design, mobile-first design, semantic markup, progressive enhancement, graceful degradation, continuous integration, and configuration over convention. These principles ultimately shape the decisions behind Toolkit.<\/p>\n

      So, is Toolkit just another front-end UI framework? Yes but, as mentioned, with some key differences: Toolkit was built to be extremely extensible, easily customizable, and efficiently architected<\/strong>.<\/p>\n

      Let’s look at some of its unique features.<\/p>\n

      Decoupled JavaScript, CSS, and HTML<\/h2>\n

      A running paradigm in front-end development is tying JavaScript to a fixed CSS structure via class names as well as a fixed HTML structure. Toolkit disagrees with this approach and strives to decouple the CSS, JavaScript, and HTML as much as possible, which opens up the possibility of customizable alternatives. Toolkit mitigates many coupling issues by requiring specific data attributes — all of which are used for element traversal, look-up, and event binding.<\/p>\n

      The following example uses Toolkit’s carousel component<\/a> as a proof of concept.<\/p>\n

      With decoupling in place, custom HTML is now possible, which isn’t the case when using alternative frameworks. No longer is the markup tied to the JavaScript component; the JavaScript component is now tied to the markup via data-*<\/code> attributes. Want to add new markup to a component? Go for it! Want to change the markup to match the project? Feel free! Want to remove component functionality? Remove away! Data attributes provide what we think is a much better level of customization.<\/p>\n

      Easier CSS Styling<\/h2>\n

      Tired of having to overwrite styles? Or dealing with bloat? Toolkit sure was. Because of this, the CSS found in Toolkit is extremely lightweight as it only defines the very bare minimum for the component to function correctly — mainly layout and structural styles. You could say that Toolkit is a themeless and styleless front-end framework. By being themeless, Toolkit is easy to style, and even easier to integrate.<\/p>\n

      Furthermore, Toolkit opted out of providing Sass variables for CSS theme customization (e.g. for border size, background color, text size, font family, etc). If you want to style an element, you can do so the old fashioned way, using CSS (or Sass, or Less)! You can also take this a step further by integrating Toolkit as a Compass extension, which allows for Toolkit’s Sass files to be imported into scope and compiled directly in your own Sass files.<\/p>\n

      Customizable CSS Class Names<\/h2>\n

      Another pain point found in existing frameworks is CSS class name collision. Toolkit resolves this issue in one of two ways.<\/p>\n

      The first way is through customizable Sass variables that allow most CSS class names to be customized. Using this approach will require compilation of the Sass files, either through a Compass extension, or in the source directly.<\/p>\n

      The second approach allows for global namespacing by prefixing classes. This works wonders when integrating the framework into an existing codebase where collisions are abundant. Enabling namespaces is as easy as modifying a Sass variable and a JavaScript property.<\/p>\n

      Do note, however, that namespaces are not applied to state, animation, or behavioral class names.<\/p>\n

      Extensible JavaScript<\/h2>\n

      The entire JavaScript layer in Toolkit is built around a flexible inheritance based object-oriented class system<\/a>. Each class manages its own state, events, and behaviors, which allow for complex interactions as each instance is unique. Since this class layer is so flexible, it allows for custom classes to be written, or existing classes to be extended via inheritance.<\/p>\n

      On top of this, each class supports a set of options for customizability<\/a>. These options can be set globally, through the constructor, or as data attributes<\/a>. Option groups and even responsive options are built into the core.<\/p>\n

      Flexbox Support<\/h2>\n

      Although experimental, Toolkit offers built-in flexbox support through the Flex component<\/a>. The Flex component shines in the building of layout and grid based structures through the concept of regions and blocks. A region is an object that contains blocks or other regions, while a block is an object that contains content and is aligned within the main and cross axis. Although being analogous to rows and columns, regions and blocks are packaged with additional support for growing, shrinking, ordering, wrapping, nesting, alignment, and responsiveness.<\/p>\n

      Feature Packed<\/h2>\n

      Besides the highlights already mentioned, Toolkit supports an array of features that include:<\/p>\n

      If you’d like to test out some of Toolkit’s components, you can visit this interactive demo page<\/a>.<\/p>\n

      Down the Pipeline<\/h2>\n

      The JavaScript ecosphere is constantly evolving with new technology, functionality, and specifications. Toolkit aims to be a part of this evolution by continuously staying in sync with the latest JavaScript developments. The roadmap as it currently stands includes the following breaking, but interesting, changes for the next 3.0 major release, some of which have already started development<\/a>.<\/p>\n