Skip to content

Commit

Permalink
Yaml configuration files support added. See changelog for details.
Browse files Browse the repository at this point in the history
  • Loading branch information
sleeping-owl committed Oct 25, 2014
1 parent 1256f7e commit 6fd0992
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 5 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
CHANGELOG
=========

2014-10-25, v1.2.0
------------------

* Updated filter chain method: each() also can be used without any arguments, return array of nodes
* Filter chain methods can be applied to array: ->each()->text()->mb_strtoupper() will return array of uppercase strings
* Yaml configuration files support added. For details see [documentation](http://sleeping-owl-apist.gopagoda.com/documentation#yaml-configuration)
* Now you can initialize api from yaml file without writing your own classes using Apist::fromYaml($file) method

2014-10-24, v1.1.0
------------------

Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"php": ">=5.4.0",
"guzzlehttp/guzzle": "5.*",
"symfony/css-selector": "2.5.*",
"symfony/dom-crawler": "2.5.*"
"symfony/dom-crawler": "2.5.*",
"symfony/yaml": "~2.0"
},
"require-dev": {
"phpunit/phpunit": "4.*",
Expand Down
29 changes: 28 additions & 1 deletion src/SleepingOwl/Apist/Apist.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use GuzzleHttp\Client;
use SleepingOwl\Apist\Methods\ApistMethod;
use SleepingOwl\Apist\Selectors\ApistSelector;
use SleepingOwl\Apist\Yaml\YamlApist;
use Symfony\Component\DomCrawler\Crawler;

abstract class Apist
Expand Down Expand Up @@ -56,6 +57,17 @@ public static function filter($cssSelector)
return new ApistSelector($cssSelector);
}

/**
* Initialize api from yaml configuration file
*
* @param $file
* @return YamlApist
*/
public static function fromYaml($file)
{
return new YamlApist($file, []);
}

/**
* @return ApistMethod
*/
Expand All @@ -72,6 +84,14 @@ public function getBaseUrl()
return $this->baseUrl;
}

/**
* @param string $baseUrl
*/
public function setBaseUrl($baseUrl)
{
$this->baseUrl = $baseUrl;
}

