{"id":13683417,"url":"https://github.com/amatsuda/jb","last_synced_at":"2025-05-13T18:05:42.575Z","repository":{"id":42234468,"uuid":"62391559","full_name":"amatsuda/jb","owner":"amatsuda","description":"A simple and fast JSON API template engine for Ruby on Rails","archived":false,"fork":false,"pushed_at":"2025-01-16T15:27:24.000Z","size":155,"stargazers_count":1302,"open_issues_count":18,"forks_count":43,"subscribers_count":29,"default_branch":"master","last_synced_at":"2025-04-25T14:50:33.858Z","etag":null,"topics":["jbuilder","json","rails","template-engine"],"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/amatsuda.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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,"zenodo":null}},"created_at":"2016-07-01T12:30:23.000Z","updated_at":"2025-04-07T14:51:26.000Z","dependencies_parsed_at":"2023-02-12T10:16:08.793Z","dependency_job_id":"6a5307e9-792a-49f0-85c3-8afc64c71745","html_url":"https://github.com/amatsuda/jb","commit_stats":{"total_commits":183,"total_committers":11,"mean_commits":"16.636363636363637","dds":0.06557377049180324,"last_synced_commit":"74218ed91d37de0db553d3df6da54971e7a63dc7"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amatsuda%2Fjb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amatsuda%2Fjb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amatsuda%2Fjb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amatsuda%2Fjb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amatsuda","download_url":"https://codeload.github.com/amatsuda/jb/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254000845,"owners_count":21997441,"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":["jbuilder","json","rails","template-engine"],"created_at":"2024-08-02T13:02:10.521Z","updated_at":"2025-05-13T18:05:42.554Z","avatar_url":"https://github.com/amatsuda.png","language":"Ruby","readme":"# Jb\n\nA simpler and faster Jbuilder alternative.\n\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'jb'\n```\n\nAnd bundle.\n\n\n## Usage\n\nPut a template file named `*.jb` in your Rails app's `app/views/*` directory, and render it.\n\n\n## Features\n\n* No original builder syntax that you have to learn\n* No `method_missing` calls\n* `render_partial` with :collection option actually renders the collection (unlike Jbuilder)\n\n\n## Syntax\n\nA `.jb` template should contain Ruby code that returns any Ruby Object that responds\\_to `to_json` (generally Hash or Array). Then the return value will be `to_json`ed to a JSON String.\n\n\n## Examples\n\nLet's start with a very simple one. Just write a Ruby Hash as a template:\n\n``` ruby\n{language: 'Ruby', author: {name: 'Matz'}}\n```\n\nThis renders the following JSON text:\n\n``` javascript\n{\"language\": \"Ruby\", \"author\": {\"name\": \"Matz\"}}\n```\n\nNote that modern Ruby Hash syntax pretty much looks alike JSON syntax. It's super-straight forward. Who needs a DSL to do this?\n\nNext one is a little bit advanced usage. The template doesn't have to be a single literal but can be any code that returns a Hash object:\n\n``` ruby\n# app/views/messages/show.json.jb\n\njson = {\n  content: format_content(@message.content),\n  created_at: @message.created_at,\n  updated_at: @message.updated_at,\n  author: {\n    name: @message.creator.name.familiar,\n    email_address: @message.creator.email_address_with_name,\n    url: url_for(@message.creator, format: :json)\n  }\n}\n\nif current_user.admin?\n  json[:visitors] = calculate_visitors(@message)\nend\n\njson[:comments] = @message.comments.map do |comment|\n  {\n    content: comment.content,\n    created_at: comment.created_at\n  }\nend\n\njson[:attachments] = @message.attachments.map do |attachment|\n  {\n    filename: attachment.filename,\n    url: url_for(attachment)\n  }\nend\n\njson\n```\n\nThis will build the following structure:\n\n``` javascript\n{\n  \"content\": \"10x JSON\",\n  \"created_at\": \"2016-06-29T20:45:28-05:00\",\n  \"updated_at\": \"2016-06-29T20:45:28-05:00\",\n\n  \"author\": {\n    \"name\": \"Yukihiro Matz\",\n    \"email_address\": \"matz@example.com\",\n    \"url\": \"http://example.com/users/1-matz.json\"\n  },\n\n  \"visitors\": 1326,\n\n  \"comments\": [\n    { \"content\": \"Hello, world!\", \"created_at\": \"2016-06-29T20:45:28-05:00\" },\n    { \"content\": \"\u003cscript\u003ealert('Hello, world!');\u003c/script\u003e\", \"created_at\": \"2016-06-29T20:47:28-05:00\" }\n  ],\n\n  \"attachments\": [\n    { \"filename\": \"sushi.png\", \"url\": \"http://example.com/downloads/sushi.png\" },\n    { \"filename\": \"sake.jpg\", \"url\": \"http://example.com/downloads/sake.jpg\" }\n  ]\n}\n```\n\nIf you want to define attribute and structure names dynamically, of course you still can do this with a Ruby Hash literal.\n\n``` ruby\n# model_name, column_name = :author, :name\n\n{model_name =\u003e {column_name =\u003e 'Matz'}}\n\n# =\u003e {\"author\": {\"name\": \"Matz\"}}\n```\n\nTop level arrays can be handled directly. Useful for index and other collection actions. And you know, Ruby is such a powerful language for manipulating collections:\n\n``` ruby\n# @comments = @post.comments\n\n@comments.reject {|c| c.marked_as_spam_by?(current_user) }.map do |comment|\n  {\n    body: comment.body,\n    author: {\n      first_name: comment.author.first_name,\n      last_name: comment.author.last_name\n    }\n  }\nend\n\n# =\u003e [{\"body\": \"🍣 is omakase...\", \"author\": {\"first_name\": \"Yukihiro\", \"last_name\": \"Matz\"}}]\n```\n\nJb has no special DSL method for extracting attributes from array directly, but you can do that with Ruby.\n\n``` ruby\n# @people = People.all\n\n@people.map {|p| {id: p.id, name: p.name}}\n\n# =\u003e [{\"id\": 1, \"name\": \"Matz\"}, {\"id\": 2, \"name\": \"Nobu\"}]\n```\n\nYou can use Jb directly as an Action View template language. When required in Rails, you can create views ala `show.json.jb`. You'll notice in the following example that the `.jb` template doesn't have to be one big Ruby Hash literal as a whole but it can be any Ruby code that finally returns a Hash instance.\n\n``` ruby\n# Any helpers available to views are available in the template\njson = {\n  content: format_content(@message.content),\n  created_at: @message.created_at,\n  updated_at: @message.updated_at,\n\n  author: {\n    name: @message.creator.name.familiar,\n    email_address: @message.creator.email_address_with_name,\n    url: url_for(@message.creator, format: :json)\n  }\n}\n\nif current_user.admin?\n  json[:visitors] = calculate_visitors(@message)\nend\n\njson\n```\n\nYou can use partials as well.  The following will render the file `views/comments/_comments.json.jb`, and set a local variable `comments` with all this message's comments, which you can use inside the partial.\n\n```ruby\nrender 'comments/comments', comments: @message.comments\n```\n\nIt's also possible to render collections of partials:\n\n```ruby\nrender partial: 'posts/post', collection: @posts, as: :post\n```\n\n\u003e NOTE: Don't use `render @post.comments` because if the collection is empty, `render` will return `nil` instead of an empty array.\n\nYou can pass any objects into partial templates with or without `:locals` option.\n\n```ruby\nrender 'sub_template', locals: {user: user}\n\n# or\n\nrender 'sub_template', user: user\n```\n\nYou can of course include Ruby `nil` as a Hash value if you want. That would become `null` in the JSON.\n\nYou can use `Hash#compact`/`!` method to prevent including `null` values in the output:\n\n```ruby\n{foo: nil, bar: 'bar'}.compact\n\n# =\u003e {\"bar\": \"bar\"}\n```\n\nIf you want to cache a template fragment, just directly call `Rails.cache.fetch`:\n\n```ruby\nRails.cache.fetch ['v1', @person], expires_in: 10.minutes do\n  {name: @person.name, age: @person.age}\nend\n```\n\n\n## The Generator\nJb extends the default Rails scaffold generator and adds some `.jb` templates. If you don't need them, please configure like so.\n\n```ruby\nRails.application.config.generators.jb false\n```\n\n\n## Why is Jb fast?\n\nJbuilder's `partial` + `:collection` [internally calls `array!` method](https://github.com/rails/jbuilder/blob/83a682aeebde96c6ef02ce742c0b97dc393f5e22/lib/jbuilder/jbuilder_template.rb#L85-L95)\ninside which [`_render_partial` is called per each element of the given collection](https://github.com/rails/jbuilder/blob/83a682aeebde96c6ef02ce742c0b97dc393f5e22/lib/jbuilder/jbuilder_template.rb#L93),\nand then it [falls back to the `view_context`'s `render` method](https://github.com/rails/jbuilder/blob/83a682aeebde96c6ef02ce742c0b97dc393f5e22/lib/jbuilder/jbuilder_template.rb#L100-L103).\n\nSo, for example if the collection has 100 elements, Jbuilder's `render partial:` performs `render` method 100 times, and so it calls `find_template` method (which is known as one of the heaviest parts of Action View) 100 times.\n\nOTOH, Jb simply calls [ActionView::PartialRenderer's `render`](https://github.com/rails/rails/blob/49a881e0db1ef64fcbae2b7ddccfd5ccea26ae01/actionview/lib/action_view/renderer/partial_renderer.rb#L423-L443) which is cleverly implemented to `find_template` only once beforehand, then pass each element to that template.\n\n\n## Benchmarks\nHere're the results of a benchmark (which you can find [here](https://github.com/amatsuda/jb/blob/master/test/dummy_app/app/controllers/benchmarks_controller.rb) in this repo) rendering a collection to JSON.\n\n### RAILS_ENV=development\n```\n% ./bin/benchmark.sh\n* Rendering 10 partials via render_partial\nWarming up --------------------------------------\n                  jb    15.000  i/100ms\n            jbuilder     8.000  i/100ms\nCalculating -------------------------------------\n                  jb    156.375  (± 7.0%) i/s -    780.000  in   5.016581s\n            jbuilder     87.890  (± 6.8%) i/s -    440.000  in   5.037225s\n\nComparison:\n                  jb:      156.4 i/s\n            jbuilder:       87.9 i/s - 1.78x slower\n\n\n* Rendering 100 partials via render_partial\nWarming up --------------------------------------\n                  jb    13.000  i/100ms\n            jbuilder     1.000  i/100ms\nCalculating -------------------------------------\n                  jb    121.187  (±14.0%) i/s -    598.000  in   5.049667s\n            jbuilder     11.478  (±26.1%) i/s -     54.000  in   5.061996s\n\nComparison:\n                  jb:      121.2 i/s\n            jbuilder:       11.5 i/s - 10.56x slower\n\n\n* Rendering 1000 partials via render_partial\nWarming up --------------------------------------\n                  jb     4.000  i/100ms\n            jbuilder     1.000  i/100ms\nCalculating -------------------------------------\n                  jb     51.472  (± 7.8%) i/s -    256.000  in   5.006584s\n            jbuilder      1.510  (± 0.0%) i/s -      8.000  in   5.383548s\n\nComparison:\n                  jb:       51.5 i/s\n            jbuilder:        1.5 i/s - 34.08x slower\n```\n\n\n### RAILS_ENV=production\n```\n% RAILS_ENV=production ./bin/benchmark.sh\n* Rendering 10 partials via render_partial\nWarming up --------------------------------------\n                  jb   123.000  i/100ms\n            jbuilder    41.000  i/100ms\nCalculating -------------------------------------\n                  jb      1.406k (± 4.2%) i/s -      7.134k in   5.084030s\n            jbuilder    418.360  (± 9.8%) i/s -      2.091k in   5.043381s\n\nComparison:\n                  jb:     1405.8 i/s\n            jbuilder:      418.4 i/s - 3.36x slower\n\n\n* Rendering 100 partials via render_partial\nWarming up --------------------------------------\n                  jb    37.000  i/100ms\n            jbuilder     5.000  i/100ms\nCalculating -------------------------------------\n                  jb    383.082  (± 8.4%) i/s -      1.924k in   5.061973s\n            jbuilder     49.914  (± 8.0%) i/s -    250.000  in   5.040364s\n\nComparison:\n                  jb:      383.1 i/s\n            jbuilder:       49.9 i/s - 7.67x slower\n\n\n* Rendering 1000 partials via render_partial\nWarming up --------------------------------------\n                  jb     4.000  i/100ms\n            jbuilder     1.000  i/100ms\nCalculating -------------------------------------\n                  jb     43.017  (± 9.3%) i/s -    216.000  in   5.080482s\n            jbuilder      4.604  (±21.7%) i/s -     23.000  in   5.082100s\n\nComparison:\n                  jb:       43.0 i/s\n            jbuilder:        4.6 i/s - 9.34x slower\n```\n\n\n### Summary\n\nAccording to the benchmark results, you can expect 2-30x performance improvement in development env, and 3-10x performance improvement in production env.\n\n\n## Contributing\n\nPull requests are welcome on GitHub at https://github.com/amatsuda/jb.\n\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famatsuda%2Fjb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famatsuda%2Fjb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famatsuda%2Fjb/lists"}