1
0
Fork 0

Merge branch 'feature/mustache-2.0'

This commit is contained in:
Jeremy Bush 2012-11-04 20:57:02 -06:00
commit ca8d4c86df
9 changed files with 138 additions and 392 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.swp

View File

@ -2,147 +2,56 @@
Kostache is a [Kohana 3](https://github.com/kohana/kohana) module for using [Mustache](http://defunkt.github.com/mustache/) templates in your application.
Mustache is a logic-less template class. It is impossible to embed logic into mustache files.
## Usage
## Example
To use, simply create a POPO (Plain Old PHP Object) like so:
Did you know the pagination view in Kohana is terrible? We are going to fix it:
```php
<?php
### How it exists now:
class View_Test
{
public $hello = 'world';
<p class="pagination">
<?php if ($first_page !== FALSE): ?>
<a href="<?php echo $page->url($first_page) ?>" rel="first"><?php echo __('First') ?></a>
<?php else: ?>
<?php echo __('First') ?>
<?php endif ?>
<?php if ($previous_page !== FALSE): ?>
<a href="<?php echo $page->url($previous_page) ?>" rel="prev"><?php echo __('Previous') ?></a>
<?php else: ?>
<?php echo __('Previous') ?>
<?php endif ?>
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
<?php if ($i == $current_page): ?>
<strong><?php echo $i ?></strong>
<?php else: ?>
<a href="<?php echo $page->url($i) ?>"><?php echo $i ?></a>
<?php endif ?>
<?php endfor ?>
<?php if ($next_page !== FALSE): ?>
<a href="<?php echo $page->url($next_page) ?>" rel="next"><?php echo __('Next') ?></a>
<?php else: ?>
<?php echo __('Next') ?>
<?php endif ?>
<?php if ($last_page !== FALSE): ?>
<a href="<?php echo $page->url($last_page) ?>" rel="last"><?php echo __('Last') ?></a>
<?php else: ?>
<?php echo __('Last') ?>
<?php endif ?>
</p><!-- .pagination -->
Wow, look at all that logic in there! How do you plan on effectively maintaining that?!?
### Our new View Class (classes/view/pagination/basic.php)
<?php defined('SYSPATH') or die('No direct script access.');
class View_Pagination_Basic extends kostache {
protected $pagination;
protected function items()
{
$items = array();
// First.
$first['title'] = 'first';
$first['name'] = __('first');
$first['url'] = ($this->pagination->first_page !== FALSE) ? $this->pagination->url($this->pagination->first_page) : FALSE;
$items[] = $first;
// Prev.
$prev['title'] = 'prev';
$prev['name'] = __('previous');
$prev['url'] = ($this->pagination->previous_page !== FALSE) ? $this->pagination->url($this->pagination->previous_page) : FALSE;
$items[] = $prev;
// Numbers.
for ($i=1; $i<=$this->pagination->total_pages; $i++)
{
$item = array();
$item['num'] = TRUE;
$item['name'] = $i;
$item['url'] = ($i != $this->pagination->current_page) ? $this->pagination->url($i) : FALSE;
$items[] = $item;
}
// Next.
$next['title'] = 'next';
$next['name'] = __('next');
$next['url'] = ($this->pagination->next_page !== FALSE) ? $this->pagination->url($this->pagination->next_page) : FALSE;
$items[] = $next;
// Last.
$last['title'] = 'last';
$last['name'] = __('last');
$last['url'] = ($this->pagination->last_page !== FALSE) ? $this->pagination->url($this->pagination->last_page) : FALSE;
$items[] = $last;
return $items;
}
public function testing()
{
return 'foobar';
}
}
```
Yum, logic in a class, where it belongs :)
And create a mustache renderer. The parameter to the engine method is the template name to use.
### Our mustache template (templates/pagination/basic.mustache)
```php
<?php
<p class="pagination">
{{#items}}
{{#url}}<a href="{{url}}" {{#title}}rel="{{rel}}"{{/title}}>{{/url}}
{{#num}}<strong>{{/num}}{{name}}{{#num}}</strong>{{/num}}
{{#url}}</a>{{/url}}
{{/items}}
</p>
$renderer = Kostache::factory();
```
Holy cow, that's more maintainable :)
And render it:
```php
<?php
$this->response->body($renderer->render(new View_Test));
```
## Templates
Templates should go in the `templates/` directory in your cascading file system. They should have a .mustache extension.
## Partials
To use a partial in your template you use the greater than sign (>) and the name, e.g. {{>header}}.
Partials are loaded automatically based on the name used in the template. So if you reference `{{>foobar}}` in your template, it will look for that partial in `templates/partials/foobar.mustache`.
You must define partials within the $_partials array in your view class. The key is the name that you use in your template and the value is a path to your partial file.
# Layouts
protected $_partials = array(
'header' => 'header', // Loads templates/header.mustache
'footer' => 'footer/default', // Loads templates/footer/default.mustache
);
KOstache supports layouts. To use, just add a `templates/layout.mustache` file (a simple one is already provided), and use `Kostache_Layout` for your renderer instead of `Kostache`. You'll probably want to put a `$title` property in your view class. The layout should include a `{{>content}}` partial to render the body of the page.
## Using the Kostache_Layout class
Kostache comes with a Kostache_Layout class instead of a template controller. This allows your layouts to be more OOP and self contained, and they do not rely on your controllers so much.
To use it, have your view extend the Kostache_Layout class. You can then specify your own layout file by placing it in templates/layout.mustache. At a minimum, it needs to have a {{>content}} partial defined in it.
If you have a view that extends the Kostache_Layout class, but wish to render only the template and not the entire layout, you can set the public $render_layout property to FALSE. This is useful if you want to use the same view class for external requests and HMVC requests.
$view = new View_Post_List;
if ($this->request !== Request::instance) // Is internal request
{
$view->render_layout = FALSE;
}
# Additional Information
For specific usage and documentation, see:
[PHP Mustache](http://github.com/bobthecow/mustache.php)
[Original Mustache](http://defunkt.github.com/mustache/)
[Original Mustache](http://defunkt.github.com/mustache/)

View File

@ -5,267 +5,46 @@
* @package Kostache
* @category Base
* @author Jeremy Bush <jeremy.bush@kohanaframework.org>
* @author Woody Gilk <woody.gilk@kohanaframework.org>
* @copyright (c) 2010-2012 Jeremy Bush
* @copyright (c) 2011-2012 Woody Gilk
* @license MIT
*/
abstract class Kohana_Kostache {
class Kohana_Kostache {
const VERSION = '3.0.0';
const VERSION = '4.0.0';
/**
* Factory method for Kostache views. Accepts a template path and an
* optional array of partial paths.
*
* @param string template path
* @param array partial paths
* @return Kostache
* @throws Kohana_Exception if the view class does not exist
*/
public static function factory($path, array $partials = NULL)
protected $_engine;
public static function factory()
{
$class = 'View_'.str_replace('/', '_', $path);
$m = new Mustache_Engine(
array(
'loader' => new Mustache_Loader_Kohana(),
'partials_loader' => new Mustache_Loader_Kohana('templates/partials'),
'escape' => function($value) {
return html::chars($value);
},
'cache' => APPPATH.'cache/mustache',
)
);
if ( ! class_exists($class))
{
throw new Kohana_Exception('View class does not exist: :class', array(
':class' => $class,
));
}
return new $class(NULL, $partials);
$class = get_called_class();
return new $class($m);
}
/**
* @var string Mustache template
*/
protected $_template;
/**
* @var array Mustache partials
*/
protected $_partials = array();
/**
* Loads the template and partial paths.
*
* @param string template path
* @param array partial paths
* @return void
* @uses Kostache::template
* @uses Kostache::partial
*/
public function __construct($template = NULL, array $partials = NULL)
public function __construct($engine)
{
if ( ! $template)
{
if ($this->_template)
{
// Load the template defined in the view
$template = $this->_template;
}
else
{
// Detect the template for this class
$template = $this->_detect_template();
}
}
// Load the template
$this->template($template);
if ($this->_partials)
{
foreach ($this->_partials as $name => $path)
{
// Load the partials defined in the view
$this->partial($name, $path);
}
}
if ($partials)
{
foreach ($partials as $name => $path)
{
// Load the partial
$this->partial($name, $path);
}
}
$this->_engine = $engine;
}
/**
* Magic method, returns the output of [Kostache::render].
*
* @return string
* @uses Kostache::render
*/
public function __toString()
public function render($class, $template = NULL)
{
try
if ($template == NULL)
{
return $this->render();
}
catch (Exception $e)
{
ob_start();
// Render the exception
Kohana_Exception::handler($e);
return (string) ob_get_clean();
}
}
/**
* Loads a new template from a path.
*
* @return Kostache
*/
public function template($path)
{
$this->_template = $this->_load($path);
return $this;
}
/**
* Loads a new partial from a path. If the path is empty, the partial will
* be removed.
*
* @param string partial name
* @param mixed partial path, FALSE to remove the partial
* @return Kostache
*/
public function partial($name, $path)
{
if ( ! $path)
{
unset($this->_partials[$name]);
}
else
{
$this->_partials[$name] = $this->_load($path);
$template = explode('_', get_class($class));
array_shift($template);
$template = implode('/', $template);
}
return $this;
return $this->_engine->loadTemplate($template)->render($class);
}
/**
* Assigns a variable by name.
*
* // This value can be accessed as {{foo}} within the template
* $view->set('foo', 'my value');
*
* You can also use an array to set several values at once:
*
* // Create the values {{food}} and {{beverage}} in the template
* $view->set(array('food' => 'bread', 'beverage' => 'water'));
*
* @param string variable name or an array of variables
* @param mixed value
* @return $this
*/
public function set($key, $value = NULL)
{
if (is_array($key))
{
foreach ($key as $name => $value)
{
$this->{$name} = $value;
}
}
else
{
$this->{$key} = $value;
}
return $this;
}
/**
* Assigns a value by reference. The benefit of binding is that values can
* be altered without re-setting them. It is also possible to bind variables
* before they have values. Assigned values will be available as a
* variable within the template file:
*
* // This reference can be accessed as {{ref}} within the template
* $view->bind('ref', $bar);
*
* @param string variable name
* @param mixed referenced variable
* @return $this
*/
public function bind($key, & $value)
{
$this->{$key} =& $value;
return $this;
}
/**
* Renders the template using the current view.
*
* @return string
*/
public function render()
{
return $this->_stash($this->_template, $this, $this->_partials)->render();
}
/**
* Return a new Mustache for the given template, view, and partials.
*
* @param string template
* @param Kostache view object
* @param array partial templates
* @return Mustache
*/
protected function _stash($template, Kostache $view, array $partials)
{
return new Kohana_Mustache($template, $view, $partials, array(
'charset' => Kohana::$charset,
));
}
/**
* Load a template and return it.
*
* @param string template path
* @return string
* @throws Kohana_Exception if the template does not exist
*/
protected function _load($path)
{
$file = Kohana::find_file('templates', $path, 'mustache');
if ( ! $file)
{
throw new Kohana_Exception('Template file does not exist: :path', array(
':path' => 'templates/'.$path,
));
}
return file_get_contents($file);
}
/**
* Detect the template name from the class name.
*
* @return string
*/
protected function _detect_template()
{
// Start creating the template path from the class name
$template = explode('_', get_class($this));
// Remove "View" prefix
array_shift($template);
// Convert name parts into a path
$template = strtolower(implode('/', $template));
return $template;
}
}

View File

@ -5,42 +5,42 @@
* @package Kostache
* @category Base
* @author Jeremy Bush <jeremy.bush@kohanaframework.org>
* @author Woody Gilk <woody.gilk@kohanaframework.org>
* @copyright (c) 2010-2012 Jeremy Bush
* @copyright (c) 2011-2012 Woody Gilk
* @license MIT
*/
abstract class Kohana_Kostache_Layout extends Kostache {
class Kohana_Kostache_Layout extends Kohana_Kostache {
/**
* @var string partial name for content
*/
const CONTENT_PARTIAL = 'content';
/**
* @var boolean render template in layout?
*/
public $render_layout = TRUE;
/**
* @var string layout path
*/
protected $_layout = 'layout';
public function render()
public static function factory($layout = 'layout')
{
if ( ! $this->render_layout)
{
return parent::render();
}
$k = parent::factory();
$k->set_layout($layout);
return $k;
}
$partials = $this->_partials;
public function set_layout($layout)
{
$this->_layout = (string) $layout;
}
$partials[Kostache_Layout::CONTENT_PARTIAL] = $this->_template;
public function render($class, $template = NULL)
{
$this->_engine->setPartials(
array(
Kostache_Layout::CONTENT_PARTIAL => parent::render($class, $template)
)
);
$template = $this->_load($this->_layout);
return $this->_stash($template, $this, $partials)->render();
return $this->_engine->loadTemplate($this->_layout)->render($class);
}
}

View File

@ -1,3 +1,3 @@
<?php defined('SYSPATH') or die('No direct script access.');
abstract class Kostache extends Kohana_Kostache { }
class Kostache extends Kohana_Kostache { }

View File

@ -1,3 +1,3 @@
<?php defined('SYSPATH') or die('No direct script access.');
abstract class Kostache_Layout extends Kohana_Kostache_Layout { }
class Kostache_Layout extends Kohana_Kostache_Layout { }

View File

@ -0,0 +1,56 @@
<?php
class Mustache_Loader_Kohana implements Mustache_Loader, Mustache_Loader_MutableLoader
{
private $_base_dir = 'templates';
private $_extension = 'mustache';
private $_templates = array();
public function __construct($base_dir = NULL, $options = array())
{
if ($base_dir)
$this->_base_dir = $base_dir;
if (isset($options['extension']))
{
$this->_extension = '.' . ltrim($options['extension'], '.');
}
}
public function load($name)
{
if (!isset($this->_templates[$name]))
{
$this->_templates[$name] = $this->_load_file($name);
}
return $this->_templates[$name];
}
protected function _load_file($name)
{
$filename = Kohana::find_file($this->_base_dir, $name, $this->_extension);
return file_get_contents($filename);
}
/**
* Set an associative array of Template sources for this loader.
*
* @param array $templates
*/
public function setTemplates(array $templates)
{
$this->_templates = array_merge($this->_templates, $templates);
}
/**
* Set a Template source by name.
*
* @param string $name
* @param string $template Mustache Template source
*/
public function setTemplate($name, $template)
{
$this->_templates[$name] = $template;
}
}

View File

@ -1,4 +1,5 @@
<?php defined('SYSPATH') or die('No direct script access.');
// Load Mustache for PHP
include Kohana::find_file('vendor', 'mustache/Mustache');
include Kohana::find_file('vendor', 'mustache/src/Mustache/Autoloader');
Mustache_Autoloader::register();

2
vendor/mustache vendored

@ -1 +1 @@
Subproject commit cd6bfaefd30e13082780b3711c2aa4b92d146b1e
Subproject commit d99be3444e5b2f0fb605941d7e692d3b5159360c