{"id":18656609,"url":"https://github.com/zendesk/curlybars","last_synced_at":"2025-04-05T14:07:11.902Z","repository":{"id":39175174,"uuid":"110094018","full_name":"zendesk/curlybars","owner":"zendesk","description":"Handlebars.js compatible templating library in Ruby","archived":false,"fork":false,"pushed_at":"2025-03-28T17:39:27.000Z","size":1170,"stargazers_count":35,"open_issues_count":7,"forks_count":4,"subscribers_count":309,"default_branch":"main","last_synced_at":"2025-03-29T13:09:23.438Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zendesk.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","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":"2017-11-09T09:24:39.000Z","updated_at":"2025-01-15T17:03:12.000Z","dependencies_parsed_at":"2023-11-19T21:29:04.907Z","dependency_job_id":"8cc7ff58-5e1b-445f-bffd-540e5c07cb60","html_url":"https://github.com/zendesk/curlybars","commit_stats":{"total_commits":883,"total_committers":26,"mean_commits":33.96153846153846,"dds":0.7078142695356738,"last_synced_commit":"14e2c7b606dac6f7e01af5cc291a48b99bd28da4"},"previous_names":[],"tags_count":94,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zendesk%2Fcurlybars","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zendesk%2Fcurlybars/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zendesk%2Fcurlybars/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zendesk%2Fcurlybars/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zendesk","download_url":"https://codeload.github.com/zendesk/curlybars/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247345853,"owners_count":20924102,"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-11-07T07:24:20.175Z","updated_at":"2025-04-05T14:07:11.880Z","avatar_url":"https://github.com/zendesk.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://github.com/zendesk/curlybars/workflows/CI/badge.svg)](https://github.com/zendesk/curlybars/actions?query=workflow%3ACI)\n\n# Curlybars\n\nCurlybars is a Ruby implementation of a subset of Handlebars, where getting the context for rendering the template is based on the presenter approach taken in [Curly](https://github.com/zendesk/curly).\n\n## Table of Contents\n\n1. [Overview](#overview)\n1. [Getting started](#getting-started)\n1. [Configuration](#configuration)\n1. [Further documentation](#further-documentation)\n1. [Contributing](#contributing)\n1. [Maintainers](#maintainers)\n1. [Releasing](#releasing)\n1. [Copyright and License](#copyright-and-license)\n\n## Overview\n\nCurlybars is a templating engine for Ruby, and it integrates with Rails out of the box. In order to use it with Rails, at least a **root presenter** and a **template** must be provided for each view that is meant to be rendered via Curlybars.\n\nA template is, in fact, a Handlebars template and must use the `.hbs` instead of `.erb` extension, such as `app/views/invoice/details.html.hbs`.\n\nA root presenter is a class providing the root context used to evaluate a given template. In other words, if the template contains the string `{{amount}}`, then the root presenter must provide an instance method named `amount`.\n\nWhenever a deeper reference like path is encountered in a template, like `{{recipient.name}}`, the root presenter is expected to have a method named `recipient`, returning an instance of a Poor Old Ruby Object presenter (a non-root presenter) with a method called `name`.\n\nMore on what has been introduced here will be explained in details later sections.\n\n## Getting started\n\nTo provide your Rails app with Curlybars, simply add the following line in your `Gemfile`, possibly narrowing down the version you want to depend on:\n\n```ruby\ngem 'curlybars'\n```\n\nAnd then simply run `bundle install`. At this point, we can start to create a trivial example, that shows how to get started with rendering a page.\n\nFor starters, let's create a couple of classes, `Invoice` and `Recipient`, that hold part of the information that would be in an invoice.\n\n```ruby\n# app/models/invoice.rb\n\nclass Invoice\n  def initialize(amount:, recipient:)\n    @amount = amount\n    @recipient = recipient\n  end\n\n  def amount\n    @amount\n  end\n\n  def recipient\n    @recipient\n  end\nend\n```\n\n```ruby\n# app/models/recipient.rb\n\nclass Recipient\n  def initialize(name:)\n    @name = name\n  end\n\n  def name\n    @name\n  end\nend\n```\n\nThen, let's add a controller to implement the `show` action for invoices, as follows.\n\n```ruby\n# app/controllers/invoices_controller.rb\n\nclass InvoicesController \u003c ApplicationController\n  def show\n    recipient = Recipient.new(name: \"John Venturini\")\n    @invoice = Invoice.new(amount: 100, recipient: recipient)\n  end\nend\n```\n\nIf we provide a Handlebars template at `app/views/invoices/show.html.hbs`, then by Rails convention it will be used. Let's get the following snippet to be the content of the template:\n\n```hbs\n{{! app/views/invoices/show.html.hbs }}\n\n\u003ch1\u003eAmount #{{amount}} (issued on {{date}})\u003c/h1\u003e\n\u003ch2\u003eTo: {{recipient.name}}\u003c/h2\u003e\n```\n\nThe template in this example is only showing how it's possible to quickly put a simple view together, but there's much more to say about the language specification that is thoroughly covered in [docs/templates.md](docs/templates.md).\n\nAs you can see, there are regular Handlebars paths within curlies (`{{` and `}}`). In a situation where we'd use Handlebars we would have to provide a context as well, so that for instance `recipient.name` can be resolved to an actual value to replace `{{recipient.name}}`. Curlybars provides a context in a similar way [Curly](https://github.com/zendesk/curly) does: namely, using presenters like the following one.\n\n```ruby\n# app/presenters/invoices/show_presenter.rb\n\nclass Invoices::ShowPresenter \u003c Curlybars::Presenter\n  presents :invoice\n\n  allow_methods :amount, :date,\n    recipient: RecipientPresenter\n\n  def amount\n    @invoice.amount\n  end\n\n  def date\n    @invoice.created_at.to_formatted_s(:short)\n  end\n\n  def recipient\n    RecipientPresenter.new(@invoice.recipient)\n  end\nend\n```\n\nPresenters of this kind, extending `Curlybars::Presenter`, are referred to as root presenters. Note that root presenters will be looked up by Rails in a similar way views are. All the presenters have to reside in the `app/presenters` folder, within a subfolder named after the model they are used for (in this case, `invoices`) and have a name that follows the `\u003caction\u003e_presenter.rb` pattern. The presenter in the example above, for instance, would be looked up at `app/presenters/invoices/show_presenter.rb`.\n\nWhen extending `Curlybars::Presenter`, we get access to a convenience method `.allow_methods` to explicitly whitelist and describe whan can be done with this presenter. In the example above, we are declaring that from the template we can access the `amount` and `date` fields using curlies (namely, `{{amount}}` and `{{date}}`) and that an extra presenter can be accessed via `recipient` and traversed using the path syntax.\n\n`RecipientPresenter` would be a PORO presenter, and as such we only need to `extend Curlybars::MethodWhitelist` so that `allow_methods` can be used.\n\nYou can find more about root presenters, PORO presenters and `Curlybars::MethodWhitelist` on [docs/presenters.md](docs/presenters.md).\n\n```ruby\n# app/presenters/recipient_presenter.rb\n\nclass RecipientPresenter\n  extend Curlybars::MethodWhitelist\n\n  allow_methods :name\n\n  def initialize(recipient)\n    @recipient = recipient\n  end\n\n  def name\n    @recipient.name\n  end\nend\n```\n\nOn a side note, describing the accessible data via `.allow_methods` also allows Curlybars to perform templates validation: in other words, Curlybars is able to tell wether a template is going to be rendered successfully or not, by simply looking at the template itself and at what has been declared accessible.\n\nAt this point, running the Rails app and going to the page configured to show the page of this example, would render the Handlebars template as expected, according to what the presenter will provide.\n\nBesides data, presenters can also provide helper methods. A helper is a method that accepts arguments and/or options, and returns a value derivated from them. A detailed explanation about how helpers can be useful and which forms they come with, refer to [docs/helpers.md](docs/helpers.md).\n\nErrors can be raised when using Curlybars incorrectly, and you can refer to [docs/errors.md](docs/errors.md) for further details.\n\n## Configuration\n\nCurlybars offers configuration options aimed to fine-tune runtime constraints, useful for when you need to make sure a page rendering is aborted when the size reaches a certain limit, or when it takes too long.\n\nGetting some configuration in Rails is as simple as adding an initializer (e.g., `config/initializers/curlybars.rb`) having a similar content like the following.\n\n```ruby\nCurlybars.configure do |config|\n  config.output_limit = 2.megabytes\n  config.rendering_timeout = 5.seconds\nend\n```\n\nMore on configuration options can be found in [docs/configuration.md](docs/configuration.md).\n\n## Further documentation\n\n1. [Templates](docs/templates.md)\n1. [Presenters](docs/presenters.md)\n1. [Helpers](docs/helpers.md)\n1. [Configuration](docs/configuration.md)\n1. [Errors](docs/errors.md)\n\n## Contributing\n\nContributing to Curlybars is fairly easy, as long as the following steps are followed:\n\n1. Fork the project\n1. Create your feature branch (`git checkout -b my-new-feature`)\n1. Commit your changes (`git commit -am 'Add some feature'`)\n1. Push to the branch (`git push origin my-new-feature`)\n1. Create a new Pull Request\n1. Mention one or more of the maintainers to get the Pull Request approved and merged\n\n## Maintainers\n\nThis project is maintained by @zendesk/vikings\n\n## Releasing\n\n* `git checkout master \u0026\u0026 git pull`\n* Update changelog\n* Bump version in `lib/curlybars/version.rb`\n* Commit changes\n* `rake release`\n\n## More information\n\n* [Announcement blog post](https://developerblog.zendesk.com/curlybars-is-now-open-source-98145f192cea)\n\n## Copyright and License\n\nCopyright (c) 2017 Zendesk Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License.\n\nYou may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzendesk%2Fcurlybars","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzendesk%2Fcurlybars","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzendesk%2Fcurlybars/lists"}