Skip to content

Commit

Permalink
Merge pull request #17 from bobthecow/feature/builder-concerns
Browse files Browse the repository at this point in the history
Extract the RuleBuilder DSL from the base Variable and VariableProperty classes.
  • Loading branch information
bobthecow committed Sep 19, 2013
2 parents 52a0a92 + 21b4c73 commit 6809668
Show file tree
Hide file tree
Showing 10 changed files with 766 additions and 389 deletions.
6 changes: 3 additions & 3 deletions src/Ruler/RuleBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@

namespace Ruler;

use Ruler\Operator;
use Ruler\Proposition;
use Ruler\Rule;
use Ruler\Operator;
use Ruler\Variable;
use Ruler\RuleBuilder;

/**
* RuleBuilder.
Expand Down Expand Up @@ -123,7 +123,7 @@ public function offsetExists($name)
public function offsetGet($name)
{
if (!isset($this->variables[$name])) {
$this->variables[$name] = new Variable($name);
$this->variables[$name] = new RuleBuilder\Variable($name);
}

return $this->variables[$name];
Expand Down
233 changes: 233 additions & 0 deletions src/Ruler/RuleBuilder/Variable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
<?php

/*
* This file is part of the Ruler package, an OpenSky project.
*
* (c) 2013 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Ruler\RuleBuilder;

use Ruler\Operator;
use Ruler\RuleBuilder\VariableProperty;
use Ruler\Variable as BaseVariable;

/**
* A propositional Variable.
*
* Variables are placeholders in Propositions and Comparison Operators. During
* evaluation, they are replaced with terminal Values, either from the Variable
* default or from the current Context.
*
* The RuleBuilder Variable extends the base Variable class with a fluent
* interface for creating VariableProperties, Operators and Rules without all
* kinds of awkward object instantiation.
*
* @author Justin Hileman <[email protected]>
*/
class Variable extends BaseVariable implements \ArrayAccess
{
private $properties = array();

/**
* Get a VariableProperty for accessing methods, indexes and properties of
* the current variable.
*
* @param string $name Property name
* @param mixed $value The default VariableProperty value
*
* @return VariableProperty
*/
public function getProperty($name, $value = null)
{
if (!isset($this->properties[$name])) {
$this->properties[$name] = new VariableProperty($this, $name, $value);
}

return $this->properties[$name];
}

/**
* Fluent interface method for checking whether a VariableProperty has been defined.
*
* @param string $name Property name
*
* @return bool
*/
public function offsetExists($name)
{
return isset($this->properties[$name]);
}

/**
* Fluent interface method for creating or accessing VariableProperties.
*
* @see getProperty
*
* @param string $name Property name
*
* @return VariableProperty
*/
public function offsetGet($name)
{
return $this->getProperty($name);
}

/**
* Fluent interface method for setting default a VariableProperty value.
*
* @see setValue
*
* @param string $name Property name
* @param mixed $value The default Variable value
*/
public function offsetSet($name, $value)
{
$this->getProperty($name)->setValue($value);
}

/**
* Fluent interface method for removing a VariableProperty reference.
*
* @param string $name Property name
*/
public function offsetUnset($name)
{
unset($this->properties[$name]);
}

/**
* Fluent interface helper to create a contains comparison operator.
*
* @param mixed $variable Right side of comparison operator
*
* @return Operator\Contains
*/
public function contains($variable)
{
return new Operator\Contains($this, $this->asVariable($variable));
}

/**
* Fluent interface helper to create a contains comparison operator.
*
* @param mixed $variable Right side of comparison operator
*
* @return Operator\DoesNotContain
*/
public function doesNotContain($variable)
{
return new Operator\DoesNotContain($this, $this->asVariable($variable));
}

/**
* Fluent interface helper to create a GreaterThan comparison operator.
*
* @param mixed $variable Right side of comparison operator
*
* @return Operator\GreaterThan
*/
public function greaterThan($variable)
{
return new Operator\GreaterThan($this, $this->asVariable($variable));
}

/**
* Fluent interface helper to create a GreaterThanOrEqualTo comparison operator.
*
* @param mixed $variable Right side of comparison operator
*
* @return Operator\GreaterThanOrEqualTo
*/
public function greaterThanOrEqualTo($variable)
{
return new Operator\GreaterThanOrEqualTo($this, $this->asVariable($variable));
}

/**
* Fluent interface helper to create a LessThan comparison operator.
*
* @param mixed $variable Right side of comparison operator
*
* @return Operator\LessThan
*/
public function lessThan($variable)
{
return new Operator\LessThan($this, $this->asVariable($variable));
}

/**
* Fluent interface helper to create a LessThanOrEqualTo comparison operator.
*
* @param mixed $variable Right side of comparison operator
*
* @return Operator\LessThanOrEqualTo
*/
public function lessThanOrEqualTo($variable)
{
return new Operator\LessThanOrEqualTo($this, $this->asVariable($variable));
}

/**
* Fluent interface helper to create a EqualTo comparison operator.
*
* @param mixed $variable Right side of comparison operator
*
* @return Operator\EqualTo
*/
public function equalTo($variable)
{
return new Operator\EqualTo($this, $this->asVariable($variable));
}

/**
* Fluent interface helper to create a NotEqualTo comparison operator.
*
* @param mixed $variable Right side of comparison operator
*
* @return Operator\NotEqualTo
*/
public function notEqualTo($variable)
{
return new Operator\NotEqualTo($this, $this->asVariable($variable));
}

/**
* Fluent interface helper to create a SameAs comparison operator.
*
* @param mixed $variable Right side of comparison operator
*
* @return Operator\SameAs
*/
public function sameAs($variable)
{
return new Operator\SameAs($this, $this->asVariable($variable));
}

/**
* Fluent interface helper to create a NotSameAs comparison operator.
*
* @param mixed $variable Right side of comparison operator
*
* @return Operator\SameAs
*/
public function notSameAs($variable)
{
return new Operator\NotSameAs($this, $this->asVariable($variable));
}

/**
* Private helper to retrieve a Variable instance for the given $variable.
*
* @param mixed $variable BaseVariable instance or value
*
* @return BaseVariable
*/
private function asVariable($variable)
{
return ($variable instanceof BaseVariable) ? $variable : new BaseVariable(null, $variable);
}
}
111 changes: 111 additions & 0 deletions src/Ruler/RuleBuilder/VariableProperty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

