{"id":13879321,"url":"https://github.com/jgaskins/primalize","last_synced_at":"2025-08-21T20:33:22.786Z","repository":{"id":56888759,"uuid":"102812884","full_name":"jgaskins/primalize","owner":"jgaskins","description":"Convert objects into primitives for serialization","archived":false,"fork":false,"pushed_at":"2023-10-19T19:41:41.000Z","size":58,"stargazers_count":42,"open_issues_count":4,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-17T11:48:33.000Z","etag":null,"topics":["ruby","serialization","serializer"],"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/jgaskins.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-09-08T03:24:40.000Z","updated_at":"2023-07-25T14:11:20.000Z","dependencies_parsed_at":"2024-10-24T10:56:55.417Z","dependency_job_id":"a34c4843-90db-4a94-8d99-4f50f4b3eb7f","html_url":"https://github.com/jgaskins/primalize","commit_stats":{"total_commits":55,"total_committers":4,"mean_commits":13.75,"dds":0.09090909090909094,"last_synced_commit":"c36e59140dbd2a6c3e75a2b0acf2491cfd2b8269"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgaskins%2Fprimalize","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgaskins%2Fprimalize/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgaskins%2Fprimalize/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgaskins%2Fprimalize/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jgaskins","download_url":"https://codeload.github.com/jgaskins/primalize/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230532451,"owners_count":18240792,"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":["ruby","serialization","serializer"],"created_at":"2024-08-06T08:02:17.177Z","updated_at":"2024-12-20T04:08:14.269Z","avatar_url":"https://github.com/jgaskins.png","language":"Ruby","readme":"# Primalize\n\nPrimalize lets you de-evolve your objects into primitive values for serialization. The primary use case is to serialize them into JSON, but once it's in its primitive state, it can be converted into other formats such as XML or CSV.\n\nPrimalizers support type checking by letting you specify the types of the resulting properties:\n\n```ruby\nclass OrderSerializer \u003c Primalize::Single\n  attributes(\n    id: integer,\n    customer_id: integer,\n    product_ids: array(integer),\n    status: enum(\n      'requested',\n      'payment_processed',\n      'awaiting_shipment',\n      'shipped',\n      'delivered',\n    ),\n    signature_required: boolean,\n    shipping_address: object(\n      address1: string,\n      address2: optional(string),\n      city: string,\n      state: string,\n      zip: string,\n    ),\n    created_at: timestamp,\n  )\nend\n\nOrderSerializer.new(order).call\n# { id: ... }\n```\n\nYou can also primalize a nested structure of response objects with `Primalize::Many`, replacing the type annotations with the classes of their respective serializers:\n\n```ruby\nclass PostResponseSerializer \u003c Primalize::Many\n  attributes(\n    post: PostSerializer,\n    author: UserSerializer,\n    comments: enumerable(CommentSerializer), # Not just one comment, but *many*\n  )\nend\n\n# Instantiate it by passing in the pertinent values\nserializer = PostResponseSerializer.new(\n  post: @post,\n  author: @post.author,\n  comments: @post.comments,\n)\n\nserializer.call\n# {\n#   post: { ... },\n#   author: { ... },\n#   comments: [\n#     { ... },\n#   ],\n# }\n```\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'primalize'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install primalize\n\n## Usage\n\nIf you need to primalize a single object, you subclass `Primalize::Single` and specify the attributes and types of the result as in the example above.\n\n### Supported types\n\nThe complete list of supported types are:\n\n- `integer`: whole numbers\n- `float`: floating-point numbers\n- `number`: any numeric value\n- `string`: text\n- `boolean`: explicitly `true` or `false` (not \"truthy\" or \"falsy\" values)\n- `array(*types)`: an array containing values of the specified types\n  - Example: `array(string, integer)`\n- `optional(*types)`: any of the specified types or `nil`\n  - Example: `optional(string)`, both `\"foo\"` and `nil` are acceptable values\n- `enum(*values)`: must be one of the specified values\n  - Example: `enum('requested', 'shipped', 'delivered')`\n- `timestamp`: a `Date`, `Time`, or `DateTime` value\n- `any(*types)`: any value of the given types\n  - Example: `any(string, integer)` will only match on strings and integers\n  - If no types are specified, any value will match\n- `primalize(YourPrimalizerClass)`: primalizes the specified attribute with the given `Primalize::Single` subclass\n  - Example: `primalize(OrderSerializer)`\n- `object(**types)`: a hash of the specified structure\n  - Example: `object(id: integer, name: string)`\n  - Only the required keys need to be specified. The rest of the hash will pass.\n  - If no keys are specified, all of them are optional and it will match any hash.\n\n### Attribute coercion\n\nReducing the object's attributes to a hash isn't all you do in most apps. You may also need to do some coercion. For example, if you have an object whose `city` isn't stored as a string but you need to translate it to one:\n\n```ruby\nclass ShipmentSerializer \u003c Primalize::Single\n  attributes(\n    city: string { |city| city.name },\n    # ...\n  )\nend\n```\n\n### Virtual attributes\n\nYou can also generate attributes that don't exist on the object being primalized by defining methods on the primalizer:\n\n```ruby\nclass ShipmentSerializer \u003c Primalize::Single\n  attributes(\n    payment_method: string,\n  )\n\n  def payment_method\n    if object.paid_with_card?\n      'credit_card'\n    elsif object.purchase_order?\n      'purchase_order'\n    elsif object.bill_later?\n      'invoice'\n    else\n      'unknown'\n    end\n  end\nend\n```\n\n### Type Checking\n\nBy default, subclasses of `Primalize::Single` will raise an `ArgumentError` if there is a mismatch between the types declared in its `attributes` call and what is passed in to be primalized. In production, you might not want that to happen, so you can change that in your production config:\n\n```ruby\nPrimalize::Single.type_mismatch_handler = proc do |primalizer, attr, type, value|\n  msg = \"Type mismatch: #{primalizer.name}##{attr} is expected to be #{type.inspect}, but is #{value.inspect}\\n\"\n  msg \u003c\u003c caller.grep(Regexp.new(Rails.root)).join(\"\\n\") # Include application stack trace\n\n  Slack.notify '#bugs', msg\n\n  nil # Don't emit any value for an incorrect attribute\nend\n```\n\nNote that this example returns `nil` when there is a type mismatch after reporting the bug. The return value of the block becomes the value serialized into your payload, so you can allow the incorrect value to pass through, clear it, or anything else that makes sense for your use case.\n\nAlso note that `type_mismatch_handler` is set on `Primalize::Single` here. You can also set it for individual classes and it will be inherited by all subclasses. For example, if you have different use cases for serialization within the same application, such as API responses sent over HTTP and events passed through a message broker, you can perform different type-mismatch handling for each.\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` 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/jgaskins/primalize. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the Primalize project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/jgaskins/primalize/blob/master/CODE_OF_CONDUCT.md).\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjgaskins%2Fprimalize","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjgaskins%2Fprimalize","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjgaskins%2Fprimalize/lists"}