/**
* @param $httpMethod
* @param $url
Expand Down Expand Up @@ -225,9 +245,16 @@ public function then($node, $blueprint)
* @param $blueprint
* @return mixed
*/
public function each(Crawler $node, $blueprint)
public function each(Crawler $node, $blueprint = null)
{
$callback = $blueprint;
if (is_null($callback))
{
$callback = function ($node)
{
return $node;
};
}
if ( ! is_callable($callback))
{
$callback = function ($node) use ($blueprint)
Expand Down
16 changes: 13 additions & 3 deletions src/SleepingOwl/Apist/Selectors/ApistSelector.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ public function getValue(ApistMethod $method, Crawler $rootNode = null)
*/
function __call($name, $arguments)
{
$resultCallback = new ResultCallback($name, $arguments);
$this->resultMethodChain[] = $resultCallback;
return $this;
return $this->addCallback($name, $arguments);
}

/**
Expand All @@ -99,4 +97,16 @@ protected function applyResultCallbackChain(Crawler $node, ApistMethod $method)
return $node;
}

/**
* @param $name
* @param $arguments
* @return $this
*/
public function addCallback($name, $arguments)
{
$resultCallback = new ResultCallback($name, $arguments);
$this->resultMethodChain[] = $resultCallback;
return $this;
}

}
14 changes: 14 additions & 0 deletions src/SleepingOwl/Apist/Selectors/ResultCallback.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ function __construct($methodName, $arguments)
*/
public function apply($node, ApistMethod $method)
{
if (is_array($node))
{
return $this->applyToArray($node, $method);
}
if ($this->methodName === 'else')
{
if (is_bool($node)) $node = ! $node;
Expand All @@ -53,6 +57,16 @@ public function apply($node, ApistMethod $method)
throw new \InvalidArgumentException("Method '{$this->methodName}' was not found");
}

protected function applyToArray($array, ApistMethod $method)
{
$result = [];
foreach ($array as $node)
{
$result[] = $this->apply($node, $method);
}
return $result;
}

/**
* @param ApistMethod $method
* @return bool
Expand Down
163 changes: 163 additions & 0 deletions src/SleepingOwl/Apist/Yaml/Parser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php namespace SleepingOwl\Apist\Yaml;

use SleepingOwl\Apist\Apist;
use SleepingOwl\Apist\Selectors\ApistSelector;
use Symfony\Component\Yaml\Yaml;

class Parser
{
/**
* @var array
*/
protected $methods = [];
/**
* @var array
*/
protected $structures = [];
/**
* @var string
*/
protected $file;

/**
* @param $file
*/
function __construct($file)
{
$this->file = $file;
}

/**
* @param Apist $resource
*/
public function load(Apist $resource)
{
$data = Yaml::parse($this->file);
if (isset($data['baseUrl']))
{
$resource->setBaseUrl($data['baseUrl']);
unset($data['baseUrl']);
}
foreach ($data as $method => $methodConfig)
{
if ($method[0] === '_')
{
# structure
$this->structures[$method] = $methodConfig;
} else
{
# method
$methodConfig['blueprint'] = $this->parseBlueprint($methodConfig['blueprint']);
$this->methods[$method] = $methodConfig;
}
}
}

/**
* @param $blueprint
* @return array
*/
protected function parseBlueprint($blueprint)
{
$callback = function (&$value)
{
if ($value[0] === ':')
{
# structure
$structure = $this->getStructure($value);
$value = $this->parseBlueprint($structure);
return;
}
if (strpos($value, '|') === false) return;

$parts = preg_split('/\s?\|\s?/', $value);
$selector = array_shift($parts);
$value = Apist::filter($selector);
foreach ($parts as $part)
{
$this->addCallbackToFilter($value, $part);
}
};
if ( ! is_array($blueprint))
{
$callback($blueprint);
} else
{
array_walk_recursive($blueprint, $callback);
}
return $blueprint;
}

/**
* @param ApistSelector $filter
* @param $callback
*/
protected function addCallbackToFilter(ApistSelector $filter, $callback)
{
$method = strtok($callback, '(),');
$arguments = [];
while ($argument = strtok('(),'))
{
$argument = trim($argument);
if (preg_match('/^[\'"].*[\'"]$/', $argument))
{
$argument = substr($argument, 1, -1);
}
if ($argument[0] === ':')
{
# structure
$structure = $this->getStructure($argument);
$argument = $this->parseBlueprint($structure);
}
$arguments[] = $argument;
}
$filter->addCallback($method, $arguments);
}

/**
* @param $name
* @return mixed
*/
protected function getStructure($name)
{
$structure = '_' . substr($name, 1);
if ( ! isset($this->structures[$structure]))
{
throw new \InvalidArgumentException("Structure '$structure' not found.'");
}
return $this->structures[$structure];
}

/**
* @param $name
* @return array
*/
public function getMethod($name)
{
if ( ! isset($this->methods[$name]))
{
throw new \InvalidArgumentException("Method '$name' not found.'");
}
$methodConfig = $this->methods[$name];
return $methodConfig;
}

/**
* @param $method
* @param $arguments
* @return mixed
*/
public function insertMethodArguments($method, $arguments)
{
array_walk_recursive($method, function (&$value) use ($arguments)
{
if ( ! is_string($value)) return;
$value = preg_replace_callback('/\$(?<num>[0-9]+)/', function ($finded) use ($arguments)
{
$argumentPosition = intval($finded['num']) - 1;
return isset($arguments[$argumentPosition]) ? $arguments[$argumentPosition] : null;
}, $value);
});
return $method;
}
}
50 changes: 50 additions & 0 deletions src/SleepingOwl/Apist/Yaml/YamlApist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php namespace SleepingOwl\Apist\Yaml;

class YamlApist extends \SleepingOwl\Apist\Apist
{
/**
* @var Parser
*/
protected $parser;

/**
* @param array $options
*/
function __construct($file = null, $options = [])
{
if ( ! is_null($file))
{
$this->loadFromYml($file);
}
parent::__construct($options);
}

/**
* Load method data from yaml file
* @param $file
*/
protected function loadFromYml($file)
{
$this->parser = new Parser($file);
$this->parser->load($this);
}

/**
* @param $name
* @param $arguments
* @return array
*/
function __call($name, $arguments)
{
if (is_null($this->parser))
{
throw new \InvalidArgumentException("Method '$name' not found.'");
}
$method = $this->parser->getMethod($name);
$method = $this->parser->insertMethodArguments($method, $arguments);
$httpMethod = isset($method['method']) ? strtoupper($method['method']) : 'GET';
$options = isset($method['options']) ? $method['options'] : [];
return $this->request($httpMethod, $method['url'], $method['blueprint'], $options);
}

}

0 comments on commit 6fd0992

Please sign in to comment.