Haml and Sass Extension

Haml and Sass have been used in Ruby for sometime to simplify templates (Haml) and make CSS more intelligent, flexible and manageable (Sass). This extension brings Haml and Sass to Yii.

It’s kind of two extensions in one as you can use Haml and/or Sass independently of each other.

Included is PhamlP, a port of Haml and Sass to PHP. It is the equivalent of V2.2 - Powerful Penny.

For details of Haml see http://haml-lang.com/, and for Sass see http://sass-lang.com/.

Haml and Sass Extension

Could you look at YiiML and comment on the differences between your extension and YiiML.

http://www.yiiframework.com/forum/index.php?/topic/7418-component-yiiml/

Hi,

I guess the biggest difference is that YiiML says it "is inspired by Haml and Sass".

By contrast PHamlP is Haml and Sass.

Because PHamlP is Haml and Sass you may get a better answer by asking the creator of YiiML what the differences are between it and Haml/Sass.

There are two differences from PHamlP from standard Haml and Sass:

[list=1]

[*]code blocks in Haml and code interpolation in both is PHP, not Ruby (but you knew that already :D )

[*]PHamlP can use tabs or spaces for indentation and automatically detects which

[/list]

The extension simply provides the interfaces for Yii to use Haml and Sass and so is kind of two in one as Haml and Sass are independent of each other, i.e. in your application you can use Haml without Sass or Sass without Haml; of course you can use both :D .

Personally I use both. I find using Haml tidies up my views and using indentation really makes the structure of the document easy to manage.

But the big win is Sass. Nesting keeps the code clean and readable, variables make the code more maintainable, and mixins (together with includes) make it modular, and Sass parses a Sass file and all its includes to a single CSS file to keep down the number of requests to the server.

In each there are various rendering options; for development there is the human readable "nested" renderer, and for production there is the "compressed" renderer that removes as much whitespace as it can to keep file sizes to a minimum. All done on the fly and cached to keep processing to a minimum (you can turn caching off BTW).

Contrary to the statements in the YiiML thread, Haml does provide flow control and PHamlP uses PHP syntax, e.g.




- if($this)

  .if-div do that

- else

  .else-div do something else



parses to




<?php if($this){ ?>

  <div class="if-div">

    do that

  </div>

<?php } else { ?>

  <div class="else-div">

    do something else

  </div>

<?php } ?>



All the control flow structures are supported (if, else, elseif, foreach, do, and while). The only one that is perhaps a little different is "do" because the "while" statement is expressed with the "do" clause, i.e.




%ul

  - do($i--)

    %li= $i



parses to:




<ul>

  <?php do { ?>

    <li>

      <?php echo $i; ?>

    </li>

  <?php } while($i--); ?>

</ul>



It seems YiiML has a different syntax from Haml. PHamlP uses the Haml and Sass syntaxes, so PHamlP documentation is the Haml and Sass documentation.

YiiML also says "but it’s really [Haml] quite focused on views, and keeping logic inside helpers/controllers". Yes it is and that is where things should be.

If you need to extend Haml this can be done by writing and using a filter, but MCV principles should be adhered to. Most of the standard Haml filters (:ruby becomes :php; there are not equivalents for :erb, :textile, or :marku; the :markdown filter is Yii specific) are bundled with PHamlP. If anyone writes others I would be interested in adding them as optional downloads.

So, for example, you can put raw PHP code in the view using the PHP filter:




:php

  foreach ($values as $key=>$value) {

    $value = $value * 2;

  }



What I don’t see in YiiML is CSS or Sass support (that probably means I didn’t find it, not that it’s not there).

I hope this goes some way to answering your question.

Thanks. Your explanation is very helpful.

I discovered this and YiiML only yesterday and on reading the YiiML thread, there were a couple of things that I found appealing.

  1. You can mix and match - that is, you don’t have to use the YiiML renderer for all the views.

  2. It provides for using Yii widgets (and maybe some other Yii customizations).

Do you think these might be easy to incorporate in your extension?

You can use widgets as of now, in fact anything you can do in a view, Yii specific or otherwise, you can do with Haml - just quicker and neater (IMHO).

