Skip to content

Renshi - a templating language which is friendly and powerful with your HTML structures.

Notifications You must be signed in to change notification settings

nicholasf/renshi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

This is the final version of Renshi. I'm now working on my idea of an ideal templating language at http://github.com/biv/cranberry .

Renshi is a templating language for Ruby which is versatile and cooperative with HTML. It has a concise syntax and 'attribute expressions', which let you build and combine sets of inline Ruby instructions upon HTML elements.

Renshi integrates with Rails and Sinatra transparently. Simply 'require renshi'. In Sinatra use 'ren' where you would have used the 'erb'/'haml' methods. Renshi templates are appended with the suffix of .ren, e.g. index.html.ren or index.ren. 

$ Ruby Interpretation
=====================
Like ERB, but less typing.

Use $ or ${..} where you would use <%= ... %> . The single $ is used for an unbroken phrase (see below).

$foo[0], $1+1, $Time.now - ERB equiv. <%= foo[0] %>, <%= 1+1 %>, <%= Time.now %> 

${some_function "takes this string as input"} (ERB equiv. - <%= some_function "takes this string as input" %>)

Use $[] instead of <% ... -%>

$[if foo]
  $foo
$[end]

Attribute Expressions
=====================
Attr. expressions are insertable into HTML elements as attributes.

<p r:if="user.known?">Welcome $user.name</p>

They can be combined on elements and are interpreted in the order of appearance and are cumulative, allowing you to program inline on the HTML structure.

<li r:each="@sphinx.results[:words], |k,v|" r:if="v[:hits].to_i > 0">
    $k - Hits $v[:hits] in $v[:docs] documents
</li>

In the above, you can see that the if statement is scoped within the preceding each block, so you can reference variables between attr. expressions. Variables within an attr. expression don't need the $ symbol. That's only for setting up Ruby insertions within regular HTML.

One more thing, Renshi will guess the expected ending of boolean structures.

<td r:if="dataset.published?">...</td>      
<td r:else>...</td>

You don't need to supply an 'end' element; it is inferred by the HTML structure (see below).

None of the attr.expressions appear in the HTML rendered to the browser.

Attribute Expression Library
============================
* If 
* Unless
* Elsif 
* Else
* While
* For
* Each

* If
<span r:if="true">hello</span> would render "<span>hello</span>"

<span r:if="false">goodbye!</span> will render "".

* Unless
e.g. <span r:unless="false">hello</span>

* Elsif

<span r:if="false">hello!</span>
<span r:elsif="true">goodbye</span>

* Else

<span r:if="false">hello!</span>
<span r:elsif="false">goodbye</span>
<span r:else>neither</span>

r:elsif and r:else must be adjacent (a sibling) to the element.

For example, the below is fine, because the if and else statements are adjacent siblings in the tree of elements.

<div id="menu" r:if="user.is_administrator?">
  <div id="admin_menu"> ... </div>
<div id="menu" r:else>
  <div id="user_menu"> ... </div>
</div>  


* While

<div r:while="foo != 2" id="content$foo">
  hello$foo
  $[foo = foo + 1]
</div>

renders:

<div id="content0">
  hello0
  
</div><div id="content1">
  hello1
  
</div>

* For

<div id="content$foo" r:for="foo in foos">
hello$foo
</div>

renders:

<div id="content0">
hello0
</div>
...etc
<div id="content99">
hello99
</div>

* Each
The expression takes the object and then any arguments for the each block after a comma; so you can do anything with it that each supports.

r:each="foos, |k,v|" to iterate over a hash
r:each="foos, |foo|" to iterate over an array

<div id="content$foo" r:each="foos, |foo|">
hello$foo
</div>

<div id="content0">
hello0
</div>
...etc
<div id="content99">
hello99
</div>

Further elements will appear as I have time to make them or others want to contribute them. 
There isn't much to making them. If there's one you'd especially like, let me know on lighthouse and I'll see about adding it.

The one restriction is that I want to limit attr. expressions to one key and value combination on the element (everything fits into r:foo="expression").
I want to avoid complexity like "<span r:foo='..' r:params="k,v" />" for example - see r:each for the workaround.

See renshi/lib/renshi/attribute_expressions for how they work.

Other Rules
===========
To print a $ use $$, e.g. $$10.95 comes out as $10.95

Because the $foo evaluation can take all sorts of symbols to interpret a Ruby statement, if you are using a special symbol *immediately* before it, you'll have to use ${foo} instead. For example, because $greetings[0].upcase is interpretable, if you want to output "[HELLO]" where the brackets surround the string, you'd use [${greetings[0].upcase}]

The $ parser *delimits* (splits) on any character which is not in this list - . " ' { } ( ) + = * / \ - @ [ ] : ? ! % \w 

Installation
============
Renshi is hosted on Rubyforge so 'sudo gem install renshi'. Alternatively, use github to do what you want with it.


How Fast Is It? - Compilable Templates
======================================
I don't know - I've not benchmarked it yet. It's built solely on Nokogiri, which is very fast, and allows Renshi to be lightweight.

To a certain extent it's an irrelevant question, given that Renshi compiles .ren documents into chunks of ruby code, which are kept in memory and reused. This was written specifically for the Rails notion of compilable templates (see ActionView::TemplateHandlers::Compilable and Renshi::Frameworks::Rails).

ERB and HAML also do this (in fact I worked from the source code of both). It means, essentially, that a foo.html.ren document is compiled once into ruby, and each presentation of the template is then a matter of using the generated code (not reading the template).

Thus, n requests for the template mean 1 call to Renshi to generate the necessary code, which is then reused. At some stage I might benchmark this raw ruby - but I've written it with an eye on efficiency as it's generated and so far it seems very fast. If someone enjoys benchmarking then please jump in.

When integrating this feature with other frameworks, simply use a delegate object to hold the compiled code and evaluate it against the binding you want to use. See the Rails and Sinatra bridges to get you started.

If you're curious to see the source it creates, add an environment variable called RENSHI_SHOW_SRC.

Development
===========
Please report any bugs at the http://renshi.lighthouseapp.com . 

Contributions are welcome. Right now it's just me.

Why Renshi?
===========
Firstly, it doesn't need a reason. It's a fun project.

But ...

I've always found ERB to be a bit cumbersome - <%= is quite tiring to type out when you realise it could be much shorter. I used to think that Velocity, in Java, was the most fun templating language I'd used, and I had wanted something equally as concise for Ruby.

A real need for it emerged in a project which relied upon an external design house handing us HTML. Converting it incessantly into HAML was a headache. A colleague mentioned Genshi in Python as ideal, which was when the idea of Renshi was conceived.

It should give the developer power to do what they want, without being restrictive. You can throw $ statements around the place in a document or use attribute expressions to preserve a strict xhtml layout.

English to Japanese
===================
Renshi (錬士 : れんし ?): instructor.

In Japanese 'renshi' refers to a martial arts master - http://en.wikipedia.org/wiki/Renshi. 'Ren' means 'polished' and 'shi' means 'person'. 

It apparently also means 'to know another' in Mandarin.

The project was originally inspired by Genshi, http://genshi.edgewall.org - though it is significantly different in its approach.

About

Renshi - a templating language which is friendly and powerful with your HTML structures.

Resources

Stars

Watchers

Forks

Packages

No packages published