{"id":15603237,"url":"https://github.com/ccocchi/rabl-rails","last_synced_at":"2025-05-16T18:09:24.671Z","repository":{"id":2536409,"uuid":"3513529","full_name":"ccocchi/rabl-rails","owner":"ccocchi","description":"Rails 4.2+ templating system with JSON, XML and Plist support.","archived":false,"fork":false,"pushed_at":"2023-05-20T14:27:45.000Z","size":333,"stargazers_count":208,"open_issues_count":8,"forks_count":51,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-12T16:59:48.877Z","etag":null,"topics":["json","rabl-template","rails","ruby"],"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/ccocchi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2012-02-22T10:31:39.000Z","updated_at":"2024-07-31T17:28:05.000Z","dependencies_parsed_at":"2023-07-06T19:01:45.989Z","dependency_job_id":null,"html_url":"https://github.com/ccocchi/rabl-rails","commit_stats":{"total_commits":237,"total_committers":9,"mean_commits":"26.333333333333332","dds":0.04641350210970463,"last_synced_commit":"12c7103432f734acff949d514045194b408d9736"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccocchi%2Frabl-rails","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccocchi%2Frabl-rails/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccocchi%2Frabl-rails/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccocchi%2Frabl-rails/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ccocchi","download_url":"https://codeload.github.com/ccocchi/rabl-rails/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254582907,"owners_count":22095518,"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":["json","rabl-template","rails","ruby"],"created_at":"2024-10-03T03:02:07.711Z","updated_at":"2025-05-16T18:09:24.616Z","avatar_url":"https://github.com/ccocchi.png","language":"Ruby","readme":"# RABL for Rails [![Build Status](https://travis-ci.org/ccocchi/rabl-rails.svg?branch=master)](https://travis-ci.org/ccocchi/rabl-rails)\n\n`rabl-rails` is a ruby templating system for rendering your objects in different format (JSON, XML, PLIST).\n\nThis gem aims for speed and little memory footprint while letting you build complex response with a very intuitive DSL.\n\n`rabl-rails` targets **Rails 4.2/5/6 application** and have been testing with MRI and jRuby.\n\n## Installation\n\nInstall as a gem :\n\n```\ngem install rabl-rails\n```\n\nor add directly to your `Gemfile`\n\n```\ngem 'rabl-rails', '~\u003e 0.6.0'\n```\n\n## Overview\n\nThe gem enables you to build responses using views like you would using HTML/erb/haml.\nAs example, assuming you have a `Post` model filled with blog posts, and a `PostController` that look like this:\n\n```ruby\nclass PostController \u003c ApplicationController\n  def index\n\t @posts = Post.order('created_at DESC')\n  end\nend\n```\n\nYou can create the following RABL-rails template to express the API output of `@posts`\n\n```ruby\n# app/views/post/index.rabl\ncollection :@posts\n\nattributes :id, :title, :subject\nchild(:user) { attributes :full_name }\nnode(:read) { |post| post.read_by?(@user) }\n```\n\nThis would output the following JSON when visiting `http://localhost:3000/posts.json`\n\n```js\n[{\n  \"id\" : 5, title: \"...\", subject: \"...\",\n  \"user\" : { full_name : \"...\" },\n  \"read\" : true\n}]\n```\n\n## How it works\n\nThis gem separates compiling, ie. transforming a RABL-rails template into a Ruby hash, and the actual rendering of the object or collection. This allows to only compile the template once (when template caching is enabled) which is the slow part, and only use hashes during rendering.\n\nThe drawback of compiling the template outside of any rendering context is that we can't access instance variables like usual. Instead, you'll mostly use symbols representing your variables and the gem will retrieve them when needed.\n\nThere are places where the gem allows for \"dynamic code\" -- code that is evaluated at each rendering, such as within `node` or `condition` blocks.\n\n```ruby\n# We reference the @posts varibles that will be used at rendering time\ncollection :@posts\n\n# Here you can use directly the instance variable because it\n# will be evaluated when rendering the object\nnode(:read) { |post| post.read_by?(@user) }\n```\n\nThe same rule applies for view helpers such as `current_user`\n\nAfter the template is compiled into a hash, `rabl-rails` will use a renderer to create the actual output. Currently, JSON, XML and PList formats are supported.\n\n## Configuration\n\nRablRails works out of the box, with default options and fastest engine available (oj, libxml). But depending on your needs, you might want to change that or how your output looks like. You can set global configuration in your application:\n\n```ruby\n# config/initializers/rabl_rails.rb\n\nRablRails.configure do |config|\n  # These are the default\n  # config.cache_templates = true\n  # config.include_json_root = true\n  # config.json_engine = ::Oj\n  # config.xml_options = { :dasherize =\u003e true, :skip_types =\u003e false }\n  # config.enable_jsonp_callbacks = false\n  # config.replace_nil_values_with_empty_strings = false\n  # config.replace_empty_string_values_with_nil = false\n  # config.exclude_nil_values = false\n  # config.non_collection_classes = Set.new(['Struct'])\nend\n```\n\n## Usage\n\n### Data declaration\n\nTo declare data to use in the template, you can use either `object` or `collection` with the symbol name or your data.\n\n```ruby\n# app/views/users/show.json.rabl\nobject :@user\n\n# app/views/users/index.json.rabl\ncollection :@users\n```\n\nYou can specify root label for the collection using hash or `:root` option\n\n```ruby\ncollection :@posts, root: :articles\n#is equivalent to\ncollection :@posts =\u003e :articles\n\n# =\u003e { \"articles\" : [{...}, {...}] }\n```\n\nThere are rares cases when the template doesn't map directly to any object. In these cases, you can set data to false.\n\n```ruby\nobject false\nnode(:some_count) { |_| @user.posts.count }\nchild(:@user) { attribute :name }\n```\n\nIf you use gems like *decent_exposure* or *focused_controller*, you can use your variable directly without the leading `@`\n\n```ruby\nobject :object_exposed\n```\n\n### Attributes / Methods\n\nAdds a new field to the response object, calling the method on the object being rendered. Methods called this way should return natives types from the format you're using (such as `String`, `integer`, etc for JSON). For more complex objects, see `child` nodes.\n\n```ruby\nattributes :id, :title, :to_s\n```\n\nYou can aliases these attributes in your response\n\n```ruby\nattributes :my_custom_method, as: :title\n# =\u003e { \"title\" : \u003cresult of my_custom_method\u003e }\n```\n\nor show attributes based on a condition. The currently rendered object is given to the `proc` condition.\n\n```ruby\nattributes :published_at, :anchor, if: -\u003e(post) { post.published? }\n```\n\n### Child nodes\n\nChanges the object being rendered for the duration of the block. Depending on if you use `node` or `glue`, the result will be added as a new field or merged respectively.\n\nData passed can be a method or a reference to an instance variable.\n\nFor example if you have a `Post` model that belongs to a `User` and want to add the user's name to your response.\n\n```ruby\nobject :@post\n\nchild(:user, as: :author) do\n\tattributes :name\nend\n# =\u003e { \"post\": { \"author\" : { \"name\" : \"John D.\" } } }\n```\n\nIf instead of having an `author` node in your response you wanted the name at the root level, you can use `glue`:\n\n```ruby\nobject :@post\n\nglue(:user) do\n  attributes :name, as: :author_name\nend\n# =\u003e { \"post\": { \"author_name\" : \"John D.\" } }\n```\n\nArbitrary data source can also be passed:\n\n```ruby\n# in your controller\n# @custom_data = [...]\n\n# in the view\nchild(:@custom_data) do\n\tattributes :id, :name\nend\n# =\u003e { \"custom_data\": [...] }\n```\n\nYou can use a Hash-like data source, as long as keys match a method or attribute of your main resource, using the `fetch` keyword:\n\n```ruby\n# assuming you have something similar in your controller\n# @users_hash = { 1 =\u003e User.new(pseudo: 'Batman') }\n\n# in the view\nobject :@post\n\nfetch(:@users_hash, as: :user, field: :user_id) do\n  attributes :pseudo\nend\n# =\u003e { user: { pseudo: 'Batman' } }\n```\n\nThis comes very handy when adding attributes from external queries not really bound to a relation, like statistics.\n\n### Constants\n\nAdds a new field to the response using an immutable value.\n\n```ruby\nconst(:api_version, API::VERSION)\nconst(:locale, 'fr_FR')\n```\n\n### Lookups\n\nAdds a new field to the response, using rendered resource's id by default or any method to fetch a value from the given hash variable.\n\n```ruby\ncollection :@posts\n\nlookup(:comments_count, :@comments_count, field: :uuid, cast: false)\n# =\u003e [{ \"comments_count\": 3 }, { \"comments_count\": 6 }]\n```\n\nIn the example above, for each post it will fetch the value from `@comments_count` using the post's `uuid` as key. When the `cast` value is set to `true` (it is `false` by default), the value will be casted to a boolean using `!!`.\n\n\n### Custom nodes\n\nAdds a new field to the response with block's result as value.\n\n```ruby\nobject :@user\nnode(:full_name) { |u| u.first_name + \" \" + u.last_name }\n# =\u003e { \"user\" : { \"full_name\" : \"John Doe\" } }\n```\n\nYou can add condition on your custom nodes. If the condition evaluates to a falsey value, the node will not added to the response at all.\n\n```ruby\nnode(:email, if: -\u003e(u) { u.valid_email? }) do |u|\n\tu.email\nend\n```\n\nNodes are evaluated at rendering time, so you can use any instance variables or view helpers within them\n\n```ruby\nnode(:url) { |post| post_url(post) }\n```\n\nIf the result of the block is a Hash, it can be directly merge into the response using `merge` instead of `node`\n\n```ruby\nobject :@user\nmerge { |u| { name: u.first_name + \" \" + u.last_name } }\n# =\u003e { \"user\" : { \"name\" : \"John Doe\" } }\n```\n\n### Extends \u0026 Partials\n\nOften objects have a basic representation that is shared accross different views and enriched according to it. To avoid code redundancy you can extend your template from any other RABL template.\n\n```ruby\n# app/views/shared/_user.rabl\nattributes :id, :name\n\n# app/views/users/show.rabl\nobject :@user\n\nextends('shared/_user')\nattributes :super_secret_attribute\n\n#=\u003e { \"id\": 1, \"name\": \"John\", \"super_secret_attribute\": \"Doe\" }\n```\n\nWhen used with child node, if they are the only thing added you can instead use the `partial` option directly.\n\n```ruby\nchild(:user, partial: 'shared/_user')\n\n# is equivalent to\n\nchild(:user) do\n  extends('shared/_user')\nend\n```\n\nExtends can be used dynamically using rendered object and lambdas.\n\n```ruby\nextends -\u003e(user) { \"shared/_#{user.client_type}_infos\" }\n```\n\nPartials can also be used inside custom nodes. When using partial this way, you MUST declare the `object` associated to the partial\n\n```ruby\nnode(:location) do |user|\n\t{ city: user.city, address: partial('users/address', object: m.address) }\nend\n```\n\nWhen used this way, partials can take locals variables that can be accessed in the included template.\n\n```ruby\n# _credit_card.rabl\nnode(:credit_card, if: -\u003e(u) { locals[:display_credit_card] }) do |user|\n  user.credit_card_info\nend\n\n# user.json.rabl\nmerge { |u| partial('_credit_card', object: u, locals: { display_credit_card: true }) }\n```\n\n### Putting it all together\n\n`rabl-rails` allows you to format your responses easily, from simple objects to hierarchy of 2 or 3 levels.\n\n```ruby\nobject :@thread\n\nattribute :caption, as: :title\n\nchild(:@sorted_posts, as: :posts) do\n  attributes :title, :slug\n\n  child :comments do\n\t\textends 'shared/_comment'\n    lookup(:upvotes, :@upvotes_per_comment)\n\tend\nend\n```\n\n### Other features\n\n* [Caching](https://github.com/ccocchi/rabl-rails/wiki/Caching)\n\nAnd more in the [WIKI](https://github.com/ccocchi/rabl-rails/wiki)\n\n## Performance\n\nBenchmarks have been made using this [application](http://github.com/ccocchi/rabl-benchmark), with rabl 0.13.1 and rabl-rails 0.5.0\n\nOverall, rabl-rails is **10% faster and use 10% less memory**, but these numbers skyrockets to **50%** when using `extends` with collection of objects.\n\nYou can see full tests on test application repository.\n\n## Authors and contributors\n\n* [Christopher Cocchi-Perrier](http://github.com/ccocchi) - Creator of the project\n\nWant to add another format to Rabl-rails ? Checkout [JSON renderer](http://github.com/ccocchi/rabl-rails/blob/master/lib/rabl-rails/renderers/json.rb) for reference\nWant to make another change ? Just fork and contribute, any help is very much appreciated. If you found a bug, you can report it via the Github issues.\n\n## Original idea\n\n* [RABL](http://github.com/nesquena/rabl) Standart RABL gem. I used it a lot but I needed to improve my API response time, and since most of the time was spent in view rendering, I decided to implement a faster rabl gem.\n\n## Copyright\n\nCopyright © 2012-2020 Christopher Cocchi-Perrier. See [MIT-LICENSE](http://github.com/ccocchi/rabl-rails/blob/master/MIT-LICENSE) for details.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fccocchi%2Frabl-rails","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fccocchi%2Frabl-rails","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fccocchi%2Frabl-rails/lists"}