judofyr/temple

Support javascript/coffeescript/php language target

Closed this issue · 4 comments

Comment by @jfirebaugh:

My desire for using Slim as a client-side templating language led me to explore the feasibility of using Temple in a cross-compilation scenario. My goal was implement a Slim-alike language (called Skim) that embeds CoffeeScript rather than Ruby and compiles to a JavaScript template (JST) function. My strategy for getting there was to reuse as much of Slim's existing Temple-based pipeline as possible, substituting a CoffeeScript generator at the end and CoffeeScript-specific filters where necessary. I found this quite feasible, and I was able to get Skim passing most of Slim's test suite (adapted where necessary to CoffeeScript) in a couple days. With a few tweaks, I think the Ruby-specific parts of Temple (and Slim) could be further abstracted, and it could become a framework for implementing templates that compile to not just to Ruby but to any language that has a Temple "backend".

https://github.com/jfirebaugh/skim

Here are some more details. Slim makes use of the following Temple filters that assume that the target language is Ruby:

  • DynamicInliner assumes interpolation syntax, line continuations via backslash, and implicit string literal concatenation.
  • HTML::Pretty uses bits of Ruby code.
  • AttributeMerger and AttributeRemover use [:if, "!#{tmp}.empty?", sexp].
  • Escapable uses a Ruby-specific :escape_code option by default, and assumes that it can both be embedded in the output and evaled as Ruby code. In my case, I wanted to use Temple::Utils.escape_html[_safe] Ruby-side and @escape (i.e. this.escape) in CoffeeScript.

I worked around these issues by providing CoffeeScript-specific implementations of the essential filters (AttributeMerger, AttributeRemover, and Escapable) and by avoiding the use of DynamicInliner and HTML::Pretty (as a consequence, Skim doesn't support pretty printing and is presumably slightly less efficient than it could be).

Since CoffeeScript is whitespace sensitive, I also had to add one new core abstraction: [:indent, sexp], and use it appropriately in ControlFlow and Skim::Compiler, so that the CoffeeScript Generator can emit correctly-indented CoffeeScript.

Fortunately, I found that most of Slim's pipeline could be used as-is, because it is mostly language agnostic, and where it isn't, it uses syntax that is mostly common to Ruby and CoffeeScript (e.g. string interpolation). I had to override only a couple Slim::Compiler methods that emit several Ruby-specific fragments. One of them happens also to be valid CoffeeScript, but the one dealing with dynamic array-valued HTML attributes will need to be ported. I punted on it for the time being. I also have yet to implement sections (logicless mode), though I don't think it will be difficult.

I've kept the CoffeeScript portions of the implementation separated from the rest of Skim, with the thought that they could later be extracted to a temple-coffee gem for reuse.

Take a look, let me know if you think cross-compilation is an interesting direction for Temple.

John

@jfirebaugh: I think it would be very interesting to have support for javascript too. I am unsure with coffeescript since it will be compiled to javascript afterwards so or so. And the whitespace sensitivity complicates things too. Coffeescript has the nicer syntax though, which is also more suitable for Slim.

It would be nice to keep most parts of Slim and Temple language agnostic and use only a few language specific classes. We might have to add some more abstractions like the ones supported by the ControlFlow filter.

Maybe create proxy classes which support a target/language option and rely on target-specific implementation classes, e.g. Temple::Generator -> Temple::Generator::Javascript, Temple::Generator::Ruby.

rkh commented

+1

It seems people prefer to rewrite instead of considering multiple language targets.