Location: PHPKode > projects > Recess PHP Framework > recess/recess/framework/helpers/blocks/PartBlock.class.php
<?php
Library::import('recess.framework.helpers.blocks.Block');
Library::import('recess.framework.helpers.Buffer');
Library::import('recess.framework.helpers.Part');
Library::import('recess.framework.helpers.exceptions.InputDoesNotExistException');

/**
 * PartBlock is an object wrapper for Part templates. Its design is
 * inspired by curried lambdas. When a PartBlock is instantiated arguments
 * can be curried into the instance. Then, when the draw method is called
 * the remaining arguments can be passed. Ex:
 * 
 * $part = new PartBlock('mypart');
 * $part->draw('foo');
 * /// Equivalent to:
 * $part = new PartBlock('mypart', 'foo');
 * $part->draw();
 * 
 * There is also a mechanism for assigning values to inputs out-of-order 
 * with jQuery style property assignment:
 * 
 * $partBlock->inputName('value')->inputName2(10)->draw();
 * 
 * The purpose of PartBlock is to enable the state of a part to be 
 * passed around and manipulated by different entities before finally
 * being drawn.
 * 
 * @author Kris Jordan
 */
class PartBlock extends Block {
	protected $partPath = '';
	protected $args = array();
	protected $curriedArgs = 0;
	
	/**
	 * Instantiate a new PartBlock by passing the name of the Part template
	 * as the first argument followed by any other arguments to curry into
	 * the instance in the order defined by the part.
	 */
	function __construct() {
		$args = func_get_args();
		
		if(!empty($args)) {
			if(count($args) == 1) {
				if(is_array($args[0])) {
					$args = $args[0];
				} else {
					$args = array($args[0]);
				}
			}
		} else {	
			throw new RecessFrameworkException('PartListBlock are required to be constructed with the name of the part as the first argument.', 1);
		}

		$this->partPath = array_shift($args);
		$this->curry($args);
	}	
	
	/**
	 * Draw may or may not take arguments depending on the inputs of the
	 * wrapped part and the arguments that were curried in the construction
	 * of the PartBlock. The Part will throw a MissingRequiredInputException
	 * if a required input has not been satisfied.
	 * 
	 * @see recess/recess/recess/framework/helpers/blocks/Block#draw()
	 */
	public function draw() {
		$args = func_get_args();
		$clone = clone $this;
		$clone->curry($args);
		try {
			Part::drawArray($clone->partPath, $clone->args);
			return true;
		} catch(MissingRequiredInputException $e) {
			throw new MissingRequiredDrawArgumentsException($e->getMessage(), 1);
		}
	}
	
	/**
	 * Converts the PartBlock to a string based on inputs available. This will 
	 * only succeed if all required inputs have been satisfied by currying or 
	 * out-of-order assignment. If not, throws 'MissingRequiredDrawArgumentsException'.
	 * 
	 * @see recess/recess/recess/framework/helpers/blocks/Block#__toString()
	 */
	public function __toString() {
		Buffer::to($returnsBlock);
		try {
			$this->draw();
		} catch(MissingRequiredDrawArgumentsException $e) {
			die($e->getMessage());
		} catch(Exception $e) {
			die($e->getMessage());
		}
		Buffer::end();
		return (string)$returnsBlock;
	}
	
	public function get($input) {
		if(!isset($this->args[$input])) {
			$inputs = Part::getInputs($this->partPath);
			if(isset($inputs[$input])) {
				if(isset($inputs[$input]['default'])) {
					eval('$this->args[$input] = ' . $inputs[$input]['default'] . ';');
				} else {
					return null;
				}
			} else {
				throw new InputDoesNotExistException("Part '$this->partPath' does not have a '$input' input.", 1);
			}
		}
		return $this->args[$input];
	}
	
	public function set($name, $value) {
		try {
			$this->assign($name, $value);
		} catch (InputDoesNotExistException $e) {
			
		} catch (InputTypeCheckException $e) {
			throw new InputTypeCheckException($e->getMessage(), 1);	
		}
		return $this;
	}
	
	/**
	 * protected helper method for currying arguments into an instance.
	 * @param array
	 */
	protected function curry($args) {
		// pair any args with their input names
		if(is_array($args) && !empty($args)) {
			$inputs = Part::getInputs($this->partPath);
			$param = 0;
			$arg = 0;
			$argCount = count($args);
			foreach($inputs as $input => $attributes) {
				if($arg >= $argCount) {
					break;
				}
				if($param >= $this->curriedArgs) {
					try {
						$this->assign($input, $args[$arg++]);
					} catch(InputTypeCheckException $e) {
						throw new InputTypeCheckException($e->getMessage(), 2);
					}				
				}
				++$param;
			}
			$this->curriedArgs = $arg;
		}
	}
	
	/**
	 * Assign a value to a property of the PartBlock. This is an internal
	 * helper method. This method will throw an InputTypeCheckException if
	 * the assigned value does not match the expected value of a template.
	 * 
	 * @param string $property input name
	 * @param varies $value The value to be assigned.
	 */
	protected function assign($property, $value) {
		$inputs = Part::getInputs($this->partPath);
		if(isset($inputs[$property])) {
			if(Part::typeCheck($value, $inputs[$property]['type'])) {
				$this->args[$property] = $value;
			} else {
				$expected = $inputs[$property]['type'];
				$passed = gettype($value);
				if($passed === 'object') {
					$passed = get_class($value);
				}
				throw new InputTypeCheckException("Part input type mismatch '$property' expects '$expected' passed '$passed'.");
			}
		} else {
			throw new InputDoesNotExistException("Part '$this->partPath' does not have a '$property' input.", 2); 
		}
	}
}
?>
Return current item: Recess PHP Framework