{"id":29954994,"url":"https://github.com/foca/granola","last_synced_at":"2025-08-03T17:09:08.017Z","repository":{"id":21091806,"uuid":"24391792","full_name":"foca/granola","owner":"foca","description":"Simple JSON serializers (Cereal-izers. GET IT?)","archived":false,"fork":false,"pushed_at":"2018-07-10T16:22:24.000Z","size":92,"stargazers_count":38,"open_issues_count":1,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-07-03T23:24:19.631Z","etag":null,"topics":["granola","json-serialization","lesscode","ruby"],"latest_commit_sha":null,"homepage":null,"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/foca.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-09-23T22:31:08.000Z","updated_at":"2024-11-17T02:02:57.000Z","dependencies_parsed_at":"2022-07-31T06:17:59.286Z","dependency_job_id":null,"html_url":"https://github.com/foca/granola","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/foca/granola","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foca%2Fgranola","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foca%2Fgranola/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foca%2Fgranola/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foca%2Fgranola/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/foca","download_url":"https://codeload.github.com/foca/granola/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foca%2Fgranola/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267418232,"owners_count":24083940,"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","status":"online","status_checked_at":"2025-07-27T02:00:11.917Z","response_time":82,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["granola","json-serialization","lesscode","ruby"],"created_at":"2025-08-03T17:09:07.243Z","updated_at":"2025-08-03T17:09:08.002Z","avatar_url":"https://github.com/foca.png","language":"Ruby","readme":"# Granola, a JSON serializer [![Build Status](https://img.shields.io/travis/foca/granola.svg)](https://travis-ci.org/foca/granola) [![RubyGem](https://img.shields.io/gem/dt/granola.svg)](https://rubygems.org/gems/granola)\n\n![A tasty bowl of Granola](https://cloud.githubusercontent.com/assets/437/4827156/9e8d33da-5f76-11e4-8574-7803e84845f2.JPG)\n\nGranola aims to provide a simple interface to generate JSON responses based on\nyour application's domain models. It doesn't make assumptions about anything and\ngets out of your way. You just write plain ruby.\n\n## Example\n\n``` ruby\nclass PersonSerializer \u003c Granola::Serializer\n  def data\n    {\n      \"name\" =\u003e object.name,\n      \"email\" =\u003e object.email,\n      \"age\" =\u003e object.age\n    }\n  end\nend\n\nPersonSerializer.new(person).to_json #=\u003e '{\"name\":\"John Doe\",...}'\n```\n\n## Install\n\n    gem install granola\n\n## JSON serialization\n\nGranola doesn't make assumptions about your code, so it shouldn't depend on a\nspecific JSON backend. It defaults to the native JSON backend, but you're free\nto change it. For example, if you were using [Oj][]:\n\n``` ruby\nGranola.render :json, via: Oj.method(:dump),\n                      content_type: \"application/json\"\n```\n\n[Oj]: https://github.com/ohler55/oj\n\n## Handling lists of entities\n\nA Granola serializer can handle a list of entities of the same type by using the\n`Serializer.list` method (instead of `Serializer.new`). For example:\n\n``` ruby\nserializer = PersonSerializer.list(Person.all)\nserializer.to_json #=\u003e '[{\"name\":\"John Doe\",...},{...}]'\n```\n\n## Rack Helpers\n\nIf your application is based on Rack, you can `require \"granola/rack\"` instead\nof `require \"granola\"`, and then simply `include Granola::Rack` to get access\nto the following interface:\n\n``` ruby\ngranola(person) #=\u003e This will infer PersonSerializer from a Person instance\ngranola(person, with: AnotherSerializer)\n```\n\nThis method returns a Rack response tuple that you can use like so (this example\nuses [Cuba][], but similar code will work for other frameworks):\n\n``` ruby\nrequire \"granola/rack\"\n\nCuba.plugin Granola::Rack\n\nCuba.define do\n  on get, \"users/:id\" do |id|\n    user = User[id]\n    halt granola(user)\n  end\nend\n```\n\n[Cuba]: http://cuba.is\n\n## Rails Support\n\nThe companion [Granola::Rails](https://github.com/foca/granola-rails) gem takes\ncare of support for Rails.\n\n## HTTP Caching\n\n`Granola::Serializer` gives you two methods that you can implement in your\nserializers: `last_modified` and `cache_key`.\n\nWhen using the `Granola::Rack` module, you should return a `Time` object from\nyour serializer's `last_modified`.  Granola will use this to generate the\nappropriate `Last-Modified` HTTP header.  Likewise, the result of `cache_key`\nwill be MD5d and set as the response's `ETag` header.\n\nIf you do this, you should also make sure that the [`Rack::ConditionalGet`][cg]\nmiddleware is in your Rack stack, as it will use these headers to avoid\ngenerating the JSON response altogether. For example, using Cuba:\n\n``` ruby\nclass UserSerializer \u003c Granola::Serializer\n  def data\n    { \"id\" =\u003e object.id, \"name\" =\u003e object.name, \"email\" =\u003e object.email }\n  end\n\n  def last_modified\n    object.updated_at\n  end\n\n  def cache_key\n    \"user:#{object.id}:#{object.updated_at.to_i}\"\n  end\nend\n\nCuba.plugin Granola::Rack\nCuba.use Rack::ConditionalGet\n\nCuba.define do\n  on get, \"users/:id\" do |id|\n    halt granola(User[id])\n  end\nend\n```\n\nThis will avoid generating the JSON response altogether if the user sends the\nappropriate `If-Modified-Since` or `If-None-Match` headers.\n\n[cg]: http://www.rubydoc.info/github/rack/rack/Rack/ConditionalGet\n\n## Caching of serialized bodies\n\nIf you are generating responses that are particularly expensive to serialize,\nyou can use the [Granola::Cache](https://github.com/foca/granola-cache) gem to\nstore their representations once generated in an external cache.\n\n## Different Formats\n\nAlthough Granola out of the box only ships with JSON serialization support, it's\neasy to extend and add support for different types of serialization in case your\nAPI needs to provide multiple formats. For example, in order to add MsgPack\nsupport (via the [msgpack-ruby][] library), you'd do this:\n\n``` ruby\nrequire \"msgpack\"\n\nGranola.render :msgpack, via: MessagePack.method(:pack),\n                         content_type: \"application/x-msgpack\"\n```\n\nNow all serializers can be serialized into MsgPack using a `to_msgpack` method.\nIn order to use this from our Rack helpers, you'd do:\n\n``` ruby\ngranola(object, as: :msgpack)\n```\n\nThis will set the correct MIME type.\n\nIf you don't explicitly set a format when rendering via the rack helper, Granola\nwill use the request's `Accept` header to choose the best format for rendering.\n\nFor example, given a request with the following header:\n\n    Accept: text/x-yaml;q=0.5,application/x-msgpack;q=0.8,*/*;q=0.2\n\nGranola will check first if you have a renderer registered for the\n`application/x-msgpack` MIME type, and then check for a renderer for the\n`text/x-yaml` MIME type. If none of these are registered, it will default to\nrendering JSON.\n\n[msgpack-ruby]: https://github.com/msgpack/msgpack-ruby\n\n## License\n\nThis project is shared under the MIT license. See the attached [LICENSE][] file\nfor details.\n\n[LICENSE]: ./LICENSE\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoca%2Fgranola","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffoca%2Fgranola","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoca%2Fgranola/lists"}