{"id":13546056,"url":"https://github.com/procore-oss/blueprinter","last_synced_at":"2025-05-13T16:07:19.855Z","repository":{"id":37550156,"uuid":"99283495","full_name":"procore-oss/blueprinter","owner":"procore-oss","description":"Simple, Fast, and Declarative Serialization Library for Ruby","archived":false,"fork":false,"pushed_at":"2025-04-07T13:09:05.000Z","size":2552,"stargazers_count":1181,"open_issues_count":16,"forks_count":112,"subscribers_count":132,"default_branch":"main","last_synced_at":"2025-04-12T12:46:29.595Z","etag":null,"topics":["json","presenter","rails","resource-serializer","ruby","serializer"],"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/procore-oss.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-08-03T23:34:27.000Z","updated_at":"2025-04-03T16:12:40.000Z","dependencies_parsed_at":"2023-08-21T00:10:04.992Z","dependency_job_id":"902cbc44-43e1-4275-8fa9-dbbbe38bf7c8","html_url":"https://github.com/procore-oss/blueprinter","commit_stats":{"total_commits":411,"total_committers":48,"mean_commits":8.5625,"dds":0.7956204379562044,"last_synced_commit":"bf9fa316bbe4e2f45ae51c85dc4245b9b3eb7300"},"previous_names":["procore-oss/blueprinter","procore/blueprinter"],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/procore-oss%2Fblueprinter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/procore-oss%2Fblueprinter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/procore-oss%2Fblueprinter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/procore-oss%2Fblueprinter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/procore-oss","download_url":"https://codeload.github.com/procore-oss/blueprinter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250513576,"owners_count":21443201,"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","presenter","rails","resource-serializer","ruby","serializer"],"created_at":"2024-08-01T12:00:30.772Z","updated_at":"2025-04-23T20:47:17.401Z","avatar_url":"https://github.com/procore-oss.png","language":"Ruby","readme":"[![Test](https://github.com/procore-oss/blueprinter/actions/workflows/test.yaml/badge.svg?branch=master)](https://github.com/procore-oss/blueprinter/actions/workflows/test.yaml)\n[![Gem Version](https://badge.fury.io/rb/blueprinter.svg)](https://badge.fury.io/rb/blueprinter)\n[![Discord](https://img.shields.io/badge/Chat-EDEDED?logo=discord)](https://discord.gg/PbntEMmWws)\n\n\u003cimg src=\"blueprinter_logo.svg\" width=\"25%\"\u003e\n\n# Recent Organization Move\n\nPlease change your local remote to pull from this repository:\n\n```bash\ngit remote set-url [previous-remote-name] git@github.com:procore-oss/blueprinter.git\n```\n\nto see the previous upstream remote name, run:\n\n```bash\ngit remote -v\n```\n\n# Blueprinter\n\nBlueprinter is a JSON Object Presenter for Ruby that takes business objects and breaks them down into simple hashes and serializes them to JSON. It can be used in Rails in place of other serializers (like JBuilder or ActiveModelSerializers). It is designed to be simple, direct, and performant.\n\nIt heavily relies on the idea of `views` which, similar to Rails views, are ways of predefining output for data in different contexts.\n\n## Documentation\n\nDocs can be found [here](http://www.rubydoc.info/gems/blueprinter).\n\n## Usage\n\n\u003cdetails open\u003e\n\u003csummary\u003eBasic\u003c/summary\u003e\n\nIf you have an object you would like serialized, simply create a blueprint. Say, for example, you have a User record with the following attributes `[:uuid, :email, :first_name, :last_name, :password, :address]`.\n\nYou may define a simple blueprint like so:\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n\n  fields :first_name, :last_name, :email\nend\n```\n\nand then, in your code:\n\n```ruby\nputs UserBlueprint.render(user) # Output is a JSON string\n```\n\nAnd the output would look like:\n\n```json\n{\n  \"uuid\": \"733f0758-8f21-4719-875f-262c3ec743af\",\n  \"email\": \"john.doe@some.fake.email.domain\",\n  \"first_name\": \"John\",\n  \"last_name\": \"Doe\"\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eCollections\u003c/summary\u003e\n\n\nYou can also pass a collection object or an array to the render method.\n\n```ruby\nputs UserBlueprint.render(User.all)\n```\n\nThis will result in JSON that looks something like this:\n\n```json\n[\n  {\n    \"uuid\": \"733f0758-8f21-4719-875f-262c3ec743af\",\n    \"email\": \"john.doe@some.fake.email.domain\",\n    \"first_name\": \"John\",\n    \"last_name\": \"Doe\"\n  },\n  {\n    \"uuid\": \"733f0758-8f21-4719-875f-743af262c3ec\",\n    \"email\": \"john.doe.2@some.fake.email.domain\",\n    \"first_name\": \"John\",\n    \"last_name\": \"Doe 2\"\n  }\n]\n```\n\n\nYou can also configure other classes to be treated like collections. For example, if you are using Mongoid, you can configure it to treat `Mongoid::Criteria` objects as collections:\n\n```ruby\nBlueprinter.configure do |config|\n  config.custom_array_like_classes = [Mongoid::Criteria]\nend\n```\n\nOr if you wanted it to treat the `Set` class as a collection:\n\n```ruby\nBlueprinter.configure do |config|\n  config.custom_array_like_classes = [Set]\nend\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eRenaming\u003c/summary\u003e\n\n\nYou can rename the resulting JSON keys in both fields and associations by using the `name` option.\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n\n  field :email, name: :login\n\n  association :user_projects, name: :projects\nend\n```\n\nThis will result in JSON that looks something like this:\n\n```json\n{\n  \"uuid\": \"92a5c732-2874-41e4-98fc-4123cd6cfa86\",\n  \"login\": \"my@email.com\",\n  \"projects\": []\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eViews\u003c/summary\u003e\n\n\nYou may define different outputs by utilizing views:\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n  field :email, name: :login\n\n  view :normal do\n    fields :first_name, :last_name\n  end\n\n  view :extended do\n    include_view :normal\n    field :address\n    association :projects\n  end\nend\n```\n\nA view can include fields from another view by utilizing `include_view` and `include_views`.\n\nUsage:\n\n```ruby\nputs UserBlueprint.render(user, view: :extended)\n```\n\nOutput:\n\n```json\n{\n  \"uuid\": \"733f0758-8f21-4719-875f-262c3ec743af\",\n  \"address\": \"123 Fake St.\",\n  \"first_name\": \"John\",\n  \"last_name\": \"Doe\",\n  \"login\": \"john.doe@some.fake.email.domain\"\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eIdentifiers\u003c/summary\u003e\n\n\n`identifier`s are used to specify a field or method name used as an identifier. Usually, this is something like `:id`.\n\nExample:\n\n```rb\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\nend\n```\n\nBlueprinter `identifier`s have a few properties that set them apart from `field`s.\n\n1. Identifiers are **always** rendered and considered their own view (the `:identifier` view).\n2. When rendering, identifier fields are always sorted first, before other fields.\n\nIf either of the above two developer conveniences are not desired, you can simply create your identifier fields as regular `field`s.\n\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eRoot\u003c/summary\u003e\n\u003ca name=\"root\"\u003e\u003c/a\u003e\n\nYou can also optionally pass in a root key to wrap your resulting json in:\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n  field :email, name: :login\n\n  view :normal do\n    fields :first_name, :last_name\n  end\nend\n```\n\nUsage:\n\n```ruby\nputs UserBlueprint.render(user, view: :normal, root: :user)\n```\n\nOutput:\n\n```json\n{\n  \"user\": {\n    \"uuid\": \"733f0758-8f21-4719-875f-262c3ec743af\",\n    \"first_name\": \"John\",\n    \"last_name\": \"Doe\",\n    \"login\": \"john.doe@some.fake.email.domain\"\n  }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eMeta Attributes\u003c/summary\u003e\n\n\nYou can additionally add meta-data to the json as well:\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n  field :email, name: :login\n\n  view :normal do\n    fields :first_name, :last_name\n  end\nend\n```\n\nUsage:\n\n```ruby\njson = UserBlueprint.render(user, view: :normal, root: :user, meta: {links: [\n  'https://app.mydomain.com',\n  'https://alternate.mydomain.com'\n]})\nputs json\n```\n\nOutput:\n\n```json\n{\n  \"user\": {\n    \"uuid\": \"733f0758-8f21-4719-875f-262c3ec743af\",\n    \"first_name\": \"John\",\n    \"last_name\": \"Doe\",\n    \"login\": \"john.doe@some.fake.email.domain\"\n  },\n  \"meta\": {\n    \"links\": [\n      \"https://app.mydomain.com\",\n      \"https://alternate.mydomain.com\"\n    ]\n  }\n}\n```\n\n_NOTE:_ For meta attributes, a [root](#root) is mandatory.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExclude Fields\u003c/summary\u003e\n\n\nYou can specifically choose to exclude certain fields for specific views\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n  field :email, name: :login\n\n  view :normal do\n    fields :first_name, :last_name\n  end\n\n  view :extended do\n    include_view :normal\n    field :address\n    exclude :last_name\n  end\nend\n```\n\nUsage:\n\n```ruby\nputs UserBlueprint.render(user, view: :extended)\n```\n\nOutput:\n\n```json\n{\n  \"uuid\": \"733f0758-8f21-4719-875f-262c3ec743af\",\n  \"address\": \"123 Fake St.\",\n  \"first_name\": \"John\",\n  \"login\": \"john.doe@some.fake.email.domain\"\n}\n```\n\nUse `excludes` to exclude multiple fields at once inline.\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n  field :email, name: :login\n\n  view :normal do\n    fields :age, :first_name, :last_name,\n  end\n\n  view :extended do\n    include_view :normal\n    field :address\n    excludes :age, :last_name\n  end\nend\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eAssociations\u003c/summary\u003e\n\n\nYou may include associated objects. Say for example, a user has projects:\n\n```ruby\nclass ProjectBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n  field :name\nend\n\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n  field :email, name: :login\n\n  view :normal do\n    fields :first_name, :last_name\n    association :projects, blueprint: ProjectBlueprint\n  end\nend\n```\n\nUsage:\n\n```ruby\nputs UserBlueprint.render(user, view: :normal)\n```\n\nOutput:\n\n```json\n{\n  \"uuid\": \"733f0758-8f21-4719-875f-262c3ec743af\",\n  \"first_name\": \"John\",\n  \"last_name\": \"Doe\",\n  \"login\": \"john.doe@some.fake.email.domain\",\n  \"projects\": [\n    {\n      \"uuid\": \"dca94051-4195-42bc-a9aa-eb99f7723c82\",\n      \"name\": \"Beach Cleanup\"\n    },\n    {\n      \"uuid\": \"eb881bb5-9a51-4d27-8a29-b264c30e6160\",\n      \"name\": \"Storefront Revamp\"\n    }\n  ]\n}\n```\n\nIt is also possible to pass options from one Blueprint to another via an association.\nFor example:\n\n```ruby\nclass VehicleBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n  field :full_name do |vehicle, options|\n    \"#{vehicle.model} #{options[:trim]}\"\n  end\nend\n\nclass DriverBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n\n  view :normal do\n    fields :first_name, :last_name\n    association :vehicles, blueprint: VehicleBlueprint, options: { trim: 'LX' }\n  end\nend\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eDefault Association/Field Option\u003c/summary\u003e\n\n\nBy default, an association or field that evaluates to `nil` is serialized as `nil`. A default serialized value can be specified as an option on the association or field for cases when the association/field could potentially evaluate to `nil`. You can also specify a global `field_default` or `association_default` in the Blueprinter config which will be used for all fields/associations that evaluate to nil.\n\n### Global Config Setting\n\n```ruby\nBlueprinter.configure do |config|\n  config.field_default = \"N/A\"\n  config.association_default = {}\nend\n```\n\n### Field-level/Association-level Setting\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n\n  view :normal do\n    field :first_name, default: \"N/A\"\n    association :company, blueprint: CompanyBlueprint, default: {}\n  end\nend\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003edefault_if\u003c/summary\u003e\n\n\nSometimes, you may want certain \"empty\" values to pass through to the default value.\nBlueprinter provides the ability to treat the following empty types as the default value (or `nil` if no default provided).\n\n### Blueprinter::EMPTY_COLLECTION\n\nAn empty array or empty active record collection.\n\n### Blueprinter::EMPTY_HASH\n\nAn empty hash.\n\n### Blueprinter::EMPTY_STRING\n\nAn empty string or symbol.\n\n#### Field-level/Association-level Setting - EMPTY_STRING\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n\n  view :normal do\n    # If first_name is an empty string, it will become \"N/A\"\n    field :first_name, default_if: Blueprinter::EMPTY_STRING, default: \"N/A\"\n    # If the projects association collection is empty, it will become nil\n    association :projects, blueprint: ProjectBlueprint, default_if: Blueprinter::EMPTY_COLLECTION\n  end\nend\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eSupporting Dynamic Blueprints For Associations\u003c/summary\u003e\n\n\nWhen defining an association, we can dynamically evaluate the blueprint. This comes in handy when adding polymorphic associations, by allowing reuse of existing blueprints.\n\n```ruby\nclass Task \u003c ActiveRecord::Base\n  belongs_to :taskable, polymorphic: true\nend\n\nclass Project \u003c ActiveRecord::Base\n  has_many :tasks, as: :taskable\n\n  def blueprint\n    ProjectBlueprint\n  end\nend\n\nclass TaskBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n\n  view :normal do\n    field :title, default: \"N/A\"\n    association :taskable, blueprint: -\u003e(taskable) {taskable.blueprint}, default: {}\n  end\nend\n```\n\n_NOTE:_ `taskable.blueprint` should return a valid Blueprint class. Currently, `has_many` is not supported because of the very nature of polymorphic associations.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eDefining A Field Directly In The Blueprint\u003c/summary\u003e\n\n\nYou can define a field directly in the Blueprint by passing it a block. This is especially useful if the object does not already have such an attribute or method defined, and you want to define it specifically for use with the Blueprint. This is done by passing `field` a block. The block also yields the object and any options that were passed from `render`. For example:\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n  field :full_name do |user, options|\n    \"#{options[:title_prefix]} #{user.first_name} #{user.last_name}\"\n  end\nend\n```\n\nUsage:\n\n```ruby\nputs UserBlueprint.render(user, title_prefix: \"Mr\")\n```\n\nOutput:\n\n```json\n{\n  \"uuid\": \"733f0758-8f21-4719-875f-262c3ec743af\",\n  \"full_name\": \"Mr John Doe\"\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eDefining An Identifier Directly In The Blueprint\u003c/summary\u003e\n\n\nYou can also pass a block to an identifier:\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid do |user, options|\n    options[:current_user].anonymize(user.uuid)\n  end\nend\n```\n\nUsage:\n\n```ruby\nputs UserBlueprint.render(user, current_user: current_user)\n```\n\nOutput:\n\n```json\n{\n  \"uuid\": \"733f0758-8f21-4719-875f-262c3ec743af\",\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eDefining An Association Directly In The Blueprint\u003c/summary\u003e\n\n\nYou can also pass a block to an association:\n\n```ruby\nclass ProjectBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n  field :name\nend\n\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n\n  association :projects, blueprint: ProjectBlueprint do |user, options|\n    user.projects + options[:draft_projects]\n  end\nend\n```\n\nUsage:\n\n```ruby\nputs UserBlueprint.render(user, draft_projects: Project.where(draft: true))\n```\n\nOutput:\n\n```json\n{\n  \"uuid\": \"733f0758-8f21-4719-875f-262c3ec743af\",\n  \"projects\": [\n    {\"uuid\": \"b426a1e6-ac41-45ab-bfef-970b9a0b4289\", \"name\": \"query-console\"},\n    {\"uuid\": \"5bd84d6c-4fd2-4e36-ae31-c137e39be542\", \"name\": \"blueprinter\"},\n    {\"uuid\": \"785f5cd4-7d8d-4779-a6dd-ec5eab440eff\", \"name\": \"uncontrollable\"}\n  ]\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003ePassing Additional Properties To #render\u003c/summary\u003e\n\n\n`render` takes an options hash which you can pass additional properties, allowing you to utilize those additional properties in the `field` block. For example:\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n  field(:company_name) do |_user, options|\n    options[:company].name\n  end\nend\n```\n\nUsage:\n\n```ruby\nputs UserBlueprint.render(user, company: company)\n```\n\nOutput:\n\n```json\n{\n  \"uuid\": \"733f0758-8f21-4719-875f-262c3ec743af\",\n  \"company_name\": \"My Company LLC\"\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eConditional Fields\u003c/summary\u003e\n\n\nBoth the `field` and the global Blueprinter Configuration supports `:if` and `:unless` options that can be used to serialize fields conditionally.\n\n### Global Config Setting - if and unless\n\n```ruby\nBlueprinter.configure do |config|\n  config.if = -\u003e(field_name, obj, _options) { !obj[field_name].nil? }\n  config.unless = -\u003e(field_name, obj, _options) { obj[field_name].nil? }\nend\n```\n\n#### Field-level Setting\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n  field :last_name, if: -\u003e(_field_name, user, options) { user.first_name != options[:first_name] }\n  field :age, unless: -\u003e(_field_name, user, _options) { user.age \u003c 18 }\nend\n```\n\n_NOTE:_ The field-level setting overrides the global config setting (for the field) if both are set.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExclude Fields with nil Values\u003c/summary\u003e\n\n\nBy default, fields with `nil` values are included when rendering. You can override this behavior by setting `:exclude_if_nil: true` in the field definition. \n\nUsage:\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :uuid\n\n  field :name\n  field :birthday, exclude_if_nil: true\nend\n\nuser = User.new(name: 'John Doe')\nputs UserBlueprint.render(user)\n```\n\nOutput:\n\n```json\n{\n  \"name\": \"John Doe\"\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eCustom Formatting for Dates and Times\u003c/summary\u003e\n\n\nTo define a custom format for a Date or DateTime field, include the option `datetime_format`.\nThis global or field-level option can be either a string representing the associated `strftime` format,\nor a Proc which receives the original Date/DateTime object and returns the formatted value.\nWhen using a Proc, it is the Proc's responsibility to handle any errors in formatting.\n\n#### Global Config Setting - datetime\n\nIf a global datetime_format is set (either as a string format or a Proc), this option will be\ninvoked and used to format all fields that respond to `strftime`.\n\n```ruby\nBlueprinter.configure do |config|\n  config.datetime_format = -\u003e(datetime) { datetime.nil? ? datetime : datetime.strftime(\"%s\").to_i }\nend\n```\n\n#### Field-level Setting - datetime_format\n\nUsage (String Option):\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :name\n  field :birthday, datetime_format: \"%m/%d/%Y\"\nend\n```\n\nOutput:\n\n```json\n{\n  \"name\": \"John Doe\",\n  \"birthday\": \"03/04/1994\"\n}\n```\n\nUsage (Proc Option):\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :name\n  field :birthday, datetime_format: -\u003e(datetime) { datetime.nil? ? datetime : datetime.strftime(\"%s\").to_i }\nend\n```\n\nOutput:\n\n```json\n{\n  \"name\": \"John Doe\",\n  \"birthday\": 762739200\n}\n```\n\n_NOTE:_ The field-level setting overrides the global config setting (for the field) if both are set.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eTransform Classes\u003c/summary\u003e\n\n\nBlueprinter provides the ability to specify `transform`s on views, which enable further\nprocessing and transforming of resulting view field hashes prior to serialization.\n\nUse `transform` to specify one transformer to be included for serialization.\nA transformer is a class, extending `Blueprinter::Transformer` and implementing the `transform` method.\nThe modified `hash` object will be the resulting hash passed to serialization.\n\n#### Example\n\nCreate a Transform class extending from `Blueprinter::Transformer`\n\n```ruby\nclass DynamicFieldTransformer \u003c Blueprinter::Transformer\n  def transform(hash, object, _options)\n    hash.merge!(object.dynamic_fields)\n  end\nend\n```\n\n```ruby\nclass User\n  def dynamic_fields\n    case role\n    when :admin\n      {\n        employer: employer,\n        time_in_role: determine_time_in role\n      }\n    when :maintainer\n      {\n        label: label,\n        settings: generate_settings_hash\n      }\n    when :read_only\n      {\n        last_login_at: last_login_at\n      }\n    end\n  end\nend\n```\n\nThen specify the transform to use for the view.\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  fields :first_name, :last_name\n  transform DynamicTransformer\nend\n```\n\n#### Transform across views\n\nTransformers can be included across views:\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  transform DefaultTransformer\n\n  view :normal do\n    transform ViewTransformer\n  end\n\n  view :extended do\n    include_view :normal\n  end\nend\n```\n\nBoth the `normal` and `extended` views have `DefaultTransformer` and `ViewTransformer` applied.\n\nTransformers are executed in a top-down order, so `DefaultTransformer` will be executed first, followed by `ViewTransformer`.\n\n#### Global Transforms\n\nYou can also specify global default transformers. Create one or more transformer classes extending from `Blueprinter::Transformer` and set the `default_transformers` configuration\n\n```ruby\nclass LowerCamelTransformer \u003c Blueprinter::Transformer\n  def transform(hash, _object, _options)\n    hash.transform_keys! { |key| key.to_s.camelize(:lower).to_sym }\n  end\nend\n```\n\n```ruby\nBlueprinter.configure do |config|\n  config.default_transformers = [LowerCamelTransformer]\nend\n```\n\n**Note: Any transforms specified on a per-blueprint or per-view level will override the `default_transformers` in the configuration.**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eConfigurable Extractors\u003c/summary\u003e\n\n\nBlueprinter gets a given objects' values from the fields definitions using extractor classes. You can substitute your own extractor class globally or per-field.\n\n#### Examples\n\nFor a specific kind of field, create an extractor class extending from `Blueprinter::Extractor`\n\n```ruby\nclass MyFieldExtractor \u003c Blueprinter::Extractor\n  def extract(_field_name, _object, _local_options, _options={})\n    # process your obscure_object\n    _object.clarified\n  end\nend\n```\n\n```ruby\nclass MysteryBlueprint \u003c Blueprinter::Base\n  field :obscure_object, extractor: MyFieldExtractor\nend\n```\n\nFor a global default, create an extractor class extending from `Blueprinter::AutoExtractor` and set the `extractor_default` configuration\n\n```ruby\nclass MyAutoExtractor \u003c Blueprinter::AutoExtractor\n  def initialize\n    super\n    @my_field_extractor = MyFieldExtractor.new\n  end\n  def extractor(object, options)\n    # dispatch to any class AutoExtractor can, plus more\n    if detect_obscurity(object)\n      @my_field_extractor\n    else\n      super\n    end\n  end\nend\n```\n\n```ruby\nBlueprinter.configure do |config|\n  config.extractor_default = MyAutoExtractor\nend\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eSorting Fields\u003c/summary\u003e\n\n\nBy default the response sorts the keys by name. If you want the fields to be sorted in the order of definition, use the below configuration option.\n\nUsage:\n\n```ruby\nBlueprinter.configure do |config|\n  config.sort_fields_by = :definition\nend\n```\n\n```ruby\nclass UserBlueprint \u003c Blueprinter::Base\n  identifier :name\n  field :email\n  field :birthday, datetime_format: \"%m/%d/%Y\"\nend\n```\n\nOutput:\n\n```json\n{\n  \"name\": \"John Doe\",\n  \"email\": \"john.doe@some.fake.email.domain\",\n  \"birthday\": \"03/04/1994\"\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eReflection\u003c/summary\u003e\n\nBlueprint classes may be reflected on to inspect their views, fields, and associations. Extensions often make use of this ability.\n\n```ruby\nclass WidgetBlueprint \u003c Blueprinter::Base\n  fields :name, :description\n  association :category, blueprint: CategoryBlueprint\n\n  view :extended do\n    field :price\n    association :parts, blueprint: WidgetPartBlueprint\n  end\nend\n\n# A Hash of views keyed by name\nviews = WidgetBlueprint.reflections\nviews.keys\n=\u003e [:default, :extended]\n\n# Hashes of fields and associations, keyed by name\nfields = views[:default].fields\nassoc = views[:default].associations\n\n# Get info about a field\nfields[:description].name\nfields[:description].display_name\nfields[:description].options\n\n# Get info about an association\nassoc[:category].name\nassoc[:category].display_name\nassoc[:category].blueprint\nassoc[:category].view\nassoc[:category].options\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExtensions\u003c/summary\u003e\n\nBlueprinter offers an extension system to hook into and modify certain behavior.\n\n```ruby\nBlueprinter.configure do |config|\n  config.extensions \u003c\u003c MyExtension.new\n  config.extensions \u003c\u003c OtherExtension.new\nend\n```\n\nExtension hooks:\n\n* [pre_render](https://github.com/procore-oss/blueprinter/blob/abca9ca8ed23edd65a0f4b5ae43e25b8e27a2afc/lib/blueprinter/extension.rb#L18): Intercept the object before rendering begins\n\nSome known extensions are:\n\n* [blueprinter-activerecord](https://github.com/procore-oss/blueprinter-activerecord)\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eDeprecations\u003c/summary\u003e\n\n\nWhen functionality in Blueprinter is invoked, that has been deprecated, the default behavior is to\nwrite a deprecation notice to stderror.\n\nHowever, deprecations can be configured to report at three different levels:\n\n|        Key        |                              Result                             |\n|:-----------------:|:---------------------------------------------------------------:|\n| `:stderr` (Default) | Deprecations will be written to stderror                        |\n| `:raise`            | Deprecations will be raised as `Blueprinter::BlueprinterError`s |\n| `:silence`          | Deprecations will be silenced and will not be raised or logged  |\n\n### Example - deprecations\n\n```ruby\nBlueprinter.configure do |config|\n  config.deprecations = :raise\nend\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003erender_as_hash\u003c/summary\u003e\n\n\nSame as `render`, returns a Ruby Hash.\n\nUsage:\n\n```ruby\nputs UserBlueprint.render_as_hash(user, company: company)\n```\n\nOutput:\n\n```ruby\n{\n  uuid: \"733f0758-8f21-4719-875f-262c3ec743af\",\n  company_name: \"My Company LLC\"\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003erender_as_json\u003c/summary\u003e\n\n\nSame as `render`, returns a Ruby Hash JSONified. This will call JSONify all keys and values.\n\nUsage:\n\n```ruby\nputs UserBlueprint.render_as_json(user, company: company)\n```\n\nOutput:\n\n```ruby\n{\n  \"uuid\" =\u003e \"733f0758-8f21-4719-875f-262c3ec743af\",\n  \"company_name\" =\u003e \"My Company LLC\"\n}\n```\n\n\u003c/details\u003e\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'blueprinter'\n```\n\nAnd then execute:\n\n```bash\nbundle\n```\n\nOr install it yourself as:\n\n```bash\ngem install blueprinter\n```\n\nYou should also have `require 'json'` already in your project if you are not using Rails or if you are not using Oj.\n\n## OJ\n\nBy default, Blueprinter will be calling `JSON.generate(object)` internally and it expects that you have `require 'json'` already in your project's code. You may use `Oj` to generate in place of `JSON` like so:\n\n```ruby\nrequire 'oj' # you can skip this if OJ has already been required.\n\nBlueprinter.configure do |config|\n  config.generator = Oj # default is JSON\nend\n```\n\nEnsure that you have the `Oj` gem installed in your Gemfile if you haven't already:\n\n```ruby\n# Gemfile\ngem 'oj'\n```\n\n## Yajl-ruby\n\n[yajl-ruby](https://github.com/brianmario/yajl-ruby) is a fast and powerful JSON generator/parser. To use `yajl-ruby` in place of `JSON / OJ`, use:\n\n```ruby\nrequire 'yajl' # you can skip this if yajl has already been required.\n\nBlueprinter.configure do |config|\n  config.generator = Yajl::Encoder # default is JSON\n  config.method = :encode # default is generate\nend\n```\n\n_NOTE:_ You should be doing this only if you aren't using `yajl-ruby` through the JSON API by requiring `yajl/json_gem`. More details [here](https://github.com/brianmario/yajl-ruby#json-gem-compatibility-api). In this case, `JSON.generate` is patched to use `Yajl::Encoder.encode` internally.\n\n## Contributing\n\nPlease read our [Contributing](CONTRIBUTING.md) file\n\n### Tests\n\nYou can run tests with `bundle exec rake`.\n\n### Maintain The Docs\n\nWe use Yard for documentation. Here are the following documentation rules:\n\n- Document all public methods we expect to be utilized by the end developers.\n- Methods that are not set to private due to ruby visibility rule limitations should be marked with `@api private`.\n\n## How to Document\n\nWe use [Yard](https://yardoc.org/) for documentation. Here are the following\ndocumentation rules:\n\n- Document all public methods we expect to be utilized by the end developers.\n- Methods that are not set to private due to ruby visibility rule limitations should be marked with `@api private`.\n\n### Releasing a New Version\n\nTo release a new version, change the version number in `version.rb`, and update the `CHANGELOG.md`. Finally, maintainers need to run `bundle exec rake release`, which will automatically create a git tag for the version, push git commits and tags to Github, and push the `.gem` file to rubygems.org.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","funding_links":[],"categories":["Ruby","Serializers"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprocore-oss%2Fblueprinter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprocore-oss%2Fblueprinter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprocore-oss%2Fblueprinter/lists"}