{"id":18368773,"url":"https://github.com/greena13/tint","last_synced_at":"2025-08-10T21:05:55.419Z","repository":{"id":56897038,"uuid":"48949977","full_name":"greena13/tint","owner":"greena13","description":"Declarative object decorators for JSON APIs","archived":false,"fork":false,"pushed_at":"2019-04-14T15:09:50.000Z","size":22,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-01T11:53:32.339Z","etag":null,"topics":["decorators","draper","json-api","rails"],"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/greena13.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}},"created_at":"2016-01-03T15:09:53.000Z","updated_at":"2019-04-14T15:09:52.000Z","dependencies_parsed_at":"2022-08-20T17:40:31.305Z","dependency_job_id":null,"html_url":"https://github.com/greena13/tint","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/greena13/tint","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greena13%2Ftint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greena13%2Ftint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greena13%2Ftint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greena13%2Ftint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/greena13","download_url":"https://codeload.github.com/greena13/tint/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greena13%2Ftint/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269787316,"owners_count":24475716,"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-08-10T02:00:08.965Z","response_time":71,"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":["decorators","draper","json-api","rails"],"created_at":"2024-11-05T23:27:24.646Z","updated_at":"2025-08-10T21:05:55.396Z","avatar_url":"https://github.com/greena13.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://svgshare.com/i/CTW.svg\" width=\"200px\" /\u003e\u003cbr/\u003e\n  \u003ch2 align=\"center\"\u003eTint\u003c/h2\u003e\n\u003c/p\u003e\n\nEasily define object decorators for JSON APIs using simple declarative syntax\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n    gem 'tint'\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install tint\n\n## Usage\n\nYou can use Tint by creating a decorator class that inherits from `Tint::Decorator`\n\n## Defining attributes\n\nTo include methods and attributes available on the decorated object, simply list them using `attributes`.\n\n```ruby\n# decorators/user_decorator.rb\nclass UserDecorator \u003c Tint::Decorator\n  attributes :username, :first_name, :last_name\nend\n```\n\nYou can map attributes to different names on the decorator by providing a hash as the final argument\n\n```ruby\n# decorators/user_decorator.rb\nclass UserDecorator \u003c Tint::Decorator\n  attributes :username, :first_name, last_name: :surname # object.surname will be available as ['last_name']\nend\n```\n\n## Defining attributes for *_ids\n\nThe `ids_for` method adds `*Ids` values to a JSON object and ensures the corresponding association is automatically eager loaded:\n\n```ruby\nclass ProductDecorator \u003c Tint::Decorator\n  attributes :id, :description, :price\n\n  ids_for :sales # will add a saleIds attribute to the JSON object\nend\n```\n\nIt's possible to give the `*Ids` attribute a different name by providing the name of the association as a key-value pair:\n\n\n```ruby\nclass ProductDecorator \u003c Tint::Decorator\n  attributes :id, :description, :price\n\n  ids_for sales: :purchase_identifiers # will add a purchaseIdentifiers attribute to the JSON object\nend\n```\n\n## Defining custom methods\n\nTint will use a decorator instance method in preference to one defined on the decorated object, so it is possible to customise how a particular attribute appears. The original definition of the attribute is available via the `object` instance variable.\n\n```ruby\nclass ProductDecorator \u003c Tint::Decorator\n  attributes :id, :description, :price\n\n  def price\n    \"$\" + object.price\n  end\nend\n```\n\nIt's also possible to define methods that are not available on the decorated object at all.\n\n```ruby\nclass ProductDecorator \u003c Tint::Decorator\n  attributes :id, :description, :on_sale\n\n  def on_sale\n    SaleItems.include?(object)\n  end\nend\n```\n\n## Defining associations\n\nThe `decorates_association` method is used for declaring associations and delegating them to other decorators.\n\n```ruby\n# decorators/product_decorator.rb\nclass ProductDecorator \u003c Tint::Decorator\n  attributes :id, :description\nend\n\n# decorators/user_decorator.rb\nclass UserDecorator \u003c Tint::Decorator\n  attributes :username\n\n  decorates_association :products\nend\n```\n\nBy default, `decorates_association` uses the name of the association to guess the decorator it should use. In this case it will use `ProductDecorator`, but if you wished to render with `SpecialProductDecorator`, the `with` option may be used:\n\n```ruby\ndecorates_association :product, with: SpecialProductDecorator\n```\n\nYou can decorate an association and make it available under a different name using the `as` option:\n\n```ruby\ndecorates_association :product, as: :sale_item\n```\n\nMultiple associations can be defined in the same statement using `decorates_associations`. Either, using interpolation to locate the correct decorator for each:\n\n```ruby\ndecorates_associations :product, :address\n```\n\nOr using the same decorator (in this case, `AddressDecorator`):\n\n```ruby\ndecorates_associations :previous_address, :current_address, with: AddressDecorator\n```\n\n## Eager loading associations\n\nWhen you declare a new association using `decorates_association` or `decorates_associations`, Tint automatically eager loads the associations when the decorator is rendered as JSON and automatically prevents many N+1 queries. It does this by maintaining a list `eager_loads` which is available on all decorators.\n\n```ruby\nclass UserDecorator \u003c Tint::Decorator\n  attributes :username\n\n  decorates_association :products\nend\n\n\nUserDecorator.eager_loads # [:products]\n```\n\nIf you need to manually add to the list of associations which are eager loaded for any reason, you can do so using the `eager_load` method\n\n```ruby\nclass UserDecorator \u003c Tint::Decorator\n  attributes :username\n\n  decorates_association :products\n\n  eager_load :addresses\nend\n\nUserDecorator.eager_loads # [:products, :addresses]\n```\n\n## Decorating a single instance\n\nTint maintains the interface defined by Draper for decorating objects. To decorate a single object, use the `decorate` method.\n\nUsing the `UserDecorator` defined above:\n\n```ruby\n# controllers/users_controller.rb\n\nclass UsersController \u003c ApplicationController\n\n  def show\n    @user = User.find(params[:id]) #\u003cUser username: \"john_doe\", first_name: \"John\", surname: \"Doe\"\u003e\n\n    render json: UserDecorator.decorate(@user) # { username: \"john_doe\", firstName: \"John\", lastName: \"Doe\" }\n  end\n\nend\n```\n\n`decorate` also accepts an optional has of options. For more information about the supported options, see the [Draper documentation](https://github.com/drapergem/draper#adding-context).\n\n\n## Decorating a collection\n\nThe `decorate_collection` method is used for decorating an instance of ActiveRecord::Relation (or any class that implements the same interface). It accepts all of the same options as the `decorate` method.\n\n```ruby\n# controllers/users_controller.rb\n\nclass UsersController \u003c ApplicationController\n\n  def index\n    @users = User.all\n\n    render json: UserDecorator.decorate_collection(@users) # [ { username: \"john_doe\", firstName: \"John\", lastName: \"Doe\" }, ... ]\n  end\n\nend\n```\n\n## Configuration\n\nBy default, Tint camelizes attribute names, however it's possible to configure Tint to use any of the following capitalization conventions:\n\n```ruby\nTint.configuration do |config|\n  # attribute_naMe123 ==\u003e attributeNaMe123 (Default)\n  # config.attribute_capitalization = :camel_case\n\n  # attribute_naMe123 ==\u003e attribute_na_me123\n  # config.attribute_capitalization = :snake_case\n\n  # attribute_naMe123 ==\u003e attribute-naMe123\n  # config.attribute_capitalization = :kebab_case\n\n  # Converts symbols to strings\n  # config.attribute_capitalization = :none\nend\n```\n\n## Running the test suite\n\nThe test suite may be run simply using\n\n    rspec\n\n## Contributions\n\nTint remains in its infancy and all pull requests, issues and feedback are welcome and appreciated.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreena13%2Ftint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgreena13%2Ftint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreena13%2Ftint/lists"}