{"id":13463251,"url":"https://github.com/judofyr/temple","last_synced_at":"2025-05-14T14:09:37.797Z","repository":{"id":44585826,"uuid":"428281","full_name":"judofyr/temple","owner":"judofyr","description":"Template compilation framework in Ruby","archived":false,"fork":false,"pushed_at":"2024-07-30T05:59:48.000Z","size":696,"stargazers_count":496,"open_issues_count":9,"forks_count":54,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-26T04:13:46.824Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://judofyr.net/posts/temple.html","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/judofyr.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2009-12-13T00:08:13.000Z","updated_at":"2025-03-27T19:01:12.000Z","dependencies_parsed_at":"2024-01-20T18:28:53.686Z","dependency_job_id":"9de0fcdf-9685-4692-840d-f526b829930c","html_url":"https://github.com/judofyr/temple","commit_stats":{"total_commits":604,"total_committers":38,"mean_commits":"15.894736842105264","dds":"0.29139072847682124","last_synced_commit":"73f850e957920e5d5dfe1c3394fbf7dea2d3d4a0"},"previous_names":[],"tags_count":51,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/judofyr%2Ftemple","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/judofyr%2Ftemple/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/judofyr%2Ftemple/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/judofyr%2Ftemple/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/judofyr","download_url":"https://codeload.github.com/judofyr/temple/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251999658,"owners_count":21678020,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T13:00:48.987Z","updated_at":"2025-05-14T14:09:37.745Z","avatar_url":"https://github.com/judofyr.png","language":"Ruby","funding_links":[],"categories":["HTML \u0026 Markup","Ruby"],"sub_categories":["Template Engines"],"readme":"Temple\n======\n\n[![test](https://github.com/judofyr/temple/actions/workflows/test.yml/badge.svg)](https://github.com/judofyr/temple/actions/workflows/test.yml) [![Code Climate](https://codeclimate.com/github/judofyr/temple.svg)](https://codeclimate.com/github/judofyr/temple) [![Gem Version](https://badge.fury.io/rb/temple.svg)](https://rubygems.org/gems/temple) [![Yard Docs](https://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/gems/temple/frames)\n\nTemple is an abstraction and a framework for compiling templates to pure Ruby.\nIt's all about making it easier to experiment, implement and optimize template\nlanguages. If you're interested in implementing your own template language, or\nanything else related to the internals of a template engine: You've come to\nthe right place.\n\nHave a look around, and if you're still wondering: Ask on the mailing list and\nwe'll try to do our best. In fact, it doesn't have to be related to Temple at\nall. As long as it has something to do with template languages, we're\ninterested: \u003chttp://groups.google.com/group/guardians-of-the-temple\u003e.\n\nLinks\n-----\n\n* Source: \u003chttp://github.com/judofyr/temple\u003e\n* Bugs:   \u003chttp://github.com/judofyr/temple/issues\u003e\n* List:   \u003chttp://groups.google.com/group/guardians-of-the-temple\u003e\n* API documentation:\n    * Latest Gem: \u003chttp://rubydoc.info/gems/temple/frames\u003e\n    * GitHub master: \u003chttp://rubydoc.info/github/judofyr/temple/master/frames\u003e\n* Abstractions: \u003chttp://github.com/judofyr/temple/blob/master/EXPRESSIONS.md\u003e\n\nOverview\n--------\n\nTemple is built on a theory that every template consists of three elements:\n\n* Static text\n* Dynamic text (pieces of Ruby which are evaluated and sent to the client)\n* Codes (pieces of Ruby which are evaluated and *not* sent to the client, but\n  might change the control flow).\n\nThe goal of a template engine is to take the template and eventually compile\nit into *the core abstraction*:\n\n```ruby\n [:multi,\n   [:static, \"Hello \"],\n   [:dynamic, \"@user.name\"],\n   [:static, \"!\\n\"],\n   [:code, \"if @user.birthday == Date.today\"],\n   [:static, \"Happy birthday!\"],\n   [:code, \"end\"]]\n```\n\nThen you can apply some optimizations, feed it to Temple and it generates fast\nRuby code for you:\n\n```ruby\n _buf = []\n _buf \u003c\u003c (\"Hello #{@user.name}!\\n\")\n if @user.birthday == Date.today\n   _buf \u003c\u003c \"Happy birthday!\"\n end\n _buf.join\n```\n\nS-expression\n------------\n\nIn Temple, an Sexp is simply an array (or a subclass) where the first element\nis the *type* and the rest are the *arguments*. The type must be a symbol and\nit's recommended to only use strings, symbols, arrays and numbers as\narguments.\n\nTemple uses Sexps to represent templates because it's a simple and\nstraightforward data structure, which can easily be written by hand and\nmanipulated by computers.\n\nSome examples:\n\n```ruby\n [:static, \"Hello World!\"]\n\n [:multi,\n   [:static, \"Hello \"],\n   [:dynamic, \"@world\"]]\n\n [:html, :tag, \"em\", [:html, :attrs], [:static, \"Hey hey\"]]\n```\n\n*NOTE:* SexpProcessor, a library written by Ryan Davis, includes a `Sexp`\nclass. While you can use this class (since it's a subclass of Array), it's not\nwhat Temple mean by \"Sexp\".\n\nAbstractions\n------------\n\nThe idea behind Temple is that abstractions are good, and it's better to have\ntoo many than too few. While you should always end up with the core\nabstraction, you shouldn't stress about it. Take one step at a time, and only\ndo one thing at every step.\n\nSo what's an abstraction? An abstraction is when you introduce a new types:\n\n```ruby\n # Instead of:\n [:static, \"\u003cstrong\u003eUse the force\u003c/strong\u003e\"]\n\n # You use:\n [:html, :tag, \"strong\", [:html, :attrs], [:static, \"Use the force\"]]\n```\n\n### Why are abstractions so important?\n\nFirst of all, it means that several template engines can share code. Instead\nof having two engines which goes all the way to generating HTML, you have two\nsmaller engines which only compiles to the HTML abstraction together with\nsomething that compiles the HTML abstraction to the core abstraction.\n\nOften you also introduce abstractions because there's more than one way to do\nit. There's not a single way to generate HTML. Should it be indented? If so,\nwith tabs or spaces? Or should it remove as much whitespace as possible?\nSingle or double quotes in attributes? Escape all weird UTF-8 characters?\n\nWith an abstraction you can easily introduce a completely new HTML compiler,\nand whatever is below doesn't have to care about it *at all*. They just\ncontinue to use the HTML abstraction. Maybe you even want to write your\ncompiler in another language? Sexps are easily serialized and if you don't\nmind working across processes, it's not a problem at all.\n\nAll abstractions used by Temple are documented in [EXPRESSIONS.md](EXPRESSIONS.md).\n\nCompilers\n---------\n\nA *compiler* is simply an object which responds a method called #call which\ntakes one argument and returns a value. It's illegal for a compiler to mutate\nthe argument, and it should be possible to use the same instance several times\n(although not by several threads at the same time).\n\nWhile a compiler can be any object, you very often want to structure it as a\nclass. Temple then assumes the initializer takes an optional option hash:\n\n```ruby\n class MyCompiler\n   def initialize(options = {})\n     @options = options\n   end\n\n   def call(exp)\n     # do stuff\n   end\n end\n```\n\n### Parsers\n\nIn Temple, a parser is also a compiler, because a compiler is just something\nthat takes some input and produces some output. A parser is then something\nthat takes a string and returns an Sexp.\n\nIt's important to remember that the parser *should be dumb*. No optimization,\nno guesses. It should produce an Sexp that is as close to the source as\npossible. You should invent your own abstraction. Maybe you even want to\nseparate the parsers into several parts and introduce several abstractions on\nthe way?\n\n### Filters\n\nA filter is a compiler which take an Sexp and returns an Sexp. It might turn\nconvert it one step closer to the core-abstraction, it might create a new\nabstraction, or it might just optimize in the current abstraction. Ultimately,\nit's still just a compiler which takes an Sexp and returns an Sexp.\n\nFor instance, Temple ships with {Temple::Filters::DynamicInliner} and\n{Temple::Filters::StaticMerger} which are general optimization filters which\nworks on the core abstraction.\n\nAn HTML compiler would be a filter, since it would take an Sexp in the HTML\nabstraction and compile it down to the core abstraction.\n\n### Generators\n\nA generator is a compiler which takes an Sexp and returns a string which is\nvalid Ruby code.\n\nMost of the time you would just use {Temple::Generators::ArrayBuffer} or any of the\nother generators in {Temple::Generators}, but nothing stops you from writing your\nown.\n\nIn fact, one of the great things about Temple is that if you write a new\ngenerator which turns out to be a lot faster then the others, it's going to\nmake *every single engine* based on Temple faster! So if you have any ideas,\nplease share them - it's highly appreciated.\n\nEngines\n-------\n\nWhen you have a chain of a parsers, some filters and a generator you can finally create your *engine*. Temple provides {Temple::Engine} which makes this very easy:\n\n```ruby\n class MyEngine \u003c Temple::Engine\n   # First run MyParser\n   use MyParser\n\n   # Then a custom filter\n   use MyFilter\n\n   # Then some general optimizations filters\n   filter :MultiFlattener\n   filter :StaticMerger\n   filter :DynamicInliner\n\n   # Finally the generator\n   generator :ArrayBuffer\n end\n\n engine = MyEngine.new(strict: \"For MyParser\")\n engine.call(something)\n```\n\nAnd then?\n---------\n\nYou've ran the template through the parser, some filters and in the end a\ngenerator. What happens next?\n\nTemple provides helpers to create template classes for [Tilt](http://github.com/rtomayko/tilt) and\nRails.\n\n```ruby\n require 'tilt'\n\n # Create template class MyTemplate and register your file extension\n MyTemplate = Temple::Templates::Tilt(MyEngine, register_as: 'ext')\n\n Tilt.new('example.ext').render     # =\u003e Render a file\n MyTemplate.new { \"String\" }.render # =\u003e Render a string\n```\n\nInstallation\n------------\n\nYou need at least Ruby 1.9.3 to work with Temple. Temple is published as a Ruby Gem which can be installed\nas following:\n\n```bash\n $ gem install temple\n```\n\nEngines using Temple\n--------------------\n\n* [Slim](https://github.com/slim-template/slim)\n* [Hamlit](https://github.com/k0kubun/hamlit)\n* [Faml](https://github.com/eagletmt/faml)\n* [Sal](https://github.com/stonean/sal.rb)\n* [Temple-Mustache (Example implementation)](https://github.com/minad/temple-mustache)\n* Temple ERB example implementation (Temple::ERB::Template)\n* [WLang](https://github.com/blambeau/wlang)\n\nAcknowledgements\n----------------\n\nThanks to [_why](http://en.wikipedia.org/wiki/Why_the_lucky_stiff) for\ncreating an excellent template engine (Markaby) which is quite slow. That's\nhow I started experimenting with template engines in the first place.\n\nI also owe [Ryan Davis](http://zenspider.com/) a lot for his excellent\nprojects ParserTree, RubyParser, Ruby2Ruby and SexpProcessor. Temple is\nheavily inspired by how these tools work.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjudofyr%2Ftemple","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjudofyr%2Ftemple","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjudofyr%2Ftemple/lists"}