{"id":14986782,"url":"https://github.com/flavoursaver/flavoursaver","last_synced_at":"2025-05-16T03:05:02.955Z","repository":{"id":4424693,"uuid":"5562609","full_name":"FlavourSaver/FlavourSaver","owner":"FlavourSaver","description":"A pure Ruby Handlebars parser built with RLTK.","archived":false,"fork":false,"pushed_at":"2025-02-08T16:47:48.000Z","size":287,"stargazers_count":70,"open_issues_count":4,"forks_count":34,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-05-16T01:12:25.011Z","etag":null,"topics":["handlebars","ruby","ruby-gem"],"latest_commit_sha":null,"homepage":"","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/FlavourSaver.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2012-08-26T18:35:15.000Z","updated_at":"2025-02-08T16:43:28.000Z","dependencies_parsed_at":"2024-04-18T15:27:54.965Z","dependency_job_id":"407bb1c5-6038-4449-aac6-c04481503dab","html_url":"https://github.com/FlavourSaver/FlavourSaver","commit_stats":{"total_commits":148,"total_committers":25,"mean_commits":5.92,"dds":0.5540540540540541,"last_synced_commit":"87d10d1a4698eef2f315d06b200c8583317748a2"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FlavourSaver%2FFlavourSaver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FlavourSaver%2FFlavourSaver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FlavourSaver%2FFlavourSaver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FlavourSaver%2FFlavourSaver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FlavourSaver","download_url":"https://codeload.github.com/FlavourSaver/FlavourSaver/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254459087,"owners_count":22074605,"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":["handlebars","ruby","ruby-gem"],"created_at":"2024-09-24T14:13:32.510Z","updated_at":"2025-05-16T03:04:57.941Z","avatar_url":"https://github.com/FlavourSaver.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FlavourSaver\n\n[Handlebars.js](http://handlebarsjs.com) without the `.js`\n\n[![Gem Version](https://badge.fury.io/rb/flavour_saver.svg)](https://badge.fury.io/rb/flavour_saver)\n![Build Status](https://github.com/FlavourSaver/FlavourSaver/actions/workflows/ci.yml/badge.svg)\n[![Maintainability](https://api.codeclimate.com/v1/badges/89a99bec5bbf49359081/maintainability)](https://codeclimate.com/github/FlavourSaver/FlavourSaver/maintainability)\n\nFlavourSaver is a Ruby-based implementation of the [Handlebars.js](http://handlebarsjs.com)\ntemplating language. FlavourSaver supports Handlebars template rendering natively on\nRails and on other frameworks (such as Sinatra) via Tilt.\n\nPlease use it, break it, and send issues/PR's for improvement.\n\n## Status\n\nThis project is currently in maintenance mode. Here's what this means:\n\n* The maintainers of this project are not actively developing new features\n* Issues and pull requests are still welcome\n* New versions of the gem will be released as changes are merged\n\n## License\n\nFlavourSaver is Copyright (c) 2013 Resistor Limited and licensed under the terms\nof the MIT Public License (see the LICENSE file included with this distribution\nfor more details).\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n    gem 'flavour_saver'\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install flavour_saver\n\n## Usage\n\nFlavourSaver provides an interface to the amazing\n[Tilt](https://github.com/jeremyevans/tilt) templating library, meaning that it\nshould work with anything that has Tilt support (Sinatra, etc) and has a\nnative Rails template handler.\n\n## Features\n\nFlavourSaver is in its infancy, your pull requests are greatly appreciated.\n\nCurrently supported:\n\n  - Full support of Mustache and Handlebars templates.\n  - Expressions:\n    - with object-paths (`{{some.method.chain}}`)\n    - containing object-literals (`{{object.['index'].method}}`):\n      Ruby's `:[](index)` method is called for literals, making FlavourSaver\n      compatible with `Hash` and hashlike objects.\n    - with list arguments (`{{method arg1 \"arg2\"}}`)\n    - with hash arguments (`{{method foo=bar bar=\"baz\"}}`)\n    - with list and hash arguments (`{{method arg1 arg2 foo=bar bar=\"baz\"}}`)\n      provided that the hash is the last argument.\n    - Comments (`{{! a comment}}`)\n    - Expression output is HTML escaped\n  - Safe expressions\n    - Expressions wrapped in triple-stashes are not HTML escaped (`{{{an expression}}}`)\n  - Block expressions\n    - Simple API for adding block helpers.\n    - Block expressions with inverse blocks\n    - Inverse blocks\n  - Partials\n  - Raw content (`{{{{raw}}}} not parsed or validated {{{{/raw}}}}`)\n  - Subexpressions (`{{sum 1 (sum 1 1)}}` returns `3`)\n\n## Helpers\n\nFlavourSaver implements the following helpers by default:\n\n### #with\n\nYields its argument into the context of the block contents:\n\n```handlebars\n{{#with person}}\n  {{name}}\n{{/with}}\n```\n\n### #each\n\nTakes a single collection argument and yields the block's contents once\nfor each member of the collection:\n\n```handlebars\n{{#each people}}\n  {{name}}\n{{/each}}\n```\n\n### #if\n\nTakes a single argument and yields the contents of the block if that argument\nis truthy.\n\n```handlebars\n{{#if person}}\n  Hi {{person.name}}!\n{{/if}}\n```\n\nIt can also handle a special case `{{else}}` expression:\n\n```handlebars\n{{#if person}}\n  Hi {{person.name}}!\n{{else}}\n  Nobody to say hi to.\n{{/if}}\n```\n\n### #unless\n\nExactly the same is `#if` but backwards.\n\n### this\n\nIn JavaScript `this` is a native keyword, in Ruby not-so-much. FlavourSaver's `this` helper\nreturns `self`:\n\n```handlebars\n{{#each names}}\n  {{this}}\n{{/each}}\n```\n\n### log\n\nWrites log output.  The destination can be changed by assigning a `Logger` instance to\n`FlavourSaver.logger=`.  On Rails `FlavourSaver.logger` automatically points at\n`Rails.logger`.\n\n### Adding additional helpers\n\nAdditional helpers can easy be added by calling `FS.register_helper`, eg:\n\n```ruby\nFS.register_helper(:whom) { 'world' }\n```\n\nNow if you were to render the following template:\n\n```handlebars\n\u003ch1\u003eHello {{whom}}!\u003c/h1\u003e\n```\n\nYou would receive the following output:\n\n```html\n\u003ch1\u003eHello world!\u003c/h1\u003e\n```\n\n### Adding block helpers\n\nCreating a block helper works exactly like adding a regular helper, except that\nthe helper implementation can call `yield.contents` one or more times, with an\noptional argument setting the context of the block execution:\n\n```ruby\nFS.register_helper(:three_times) do\n  yield.contents\n  yield.contents\n  yield.contents\nend\n```\n\nWhich when called with the following template:\n\n```handlebars\n{{#three_times}}\n  hello\n{{/three_times}}\n```\n\nwould result in the following output:\n```\n  hello\n  hello\n  hello\n```\n\nImplementing a simple iterator is dead easy:\n\n```ruby\nFS.register_helper(:list_people) do |people|\n  people.each do |person|\n    yield.contents person\n  end\nend\n```\n\nWhich could be used like so:\n\n```handlebars\n{{#list_people people}}\n  \u003cb\u003e{{name}}\u003cb\u003e\u003cbr /\u003e\n  Age: {{age}}\u003cbr /\u003e\n  Sex: {{sex}}\u003cbr /\u003e\n{{/list_people}}\n```\n\nBlock helpers can also contain an `{{else}}` statement, which, when used creates\na second set of block contents (called `inverse`) which can be yielded to the output:\n\n```ruby\nFS.register_helper(:isFemale) do |person,\u0026block|\n  if person.sex == 'female'\n    block.call.contents\n  else\n    block.call.inverse\n  end\nend\n```\n\nYou can also register an existing method:\n\n```ruby\ndef isFemale(person)\n  if person.sex == 'female'\n    yield.contents\n  else\n    yield.inverse\n  end\nend\n\nFS.register_helper(method(:isFemale))\n```\n\nWhich could be used like so:\n\n```handlebars\n{{#isFemale person}}\n  {{person.name}} is female.\n{{else}}\n  {{person.name}} is male.\n{{/isFemale}}\n```\n\n### Subexpressions\n\nYou can use a subexpression as any value for a helper, and it will be executed before it is ran. You can also nest them, and use them in assignment of variables. \n\nBelow are some examples, utilizing a \"sum\" helper than adds together two numbers.\n\n```\n{{sum (sum 5 10) (sum 2 (sum 1 4))}}\n#=\u003e 22\n\n{{#if (sum 1 2) \u003e 2}}its more{{/if}}\n#=\u003e its more\n\n{{#student_heights size=(sum boys girls)}}\n```\n\n### Raw Content\n\nSometimes you don't want a section of content to be evaluted as handlebars, such as when you want to display it in a page that renders with handlebars. FlavourSaver offers a `raw` helper, that will allow you to pass anything through wrapped in those elements, and it will not be evaluated. \n\n```\n{{{{raw}}}}\n{{if} this tries to parse, it will break on syntax\n{{{{/raw}}}}\n=\u003e {{if} this tries to parse, it will break on syntax\n```\n\nIts important to note that while this looks like a block helper, it is not in practice. This is why you must omit the use of a `#` when writing it. \n\n### Using Partials\n\nHandlebars allows you to register a partial either as a function or a string template with\nthe engine before compiling, FlavourSaver retains this behaviour (with the notable exception\nof within Rails - see below).\n\nTo register a partial you call `FlavourSaver.register_partial` with a name and a string:\n\n```ruby\nFlavourSaver.register_partial(:my_partial, \"{{this}} is a partial\")\n```\n\nYou can then use this partial within your templates:\n\n```handlebars\n{{#each people}}{{\u003e my_partial this}}{{/each}}\n```\n\n## Using with Rails\n\nOne potential gotcha of using FlavourSaver with Rails is that FlavourSaver doesn't let you\nhave any access to the controller's instance variables. This is done to maintain compatibility\nwith the original JavaScript implementation of Handlebars so that templates can be used on\nboth the server and client side without any change.\n\nWhen accessing controller instance variables you should access them by way of a helper method\nor a presenter object.\n\nFor example, in `ApplicationController.rb` you may have a `before_filter` which authenticates\nthe current user's session cookie and stores it in the controller's `@current_user` instance\nvariable.\n\nTo access this variable you could create a simple helper method in `ApplicationHelpers`:\n\n```ruby\ndef current_user\n  @current_user\nend\n```\n\nWhich would mean that you are able to access it in your template:\n\n```handlebars\n{{#if current_user}}\n  Welcome back, {{current_user.first_name}}!\n{{/if}}\n```\n\n## Using the Tilt Interface Directly\n\nYou can use the registered Tilt interface directly to render template strings with a hash of template variables.\n\nThe Tilt template's `render` method expects an object that can respond to messages using dot notation. In the following example, the template variable `{{foo}}` will result in a call to `.foo` on the `data` object. For this reason the `data` object can't be a simple hash. A model would work, but if you have a plain old Ruby hash, use it to create a new OpenStruct object, which will provide the dot notation needed.\n\n```ruby\ntemplate = Tilt['handlebars'].new { \"{{foo}} {{bar}}\" }\ndata = OpenStruct.new foo: \"hello\", bar: \"world\"\n\ntemplate.render data # =\u003e \"hello world\"\n```\n\n### Special behaviour of Handlebars' partial syntax\n\nIn Handlebars.js all partial templates must be pre-registered with the engine before they are\nable to be used.  When running inside Rails FlavourSaver modifies this behaviour to use Rails'\nrender partial helper:\n\n```handlebars\n{{\u003e my_partial}}\n```\n\nWill be translated into:\n\n```ruby\nrender :partial =\u003e 'my_partial'\n```\n\nHandlebars allows you to send a context object into the partial, which sets the execution\ncontext of the partial.  In Rails this behaviour would be confusing and non-standard, so\ninstead any argument passed to the partial is evaluated and passed to the partial's\n`:object` argument:\n\n```handlebars\n{{\u003e my_partial my_context}}\n```\n\n```ruby\nrender :partial =\u003e 'my_partial', :object =\u003e my_context\n```\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Added some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflavoursaver%2Fflavoursaver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflavoursaver%2Fflavoursaver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflavoursaver%2Fflavoursaver/lists"}