{"id":18103291,"url":"https://github.com/pragmarb/pragma-decorator","last_synced_at":"2025-04-13T19:23:04.677Z","repository":{"id":56888640,"uuid":"73838421","full_name":"pragmarb/pragma-decorator","owner":"pragmarb","description":"Powerful and elegant model presenters with a twist.","archived":false,"fork":false,"pushed_at":"2020-02-01T14:17:16.000Z","size":195,"stargazers_count":4,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-04-23T18:34:23.035Z","etag":null,"topics":["api","decorator","pragma","ruby","ruby-on-rails","trailblazer"],"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/pragmarb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-11-15T17:29:23.000Z","updated_at":"2020-02-01T14:17:18.000Z","dependencies_parsed_at":"2022-08-20T23:40:38.922Z","dependency_job_id":null,"html_url":"https://github.com/pragmarb/pragma-decorator","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmarb%2Fpragma-decorator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmarb%2Fpragma-decorator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmarb%2Fpragma-decorator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmarb%2Fpragma-decorator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pragmarb","download_url":"https://codeload.github.com/pragmarb/pragma-decorator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248766687,"owners_count":21158302,"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":["api","decorator","pragma","ruby","ruby-on-rails","trailblazer"],"created_at":"2024-10-31T22:11:37.060Z","updated_at":"2025-04-13T19:23:04.645Z","avatar_url":"https://github.com/pragmarb.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pragma::Decorator\n\n[![Build Status](https://travis-ci.org/pragmarb/pragma-decorator.svg?branch=master)](https://travis-ci.org/pragmarb/pragma-decorator)\n[![Coverage Status](https://coveralls.io/repos/github/pragmarb/pragma-decorator/badge.svg?branch=master)](https://coveralls.io/github/pragmarb/pragma-decorator?branch=master)\n[![Maintainability](https://api.codeclimate.com/v1/badges/e51e8d7489eb72ab97ba/maintainability)](https://codeclimate.com/github/pragmarb/pragma-decorator/maintainability)\n\nDecorators are a way to easily convert your API resources to JSON with minimum hassle.\n\nThey are built on top of [ROAR](https://github.com/apotonick/roar). We provide some useful helpers\nfor rendering collections, expanding associations and much more.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'pragma-decorator'\n```\n\nAnd then execute:\n\n```console\n$ bundle\n```\n\nOr install it yourself as:\n\n```console\n$ gem install pragma-decorator\n```\n\n## Usage\n\nCreating a decorator is as simple as inheriting from `Pragma::Decorator::Base`:\n\n```ruby\nmodule API\n  module V1\n    module User\n      module Decorator\n        class Instance \u003c Pragma::Decorator::Base\n          property :id\n          property :email\n          property :full_name\n        end\n      end\n    end\n  end\nend\n```\n\nJust instantiate the decorator by passing it an object to decorate, then call `#to_hash` or\n`#to_json`:\n\n```ruby\ndecorator = API::V1::User::Decorator::Instance.new(user)\ndecorator.to_json\n```\n\nThis will produce the following JSON:\n\n```json\n{\n  \"id\": 1,\n  \"email\": \"jdoe@example.com\",\n  \"full_name\": \"John Doe\"\n}\n```\n\nSince Pragma::Decorator is built on top of [ROAR](https://github.com/apotonick/roar) (which, in\nturn, is built on top of [Representable](https://github.com/apotonick/representable)), you should\nconsult their documentation for the basic usage of decorators; the rest of this section only covers\nthe features provided specifically by Pragma::Decorator.\n\n### Object Types\n\nIt is recommended that decorators expose the type of the decorated object. You can achieve this\nwith the `Type` mixin:\n\n```ruby\nmodule API\n  module V1\n    module User\n      module Decorator\n        class Instance \u003c Pragma::Decorator::Base\n          include Pragma::Decorator::Type\n        end\n      end\n    end\n  end\nend\n```\n\nThis would result in the following representation:\n\n```json\n{\n  \"type\": \"user\",\n  \"...\": \"...\"\n}\n```\n\nYou can also set a custom type name (just make sure to use it consistently!):\n\n```ruby\nmodule API\n  module V1\n    module User\n      module Decorator\n        class Instance \u003c Pragma::Decorator::Base\n          def type\n            :custom_type\n          end\n        end\n      end\n    end\n  end\nend\n```\n\n`Array` and `ActiveRecord::Relation` are already overridden as `list` to avoid exposing internal\ndetails. If you want to specify your own global overrides, you can do it by adding entries to the\n`Pragma::Decorator::Type.overrides` hash:\n\n```ruby\nPragma::Decorator::Type.overrides['Article'] = 'post'\n```\n\n### Timestamps\n\n[UNIX time](https://en.wikipedia.org/wiki/Unix_time) is your safest bet when rendering/parsing\ntimestamps in your API, as it doesn't require a timezone indicator (the timezone is always UTC).\n\nYou can use the `Timestamp` mixin for converting `Time` instances to UNIX times:\n\n```ruby\nmodule API\n  module V1\n    module User\n      module Decorator\n        class Instance \u003c Pragma::Decorator::Base\n          include Pragma::Decorator::Timestamp\n\n          timestamp :created_at\n        end\n      end\n    end\n  end\nend\n```\n\nThis will render a user like this:\n\n```json\n{\n  \"type\": \"user\",\n  \"created_at\": 1480287994\n}\n```\n\nThe `#timestamp` method supports all the options supported by `#property`.\n\n### Associations\n\n`Pragma::Decorator::Association` allows you to define associations in your decorator (currently,\nonly `belongs_to`/`has_one` associations are supported):\n\n```ruby\nmodule API\n  module V1\n    module Invoice\n      module Decorator\n        class Instance \u003c Pragma::Decorator::Base\n          include Pragma::Decorator::Association\n\n          belongs_to :customer, decorator: API::V1::Customer::Decorator::Instance\n        end\n      end\n    end\n  end\nend\n```\n\nRendering an invoice will now create the following representation:\n\n```json\n{\n  \"customer\": 19\n}\n```\n\nYou can pass `expand[]=customer` as a request parameter and have the `customer` property expanded\ninto a full object!\n\n```json\n{\n  \"customer\": {\n    \"id\": 19,\n    \"...\": \"...\"\n  }\n}\n```\n\nThis also works for nested associations. For instance, if the customer decorator had a `company`\nassociation, you could pass `expand[]=customer\u0026expand[]=customer.company` to get the company\nexpanded too.\n\nNote that you will have to pass the associations to expand as a user option when rendering:\n\n```ruby\ndecorator = API::V1::Invoice::Decorator::Instance.new(invoice)\ndecorator.to_json(user_options: {\n  expand: ['customer', 'customer.company', 'customer.company.contact']\n})\n```\n\nNeedless to say, this is done automatically for you when you use all components together through\nthe [pragma](https://github.com/pragmarb/pragma) gem! :)\n\nAssociations support all the options supported by `#property`. Additionally, `decorator` can be a\ncallable object, which is useful for polymorphic associations:\n\n```ruby\nmodule API\n  module V1\n    module Discount\n      module Decorator\n        class Instance \u003c Pragma::Decorator::Base\n          include Pragma::Decorator::Association\n\n          belongs_to :discountable, decorator: -\u003e (discountable) {\n            \"API::V1::#{discountable.class}::Decorator::Instance\".constantize\n          }\n        end\n      end\n    end\n  end\nend\n```\n\n### Collection\n\n`Pragma::Decorator::Collection` wraps collections in a `data` property so that you can include\nmetadata about them:\n\n```ruby\nmodule API\n  module V1\n    module Invoice\n      module Decorator\n        class Collection \u003c Pragma::Decorator::Base\n          include Pragma::Decorator::Collection\n          decorate_with Instance # this is optional, the default is 'Instance'\n\n          property :total_cents, exec_context: :decorator\n\n          def total_cents\n            represented.sum(:total_cents)\n          end\n        end\n      end\n    end\n  end\nend\n```\n\nYou can now do this:\n\n```ruby\nAPI::V1::Invoice::Decorator::Collection.new(Invoice.all).to_json\n```\n\nWhich will produce the following JSON:\n\n```json\n{\n  \"data\": [{\n    \"id\": 1,\n    \"total_cents\": 1500,\n  }, {\n    \"id\": 2,\n    \"total_cents\": 3000,\n  }],\n  \"total_cents\": 4500\n}\n```\n\nThis is very useful, for instance, when you have a paginated collection, but want to include data\nabout the entire collection (not just the current page) in the response.\n\n### Pagination\n\nSpeaking of pagination, you can use `Pragma::Decorator::Pagination` in combination with\n`Collection` to include pagination data in your response:\n\n```ruby\nmodule API\n  module V1\n    module Invoice\n      module Decorator\n        class Collection \u003c Pragma::Decorator::Base\n          include Pragma::Decorator::Collection\n          include Pragma::Decorator::Pagination\n\n          decorate_with Instance\n        end\n      end\n    end\n  end\nend\n```\n\nNow, you can run this code:\n\n```ruby\nAPI::V1::Invoice::Decorator::Collection.new(Invoice.all).to_json\n```\n\nWhich will produce the following JSON:\n\n```json\n{\n  \"data\": [{\n    \"id\": 1,\n    \"...\": \"...\",\n  }, {\n    \"id\": 2,\n    \"...\": \"...\",\n  }],\n  \"total_entries\": 2,\n  \"per_page\": 30,\n  \"total_pages\": 1,\n  \"previous_page\": null,\n  \"current_page\": 1,\n  \"next_page\": null\n}\n```\n\nIt works with both [will_paginate](https://github.com/mislav/will_paginate) and\n[Kaminari](https://github.com/kaminari/kaminari)!\n\n### Restricting property visibility\n\nIf you want to show or hide certain properties programmatically, you can do it with the `if` option:\n\n```ruby\nmodule API\n  module V1\n    module User\n      module Decorator\n        class Instance \u003c Pragma::Decorator::Base\n          property :id\n          property :first_name\n          property :last_name\n          property :email, if: -\u003e (user_options:, decorated:, **) {\n            # Only show the email to admins or to the same user.\n            user_options[:current_user].admin? || user_options[:current_user] == decorated\n          }\n        end\n      end\n    end\n  end\nend\n```\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/pragmarb/pragma-decorator.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpragmarb%2Fpragma-decorator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpragmarb%2Fpragma-decorator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpragmarb%2Fpragma-decorator/lists"}