diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1377554 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/README.markdown b/README.markdown index 4b5a9e9..fe144b7 100644 --- a/README.markdown +++ b/README.markdown @@ -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 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- -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) - - 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 + - {{#items}} - {{#url}}{{/url}} - {{#num}}{{/num}}{{name}}{{#num}}{{/num}} - {{#url}}{{/url}} - {{/items}} -

+$renderer = Kostache::factory(); +``` -Holy cow, that's more maintainable :) +And render it: + +```php +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/) \ No newline at end of file +[Original Mustache](http://defunkt.github.com/mustache/) diff --git a/classes/Kohana/Kostache.php b/classes/Kohana/Kostache.php index cc35b7f..bacb1b0 100644 --- a/classes/Kohana/Kostache.php +++ b/classes/Kohana/Kostache.php @@ -5,267 +5,46 @@ * @package Kostache * @category Base * @author Jeremy Bush - * @author Woody Gilk * @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; - } - } diff --git a/classes/Kohana/Kostache/Layout.php b/classes/Kohana/Kostache/Layout.php index 1b8a7d2..1f85dc1 100644 --- a/classes/Kohana/Kostache/Layout.php +++ b/classes/Kohana/Kostache/Layout.php @@ -5,42 +5,42 @@ * @package Kostache * @category Base * @author Jeremy Bush - * @author Woody Gilk * @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); } } diff --git a/classes/Kostache.php b/classes/Kostache.php index 9566dc3..2595419 100644 --- a/classes/Kostache.php +++ b/classes/Kostache.php @@ -1,3 +1,3 @@ _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; + } +} diff --git a/init.php b/init.php index 22b0b8b..b6a24da 100644 --- a/init.php +++ b/init.php @@ -1,4 +1,5 @@