/*
* This file is part of the Ruler package, an OpenSky project.
*
* (c) 2013 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Ruler\RuleBuilder;

use Ruler\Context;
use Ruler\RuleBuilder\Variable;
use Ruler\Value;
use Ruler\Variable as BaseVariable;

/**
* A propositional VariableProperty.
*
* A VariableProperty is a special propositional Variable which maps to a
* property, method or offset of another Variable. During evaluation, they are
* replaced with terminal Values from properties of their parent Variable,
* either from their default Value, or from the current Context.
*
* The RuleBuilder VariableProperty extends the base VariableProperty class with
* a fluent interface for creating VariableProperties, Operators and Rules
* without all kinds of awkward object instantiation.
*
* (Note that this class doesn't *literally* extend the base VariableProperty
* class, due to PHP's complete inability to use multiple inheritance. Nor does
* it use a trait like it probably should, because this library targets
* PHP 5.3+. Instead it uses a highly refined "copy and paste" technique,
* perfected over years of diligent practice.)
*
* @author Justin Hileman <[email protected]>
*/
class VariableProperty extends Variable
{
private $parent;

/**
* VariableProperty class constructor.
*
* @param BaseVariable $parent Parent Variable instance
* @param string $name Property name
* @param mixed $value Default Property value (default: null)
*/
public function __construct(BaseVariable $parent, $name, $value = null)
{
$this->parent = $parent;
parent::__construct($name, $value);
}

/**
* Prepare a Value for this VariableProperty given the current Context.
*
* To retrieve a Value, the parent Variable is first resolved given the
* current context. Then, depending on its type, a method, property or
* offset of the parent Value is returned.
*
* If the parent Value is an object, and this VariableProperty name is
* "bar", it will do a prioritized lookup for:
*
* 1. A method named `bar`
* 2. A public property named `bar`
* 3. ArrayAccess + offsetExists named `bar`
*
* If it is an array:
*
* 1. Array index `bar`
*
* Otherwise, return the default value for this VariableProperty.
*
* @param Context $context The current Context
*
* @return Value
*/
public function prepareValue(Context $context)
{
$name = $this->getName();
$value = $this->parent->prepareValue($context)->getValue();

if (is_object($value) && !$value instanceof \Closure) {
if (method_exists($value, $name)) {
return $this->asValue(call_user_func(array($value, $name)));
} elseif (isset($value->$name)) {
return $this->asValue($value->$name);
} elseif ($value instanceof \ArrayAccess && $value->offsetExists($name)) {
return $this->asValue($value->offsetGet($name));
}
} elseif (is_array($value) && array_key_exists($name, $value)) {
return $this->asValue($value[$name]);
}

return $this->asValue($this->getValue());
}

/**
* Private helper to retrieve a Value instance for the given $value.
*
* @param mixed $value Value instance or value
*
* @return Value
*/
private function asValue($value)
{
return ($value instanceof Value) ? $value : new Value($value);
}
}
Loading

0 comments on commit 6809668

Please sign in to comment.