As an example, here is the main layout for one of my recent projects (with names changed to protect the innocent :) ). This uses the MainMenu and breadcrumbs widgets, registers some scripts, uses a helper, has conditional comment for MSIE6, etc. (Note: cs() is a shortcut for Yii::app()->getClientScript, and similarly am() for the asset manager)




!!! XML

!!! 1.1

%html(xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml")

  %head(profile="http://dublincore.org/documents/2008/08/04/dc-html/")

    %title= $this->pageTitle

    = EMetadata::render($this->metadata)

    - cs()->registerCssFile(am()->publish(Yii::getPathOfAlias('application.assets.sass') . DIRECTORY_SEPARATOR . 'main.sass'))

    /[if IE 6]

      %link(rel="stylesheet" type="text/css" href="/msie/msie6.css")

    - cs()->registerCoreScript('jquery')

    - cs()->registerScriptFile('/js/jquery.tools.min.js')

    - cs()->registerScriptFile('/js/jquery.qtip.min.js')

    - cs()->registerScriptFile('/js/qtip.js')

  %body(id = "#{$this->id.'-'.$this->action->id}")

    #header

      %a(href="/" title="Home Page")

        %img(src="/images/logo.jpg" alt="Logo")

      - $this->widget('MainMenu', array('id' => 'main-menu'))

    #content

      - if (isset($this->breadcrumbs))

        - $this->widget('zii.widgets.CBreadcrumbs', array('links'=>$this->breadcrumbs));

      - Yii::app()->user->flash('email, success, warning, failure, unauthorised')

      = $content

    #footer

      - $this->widget('MainMenu', array('id' => 'footer-menu'))

      %p(class = "legal")

        %a(href = "/website_terms#copyright")

          Copyright &copy; 2000-#{date('Y').' '.Yii::app()->params['rightsHolder']} – All Rights Reserved

        %a(href = "/website_terms" title = "Read the Terms &amp; Conditions governing use of this site")

          Terms &amp; Conditions

        %a(href = "/privacy_policy" title = "Read our Privacy Policy")

          Privacy Policy



Interested in the mix and match idea. At the moment the view renderer to use is configured in the main config file, so it’s a one off deal. I will investigate.

Cool. This looks good.

OK - I have got that working; thanks for the idea - definitely makes the "entry barrier" lower as you can now gradually migrate views and layouts.

There is a new release R0014 available for download.

It does mean overriding CController::resolveViewFile in the application to allow fallback to a PHP view file, but I have raised a ticket to see if this can be done in the core (will make Yii more flexible - so I hope so) so that using this extension remains a case of unzipping the file and declaring the view renderer component. I suspect the change (assuming it happens) will also help YiiML as well.

R0016 is released.

PHamlP changed to provide better support for user defined filters.

If you are using the Markdown filter you must now provide the path alias in your configuration to the directory containing the Yii specific version. The Yii specific version is included in the extension.




  'viewRenderer'=>array(

    'class'=>'ext.haml.Haml',

    'filterPathAlias'=>'ext.haml.filters'

  },



Not sure if you wanted bugs listed here or on Google Code. Anyway, I’ve a question too, so maybe this is fine for once.

First the question: Do you know of any editor that’ll syntax highlight the php haml file. The one I tried (on vim) worked for Ruby files and didn’t do much for php.

Now the bug:

The control blocks (if, for …) were causing errors in file HamlParser.php. Made some changes (given below) and it’s working fine now.


private function parseCode($line, &$lines, $parent) {

		if (preg_match('/^(if|foreach|for|switch|do|while)\b(.*)$/',

				$line[self::Haml_CONTENT], $block)) {

			if ($block[1] === 'do') {

				$node = new HamlCodeBlockNode('<?php do { ?>');

				$node->doWhile = 'while' . $block[2] . ';';

			}

This page on the Haml website has a list of supported editors. I guess PHP support is limited as Haml and Sass are - currently at least - used mainly with Ruby.

Thanks for this. Will make an update.

Release R0017 is now available. This fixes issues 3 - 7, which includes the one mentioned above.

Nope. You missed this one. Still getting the error with the updated code.

Brain must have been some where else when I did the upload :rolleyes:

OK - R0018 does fix the issue above, plus the others.

Thanks!

Couple extension requests.

a. Have // be equivalent to -#. It’s much easier to type.

b. Allow single line filters. That is, pass on the remainder of the :filtername line to the filter.

If you decide against these, I’d appreciate it if you could point me to the right place so I can make the changes locally.

Also, I got the syntax highlighting to work in emacs (on windows). The difference from the original is that my hack handles php code and allows indendation by tabs. If you are interested, let me know.

Added both the above requests.

Plus the output view file now defaults to a .php file (which of course it is :) )

The extension of the output view file is set from an additional property -

$viewFileExtension - that can be set in the configuration; the default is .php

To control were the output view file is written use the $useRuntimePath path property

defined in CViewRenderer - set it in the configuration along with other renderer

properties.

The default is true; output files are written to protected.runtime.views.nnnnnnn

If set to false output files are written to the source file directory.

R0019 is available for download.

And yes, very interested in the syntax highlighting.

Attached the files needed for syntax highlighting in Emacs. The zip file contains php-mode.el and haml-mode.el. Copy these to your emacs/lisp/progmodes directory.

Add the line


(require 'haml-mode)

to your .emacs file (This file may need to be created).

R0020 is available. This fixes a Sass issue (Issue 13)

Thanks

Thanks for this extensions, it works great so far.

I encountered a small situation i cannot solve. Is it possible to change the haml behavior depending an a variable? Example:

i want to convert this php view code to haml:




<div class="waypointarea">

<span class="waypoint 

<?php echo ($point == 1) ? 'active' : ''; ?>"> One </span>

<span class="waypoint 

<?php echo ($point == 2) ? 'active' : ''; ?>"> Two </span>

</div> <!-- waypointarea>



Now, this goes like this:




.waypointarea

  %span.waypoint

    One

  %span.waypoint

    Two



but HOW is it possible to give the span.waypoint the class active only where $point is set… i know there is a way, but how to do this with haml?

thanks

Hi thyseus,

Glad you like the extension.

The way to do what you want is with code interpolation - the #{<PHP code>} construct. Code interpolation isn’t supported (yet) in the class and id shortcuts, so you need to declare the class as an attribute. I will investigate to see if I can get interpolation working in the shortcuts. If I can I’ll put in the next release.

To take your example verbatim, here is the Haml code (I’ve put the text on the same line as it’s short, but can be on the next line and indented)




-# setup

- $point = 2

	

.waypointarea

  %span(class="waypoint #{($point == 1 ? 'active' : '')}") One

  %span(class="waypoint #{($point == 2 ? 'active' : '')}") Two



This parses to:




<?php $point = 2; ?>

<div class="waypointarea">

  <span class="waypoint <?php echo ($point == 1 ? 'active' : ''); ?>">

    One

  </span>

  <span class="waypoint <?php echo ($point == 2 ? 'active' : ''); ?>">

    Two

  </span>

</div>



and the HTML is:




<div class="waypointarea">

  <span class="waypoint ">

    One

  </span>

  <span class="waypoint active">

    Two

  </span>

</div>



Another method (depending on what you are passing to your view) is to use a loop; for example:

Haml




-# setup

- $points = array('One', 'Two', 'Three')

- $point = 2


.waypointarea

  - foreach ($points as $p => $pointText)

    %span(class="waypoint #{($point == ++$p ? 'active' : '')}")= $pointText



parsed




<?php $points = array('One', 'Two', 'Three'); ?>

<?php $point = 2; ?>

<div class="waypointarea">

  <?php foreach ($points as $p => $pointText) { ?>

    <span class="waypoint <?php echo ($point == ++$p ? 'active' : ''); ?>">

      <?php echo $pointText; ?>

    </span>

  <?php }  ?>

</div>



HTML




<div class="waypointarea">

  <span class="waypoint ">

    One

  </span>

  <span class="waypoint active">

    Two

  </span>

  <span class="waypoint ">

    Three

  </span>

</div>