{"id":13879798,"url":"https://github.com/CultureHQ/adequate_serialization","last_synced_at":"2025-07-16T15:33:06.996Z","repository":{"id":32437641,"uuid":"133739214","full_name":"CultureHQ/adequate_serialization","owner":"CultureHQ","description":"Serializes objects adequately","archived":false,"fork":false,"pushed_at":"2023-03-06T10:58:53.000Z","size":392,"stargazers_count":18,"open_issues_count":13,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-11-15T05:05:39.407Z","etag":null,"topics":["rails","serialization"],"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/CultureHQ.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-05-17T00:54:09.000Z","updated_at":"2022-01-07T10:07:32.000Z","dependencies_parsed_at":"2023-02-14T16:01:53.506Z","dependency_job_id":null,"html_url":"https://github.com/CultureHQ/adequate_serialization","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CultureHQ%2Fadequate_serialization","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CultureHQ%2Fadequate_serialization/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CultureHQ%2Fadequate_serialization/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CultureHQ%2Fadequate_serialization/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CultureHQ","download_url":"https://codeload.github.com/CultureHQ/adequate_serialization/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226143895,"owners_count":17580245,"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":["rails","serialization"],"created_at":"2024-08-06T08:02:33.763Z","updated_at":"2024-11-24T08:31:48.612Z","avatar_url":"https://github.com/CultureHQ.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# AdequateSerialization\n\n[![Build Status](https://github.com/CultureHQ/adequate_serialization/workflows/Main/badge.svg)](https://github.com/CultureHQ/adequate_serialization/actions)\n[![Gem Version](https://img.shields.io/gem/v/adequate_serialization.svg)](https://github.com/CultureHQ/adeqaute_serialization)\n\n`AdequateSerialization` allows you to define serializers that will convert your objects into simple hashes that are suitable for variable purposes such as caching or using in an HTTP response. It stems from the simple idea of giving slightly more control over the `as_json` method that gets called when objects are serialized using Rails' default controller serialization.\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Defining attributes](#defining-attributes)\n    - [:if](#if)\n    - [:unless](#unless)\n    - [:optional](#optional)\n  - [Attaching objects](#attaching-objects)\n  - [Usage with Rails](#usage-with-rails)\n    - [Cache busting](#cache-busting)\n    - [Caching plain objects](#caching-plain-objects)\n  - [Advanced](#advanced)\n- [Development](#development)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'adequate_serialization'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install adequate_serialization\n\n## Usage\n\nThere are two ways to define the serialization process for objects.\n\nFor larger objects where it makes sense to define the serialization in a separate class, you should include the `AdequateSerialization::Serializable` in the object that you want to be able to serialize. Then, define a serializer matching the name of that object, postfixed with `\"Serializer\"`, as in:\n\n```ruby\nclass UserSerializer \u003c AdequateSerialization::Serializer\n  attribute :id, :name, :title\nend\n```\n\nFor smaller objects where it makes sense to define the serialization inline, you can include the result of the `AdequateSerialization::inline` method, as in:\n\n```ruby\nclass User\n  include AdequateSerialization.inline { attribute :id, :name, :title }\n\n  ...\nend\n```\n\nFor both types of serialization definition, you can then use the `AdequateSerialization` DSL to define the attributes that are available to the serializer. You can then call `as_json` on any instance of that object to get the resultant hash. Below is an example:\n\n```ruby\nUser.new(id: 1, name: 'Clark Kent', title: 'Superman').as_json\n# =\u003e {:id=\u003e1, :name=\u003e\"Clark Kent\", :title=\u003e\"Superman\"}\n```\n\n### Defining attributes\n\nThe `AdequateSerialization::Serializer` DSL is just the one `attribute` method. You can pass as many names as you want, and each attribute will become a key in the resultant serialized hash. If you need to build a \"synthesized\" attribute (one that is defined in the serializer), you can do so with a block that receives the object as an argument, as in:\n\n```ruby\nclass UserSerializer \u003c AdequateSerialization::Serializer\n  attribute :double_name do |user|\n    user.name * 2\n  end\nend\n```\n\nThere are also a couple of options that you can pass to the `attribute` method as the last argument that modify the serializer's behavior, listed below.\n\n#### :if\n\nIf you pass an `:if` condition, that method will be called on the serializable object to determine whether or not that attribute should be included in the resultant hash, as in:\n\n```ruby\nclass UserSerializer \u003c AdequateSerialization::Serializer\n  attribute :title, if: :manager?\nend\n\nuser = User.new(...)\nuser.as_json\n# =\u003e {:id=\u003e1, :name=\u003e\"Clark Kent\"}\n\nuser.update(manager: true)\nuser.as_json\n# =\u003e {:id=\u003e1, :name=\u003e\"Clark Kent\", :title=\u003e\"Superman\"}\n```\n\n#### :unless\n\nThis is the same as the `:if` option, but will result in the opposite behavior (the attribute will be present if the predicate is not met).\n\n#### :optional\n\nThere are times when you want to include an attribute that you normally wouldn't. For example, if you have both `Post` and `Comment` objects, normally you wouldn't include the `post` attribute on the child `comment` objects. However, if you're serializing just the comment, it might be useful to have the `post` attached. In this case, you could mark the attribute as `optional` and it would only be included if it was listed in the `:includes` option passed to the `as_json` method, as in:\n\n```ruby\nclass PostSerializer \u003c AdequateSerialization::Serializer\n  attribute :id, :title, :body\n  attribute :comments, optional: true\nend\n\nclass CommentSerializer \u003c AdequateSerialization::Serializer\n  attribute :id, :body\n  attribute :post, optional: true\nend\n\ncomment = Comment.new(...)\ncomment.as_json\n# =\u003e {:id=\u003e1, :body=\u003e\"This is a great gem!\"}\n\ncomment.as_json(includes: :post)\n# =\u003e {:id=\u003e1, :body=\u003e\"This is a great gem!\", :post=\u003e{:id=\u003e1, :title=\u003e\"Introducing Adequate Serializer\", :body=\u003e\"This is adequate serializer.\"}}\n```\n\nThe `includes` key can take either a single name or an array of names.\n\n### Attaching objects\n\nThere are times where it's more performant to serialize the objects using normal serialization and to attach an additional attribute later. For instance, you could serialize all of the posts and then attach whether or not a user had upvoted them. In that case, there's a special syntax that looks like the below:\n\n```ruby\nupvotes =\n  User.upvotes.each_with_object({}) do |post, votes|\n    votes[post.id] = true\n  end\n# =\u003e {1=\u003etrue}\n\nPost.all.map(\u0026:as_json)\n# =\u003e [{:id=\u003e1}, {:id=\u003e2}]\n\nposts = Post.all.map { |post| post.as_json(attach: { upvoted: upvotes }) }\n# =\u003e [{:id=\u003e1, :upvoted=\u003etrue}, {:id=\u003e2, :upvoted=\u003efalse}]\n```\n\nThis relies on the objects to which you are attaching having an `id` attribute and the attachable hash being an index of `id` pointing to the attribute value.\n\n### Usage with Rails\n\nIf `::Rails` is defined when `adequate_serialization` is required, it will hook into `ActiveRecord` in three ways:\n\n1. By including `AdequateSerializer::Serializable` in `ActiveRecord::Base` so that all of your models will be serializable by overwriting `ActiveRecord::Base`'s `as_json` method, which by default will use `Rails.cache.fetch`.\n2. By overwriting `ActiveRecord::Relation`'s `as_json` method to use the `AdequateSerializer::Rails::RelationSerializer` object, which by default will use the `Rails.cache.fetch_multi` method in order to more efficiently serialize all of the records in the relation.\n3. By introducing cache busting behavior in the background using `ActiveJob` if you're serializing objects outside of a one-to-many relationship.\n\n#### Cache busting\n\nWhen using `adequate_serialization` with `rails`, each `attribute` call will check if you're serializing an association. If you are, then it will ensure you have appropriate caching behavior enabled:\n\n* If it's a `has_many` or `has_one` association, then it will make sure that the inverse has the `touch: true` option on the association.\n* If it's a `belongs_to` association, then it will add an `after_update_commit` hook to the inverse class that will loop through the associated objects and bust the association using an `ActiveJob` task.\n\nYou can visualize this cache busting behavior with a prebaked Rack application that is shipped with this gem by adding the following to your `config/routes.rb` file:\n\n```ruby\nif Rails.env.development?\n  mount AdequateSerialization::Rails::CacheVisualization,\n        at: '/cache_visualization'\nend\n```\n\nThis will allow you to view which caches will bust which others in development by navigating to your application's `/cache_visualization` path.\n\n#### Caching plain objects\n\nYou can still use plain objects to be serialized, and if you want to take advantage of the caching behavior, you can define a `cache_key` method on the objects that you're serializing. This will cause `AdequateSerialization` to start putting them into the Rails cache.\n\nThe result is that you can now this in your controllers:\n\n```ruby\nclass UsersController\n  def show\n    user = User.find(params[:id])\n\n    render json: { user: user }\n  end\nend\n```\n\nand the response will be the serialized user. You can pass additional options that will get forwarded on to the serializer as well, as in:\n\n```ruby\nclass UsersController\n  def show\n    user = User.find(params[:id])\n\n    render json: { user: user }, includes: :title\n  end\nend\n```\n\nand the result will now contain the `title` attribute (provided it was configured as an optional attribute). All options that previously were passed in to the `as_json` method get forwarded appropriately.\n\n### Advanced\n\nThe serialization process happens through a series of `AdequateSerialization::Steps`. The caching behavior mentioned in the `Usage with Rails` section is one such step that gets introduced. You can introduce more yourself like so:\n\n```ruby\nclass LoggingStep \u003c AdequateSerialization::Steps::Step\n  def apply(response)\n    Logger.log(\"#{response.object} is being serialized with #{response.opts} options\")\n    apply_next(response)\n  end\nend\n\nAdequateSerialization.prepend(LoggingStep)\n```\n\nThis will cause this object to be placed into the list of steps taken to serialize objects, and can be used for much more powerful and advanced workflows.\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/CultureHQ/adequate_serialization.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCultureHQ%2Fadequate_serialization","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FCultureHQ%2Fadequate_serialization","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCultureHQ%2Fadequate_serialization/lists"}