{"id":13879819,"url":"https://github.com/nesaulov/surrealist","last_synced_at":"2025-05-15T00:07:12.499Z","repository":{"id":43728216,"uuid":"102661666","full_name":"nesaulov/surrealist","owner":"nesaulov","description":"to_json but I wrote it myself","archived":false,"fork":false,"pushed_at":"2025-02-26T02:50:23.000Z","size":378,"stargazers_count":264,"open_issues_count":7,"forks_count":20,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-13T20:40:32.758Z","etag":null,"topics":["declarative","dsl","json","ruby","serialization","serializer"],"latest_commit_sha":null,"homepage":"https://nesaulov.github.io/surrealist","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/nesaulov.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"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-06T21:49:54.000Z","updated_at":"2025-02-21T17:57:35.000Z","dependencies_parsed_at":"2024-04-21T18:56:55.627Z","dependency_job_id":"00ca416c-d704-49dc-832f-238e9e8d0a51","html_url":"https://github.com/nesaulov/surrealist","commit_stats":{"total_commits":172,"total_committers":19,"mean_commits":9.052631578947368,"dds":0.2441860465116279,"last_synced_commit":"26c6a1949354e34b2e07eb210acee179c516030f"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nesaulov%2Fsurrealist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nesaulov%2Fsurrealist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nesaulov%2Fsurrealist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nesaulov%2Fsurrealist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nesaulov","download_url":"https://codeload.github.com/nesaulov/surrealist/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254110448,"owners_count":22016392,"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":["declarative","dsl","json","ruby","serialization","serializer"],"created_at":"2024-08-06T08:02:34.552Z","updated_at":"2025-05-15T00:07:12.470Z","avatar_url":"https://github.com/nesaulov.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Surrealist\n[![Build Status](https://travis-ci.org/nesaulov/surrealist.svg?branch=master)](https://travis-ci.org/nesaulov/surrealist)\n[![Coverage Status](https://coveralls.io/repos/github/nesaulov/surrealist/badge.svg?branch=master)](https://coveralls.io/github/nesaulov/surrealist?branch=master)\n[![Inline docs](http://inch-ci.org/github/nesaulov/surrealist.svg?branch=master)](http://inch-ci.org/github/nesaulov/surrealist)\n[![Gem Version](https://badge.fury.io/rb/surrealist.svg)](https://rubygems.org/gems/surrealist)\n[![Open Source Helpers](https://www.codetriage.com/nesaulov/surrealist/badges/users.svg)](https://www.codetriage.com/nesaulov/surrealist)\n[![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)\n\n![Surrealist](surrealist-icon.png)\n\nSurrealist is a schema-driven object serialization ruby library. It also provides a trivial type checking in the runtime before serialization.\n[Yard documentation](http://www.rubydoc.info/github/nesaulov/surrealist/master)\n\n\n## Motivation\nA typical use case for this gem could be, for example, serializing a (decorated) object outside\nof the view context. The schema is described through a hash, so you can build the structure\nof serialized object independently of its methods and attributes, while also having possibility\nto serialize nested objects and structures. [Introductory blogpost.](https://medium.com/@billikota/introducing-surrealist-a-gem-to-serialize-ruby-objects-according-to-a-defined-schema-6ca7e550628d)\n\n* [Installation](#installation)\n* [Usage](#usage)\n  * [Simple example](#simple-example)\n  * [Nested structures](#nested-structures)\n  * [Nested objects](#nested-objects)\n  * [Collection Surrealization](#collection-surrealization)\n  * [Defining custom serializers](#defining-custom-serializers)\n  * [Multiple serializers](#multiple-serializers)\n  * [Build schema](#build-schema)\n  * [Defined schema](#defined-schema)\n  * [Working with ORMs](#working-with-orms)\n    * [ActiveRecord](#activerecord)\n    * [ROM](#rom)\n    * [Sequel](#sequel)\n  * [Usage with Dry::Types](#usage-with-drytypes)\n  * [Delegating Surrealization](#delegating-surrealization)\n  * [Optional arguments](#optional-arguments)\n    * [Camelization](#camelization)\n    * [Include root](#include-root)\n    * [Root](#root)\n    * [Include namespaces](#include-namespaces)\n  * [Configuration](#configuration)\n  * [Hash serialization](#hash-serialization)\n  * [Bool and Any](#bool-and-any)\n  * [Type errors](#type-errors)\n  * [Undefined methods in schema](#undefined-methods-in-schema)\n  * [Other notes](#other-notes)\n* [Roadmap](#roadmap)\n* [Contributing](#contributing)\n* [Credits](#credits)\n* [Authors](#authors)\n* [License](#license)\n\n\n## Installation\nAdd this line to your application's Gemfile:\n\n``` ruby\ngem 'surrealist'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install surrealist\n\n\n## Usage\nSchema should be defined with a block that contains a hash. Every key of the schema should be\neither a name of a method of the surrealizable object (or it's ancestors/mixins),\nor - in case you want to build json structure independently from object's structure - a symbol.\nEvery value of the hash should be a constant that represents a Ruby class,\nthat will be used for type-checks.\n\n### Simple example\n* Include Surrealist in your class.\n* Define a schema with methods that need to be serialized.\n\n``` ruby\nclass Person\n  include Surrealist\n\n  json_schema do\n    { name: String, age: Integer }\n  end\n\n  def name\n    'John Doe'\n  end\n\n  def age\n    42\n  end\nend\n```\n\n* Surrealize it.\n\n``` ruby\nPerson.new.surrealize\n# =\u003e '{ \"name\": \"John Doe\", \"age\": 42 }'\n```\n\n### Nested structures\n\n``` ruby\nclass Person\n  include Surrealist\n\n  json_schema do\n    {\n      foo: String,\n      name: String,\n      nested: {\n        at: {\n          any: Integer,\n          level: Bool,\n        },\n      },\n    }\n  end\n  # ... method definitions\nend\n\nPerson.find_by(email: 'example@email.com').surrealize\n# =\u003e '{ \"foo\": \"Some string\", \"name\": \"John Doe\", \"nested\": { \"at\": { \"any\": 42, \"level\": true } } }'\n```\n\n### Nested objects\nIf you need to serialize nested objects and their attributes, you should\ndefine a method that calls nested object:\n\n``` ruby\nclass User\n  include Surrealist\n\n  json_schema do\n    {\n      name: String,\n      credit_card: {\n        number: Integer,\n        cvv: Integer,\n      },\n    }\n  end\n\n  def name\n    'John Doe'\n  end\n\n  def credit_card\n    # Assuming that instance of a CreditCard has methods #number and #cvv defined\n    CreditCard.find_by(holder: name)\n  end\nend\n\nUser.new.surrealize\n# =\u003e '{ \"name\": \"John Doe\", \"credit_card\": { \"number\": 1234, \"cvv\": 322 } }'\n\n```\n\n### Collection Surrealization\nSince 0.2.0 Surrealist has API for collection serialization. Example for ActiveRecord:\n``` ruby\nclass User \u003c ActiveRecord::Base\n  include Surrealist\n\n  json_schema do\n    { name: String, age: Integer }\n  end\nend\n\nusers = User.all\n# =\u003e [#\u003cUser:0x007fa1485de878 id: 1, name: \"Nikita\", age: 23\u003e, #\u003cUser:0x007fa1485de5f8 id: 2, name: \"Alessandro\", age: 24\u003e]\n\nSurrealist.surrealize_collection(users)\n# =\u003e '[{ \"name\": \"Nikita\", \"age\": 23 }, { \"name\": \"Alessandro\", \"age\": 24 }]'\n```\nYou can find motivation behind introducing new API versus monkey-patching [here](https://alessandrominali.github.io/monkey_patching_real_example).\n`#surrealize_collection` works for all data structures that behave like `Enumerable`. All ActiveRecord\nfeatures (like associations, inheritance etc) are supported and covered. Further reading: [working with ORMs](#working-with-orms).\nAll optional arguments (`camelize`, `include_root` etc) are also supported.\n\nAn additional and unique argument for `#surrealize_collection` is `raw` which is evaluated as a Boolean.\nIf this option is 'truthy' then the results will be an array of surrealized hashes (i.e. NOT a JSON string).\n```\nSurrealist.surrealize_collection(users, raw: true)\n# =\u003e [{ \"name\": \"Nikita\", \"age\": 23 }, { \"name\": \"Alessandro\", \"age\": 24 }]\n```\n\n### Defining custom serializers\nIf you need to keep serialization logic separately from the model, you can define a class that\nwill inherit from `Surrealist::Serializer`. To point to that class from the model use a class method\n`.surrealize_with`. Example usage:\n``` ruby\nclass CatSerializer \u003c Surrealist::Serializer\n  json_schema { { age: Integer, age_group: String } }\n\n  def age_group\n    age \u003c= 5 ? 'kitten' : 'cat'\n  end\nend\n\nclass Cat\n  include Surrealist\n  attr_reader :age\n\n  surrealize_with CatSerializer\n\n  def initialize(age)\n    @age = age\n  end\nend\n\nCat.new(12).surrealize # Implicit usage through .surrealize_with\n# =\u003e '{ \"age\": 12, \"age_group\": \"cat\" }'\n\nCatSerializer.new(Cat.new(3)).surrealize # explicit usage of CatSerializer\n# =\u003e '{ \"age\": 3, \"age_group\": \"kitten\" }'\n```\nThe constructor of `Surrealist::Serializer` takes two arguments: serializable model (or collection) and\na context hash. So if there is an object that is not coupled to serializable model\nbut it is still necessary for constructing JSON, you can pass it to constructor as a hash. It will\nbe available in the serializer in the `context` hash.\n``` ruby\nclass IncomeSerializer \u003c Surrealist::Serializer\n  json_schema { { amount: Integer } }\n\n  def amount\n    current_user.guest? ? 100000000 : object.amount\n  end\n\n  def current_user\n    context[:current_user]\n  end\nend\n\nclass Income\n  include Surrealist\n  surrealize_with IncomeSerializer\n\n  attr_reader :amount\n\n  def initialize(amount)\n    @amount = amount\n  end\nend\n\nincome = Income.new(200)\nIncomeSerializer.new(income, current_user: GuestUser.new).surrealize\n# =\u003e '{ \"amount\": 100000000 }'\n\nIncomeSerializer.new(income, current_user: User.find(3)).surrealize\n# =\u003e '{ \"amount\": 200 }'\n```\nIf you happen to pass a context to a serializer, there is a handy DSL to reduce the number of methods\nyou have to define yourself. DSL looks as follows\n``` ruby\nclass IncomeSerializer \u003c Surrealist::Serializer\n  serializer_context :current_user\n  json_schema { { amount: Integer } }\n\n  def amount\n    current_user.guest? ? 100000000 : object.amount\n  end\nend\n```\n`.serializer_context` takes an array of symbols and dynamically defines instance methods\nthat read values from the context hash. So `.serializer_context :current_user` will become\n``` ruby\ndef current_user\n  context[:current_user]\nend\n```\nThere is also an alias in the plural form: `.serializer_contexts`.\n### Multiple serializers\n\nYou can define several custom serializers for one object and use it in different cases. Just mark it with a tag:\n\n``` ruby\nclass PostSerializer \u003c Surrealist::Serializer\n  json_schema { { id: Integer, title: String, author: { name: String } } }\nend\n\nclass PreviewSerializer \u003c Surrealist::Serializer\n  json_schema { { id: Integer, title: String } }\nend\n\nclass Post\n  include Surrealist\n\n  surrealize_with PostSerializer\n  surrealize_with PreviewSerializer, tag: :preview\n\n  attr_reader :id, :title, :author\nend\n```\n\nAnd then specify serializer's tag with `for` argument:\n``` ruby\nauthor = Struct.new(:name).new(\"John\")\npost = Post.new(1, \"Ruby is awesome\", author)\npost.surrealize # =\u003e '{ \"id\": 1, \"title\": \"Ruby is awesome\", author: { name: \"John\" } }'\n\npost.surrealize(for: :preview) # =\u003e '{ \"id\": 1, \"title\": \"Ruby is awesome\" }'\n```\nOr specify serializer explicitly with `serializer` argument:\n\n``` ruby\npost.surrealize(serializer: PreviewSerializer) # =\u003e '{ \"id\": 1, \"title\": \"Ruby is awesome\" }'\n```\n\n### Build schema\nIf you don't need to dump the hash to json, you can use `#build_schema`\nmethod on the instance. It calculates values and checks types, but returns\na hash instead of a JSON string. From the previous example:\n\n``` ruby\nCar.new.build_schema\n# =\u003e { age: 7, brand: \"Toyota\", doors: nil, horsepower: 140, fuel_system: \"Direct injection\", previous_owner: \"John Doe\" }\n```\n\n### Defined schema\nUse the `.defined_schema` method to get the schema that has been defined with `json_schema`:\n\n``` ruby\nUser.defined_schema\n# =\u003e { name: String, age: Integer }\n```\n\n### Working with ORMs\n\nThere are two kinds of return values of ORM methods: some return collections of objects, while others return instances.\nFor the first ones one should use `instance#surrealize`, whereas for the second ones `Surrealist.surrealize_collection(collection)`\nPlease keep in mind that if your serialization logic is [kept in a separate class](#defining-custom-serializers) which is inherited from\n`Surrealist::Serializer`, than usage boils down to `YourSerializer.new(instance || collection).surrealize`.\n\n#### ActiveRecord\nAll associations work as expected: `.has_many`, `.has_and_belongs_to_many` return collections,\n`.has_one`, `.belongs_to` return instances.\n\nMethods that return instances:\n``` ruby\n.find\n.find_by\n.find_by!\n.take!\n.first\n.first!\n.second\n.second!\n.third\n.third!\n.fourth\n.fourth!\n.fifth\n.fifth!\n.forty_two\n.forty_two!\n.last\n.last!\n.third_to_last\n.third_to_last!\n.second_to_last\n.second_to_last!\n```\nMethods that return collections:\n``` ruby\n.all\n.where\n.where_not\n.order\n.take\n.limit\n.offset\n.lock\n.readonly\n.reorder\n.distinct\n.find_each\n.select\n.group\n.order\n.except\n.extending\n.having\n.references\n.includes\n.joins\n```\n\n#### ROM\n\nFor detailed usage example (covering ROM 3.x and ROM 4.x) please see `spec/orms/rom/`.\nUnder the hood ROM uses Sequel, and Sequel returns instances only on `.first`, `.last`, `.[]` and `.with_pk!`.\nCollections are returned for all other methods.\n``` ruby\ncontainer = ROM.container(:sql, ['sqlite::memory']) do |conf|\n  conf.default.create_table(:users) do\n    primary_key :id\n    column :name, String, null: false\n    column :email, String, null: false\n  end\n  # ...\nend\n\nusers = UserRepo.new(container).users\n# =\u003e #\u003cROM::Relation[Users] name=ROM::Relation::Name(users) dataset=#\u003cSequel::SQLite::Dataset: \"SELECT `users`.`id`, `users`.`name`, `users`.`email` FROM `users` ORDER BY `users`.`id`\"\u003e\u003e\n```\nBasically, there are several ways to fetch/represent data in ROM:\n``` ruby\n# With json_schema defined in ROM::Struct::User\nclass ROM::Struct::User \u003c ROM::Struct\n  include Surrealist\n\n  json_schema { { name: String } }\nend\n\nusers.to_a.first # =\u003e #\u003cROM::Struct::User id=1 name=\"Jane Struct\" email=\"jane@struct.rom\"\u003e\nusers.to_a.first.surrealize # =\u003e \"{\\\"name\\\":\\\"Jane Struct\\\"}\"\n\nusers.where(id: 1).first # =\u003e #\u003cROM::Struct::User id=1 name=\"Jane Struct\" email=\"jane@struct.rom\"\u003e\nusers.where(id: 1).first.surrealize # =\u003e \"{\\\"name\\\":\\\"Jane Struct\\\"}\"\n\nSurrealist.surrealize_collection(users.to_a) # =\u003e \"[{\\\"name\\\":\\\"Jane Struct\\\"},{\\\"name\\\":\\\"Dane As\\\"},{\\\"name\\\":\\\"Jack Mapper\\\"}]\"\n\n# using ROM::Struct::Model#as(Representative) with json_schema defined in representative\nclass RomUser \u003c Dry::Struct\n  include Surrealist\n\n  attribute :name, String\n  attribute :email, String\n\n  json_schema { { email: String } }\nend\n\n# ROM 3.x\nrom_users = users.as(RomUser).to_a\n\n# ROM 4.x\nrom_users = users.map_to(RomUser).to_a\n\nrom_users[1].surrealize # =\u003e \"{\\\"email\\\":\\\"dane@as.rom\\\"}\"\nSurrealist.surrealize_collection(rom_users) # =\u003e \"[{\\\"email\\\":\\\"jane@struct.rom\\\"},{\\\"email\\\":\\\"dane@as.rom\\\"},{\\\"email\\\":\\\"jack@mapper.rom\\\"}]\"\n\n# using Mappers\nclass UserModel\n  include Surrealist\n\n  json_schema { { id: Integer, email: String } }\n\n  attr_reader :id, :name, :email\n\n  def initialize(attributes)\n    @id, @name, @email = attributes.values_at(:id, :name, :email)\n  end\nend\n\nclass UsersMapper \u003c ROM::Mapper\n  register_as :user_obj\n  relation :users\n  model UserModel\nend\n\n# ROM 3.x\nmapped = users.as(:user_obj)\n# ROM 4.x\nmapped = users.map_with(:user_obj)\n\nmapped.to_a[2] # =\u003e #\u003cUserModel:0x00007f8ec19fb3c8 @email=\"jack@mapper.rom\", @id=3, @name=\"Jack Mapper\"\u003e\nmapped.where(id: 3).first # =\u003e #\u003cUserModel:0x00007f8ec19fb3c8 @email=\"jack@mapper.rom\", @id=3, @name=\"Jack Mapper\"\u003e\nmapped.to_a[2].surrealize # =\u003e \"{\\\"id\\\":3,\\\"email\\\":\\\"jack@mapper.rom\\\"}\"\nSurrealist.surrealize_collection(mapped.to_a) # =\u003e \"[{\\\"email\\\":\\\"jane@struct.rom\\\"},{\\\"email\\\":\\\"dane@as.rom\\\"},{\\\"email\\\":\\\"jack@mapper.rom\\\"}]\"\nSurrealist.surrealize_collection(mapped.where { id \u003c 4 }.to_a) # =\u003e \"[{\\\"email\\\":\\\"jane@struct.rom\\\"},{\\\"email\\\":\\\"dane@as.rom\\\"},{\\\"email\\\":\\\"jack@mapper.rom\\\"}]\"\n```\n\n#### Sequel\nBasically, Sequel returns instances only on `.first`, `.last`, `.[]` and `.with_pk!`. Collections are returned for all other methods.\nMost of them are covered in `spec/orms/sequel` specs, please refer to them for code examples.\nAssociations serialization works the same way as it does with ActiveRecord.\n\n### Usage with Dry::Types\nYou can use `Dry::Types` for type checking. Note that Surrealist does not ship\nwith dry-types by default, so you should do the [installation and configuration](http://dry-rb.org/gems/dry-types/)\nby yourself. All built-in features of dry-types work, so if you use, say, `Types::Coercible::String`,\nyour data will be coerced if it is able to, otherwise you will get a TypeError.\nAssuming that you have defined module called `Types`:\n\n``` ruby\nrequire 'dry-types'\n\nclass Car\n  include Surrealist\n\n  json_schema do\n    {\n      age:            Types::Coercible::Integer,\n      brand:          Types::Coercible::String,\n      doors:          Types::Integer.optional,\n      horsepower:     Types::Strict::Integer.constrained(gteq: 20),\n      fuel_system:    Types::Any,\n      previous_owner: Types::String,\n    }\n  end\n\n  def age;\n    '7'\n  end\n\n  def previous_owner;\n    'John Doe'\n  end\n\n  def horsepower;\n    140\n  end\n\n  def brand;\n    'Toyota'\n  end\n\n  def doors; end\n\n  def fuel_system;\n    'Direct injection'\n  end\nend\n\nCar.new.surrealize\n# =\u003e '{ \"age\": 7, \"brand\": \"Toyota\", \"doors\": null, \"horsepower\": 140, \"fuel_system\": \"Direct injection\", \"previous_owner\": \"John Doe\" }'\n```\n\n### Delegating surrealization\nYou can share the `json_schema` between classes:\n``` ruby\nclass Host\n  include Surrealist\n\n  json_schema do\n    { name: String }\n  end\n\n  def name\n    'Host'\n  end\nend\n\nclass Guest\n  delegate_surrealization_to Host\n\n  def name\n    'Guest'\n  end\nend\n\nHost.new.surrealize\n# =\u003e '{ \"name\": \"Host\" }'\nGuest.new.surrealize\n# =\u003e '{ \"name\": \"Guest\" }'\n```\nSchema delegation works without inheritance as well, so if you wish you can\ndelegate surrealization not only to parent classes, but to any class. Please note that\nin this case you have to `include Surrealist` in class that delegates schema as well.\n``` ruby\nclass Potato\n  include Surrealist\n  delegate_surrealization_to Host\n\n  def name\n    'Potato'\n  end\nend\n\nPotato.new.surrealize\n# =\u003e '{ \"name\": \"Potato\" }'\n```\n\n### Optional arguments\n\n#### Camelization\nIf you need to have keys in camelBack, you can pass optional `camelize` argument\nto `#surrealize or #build_schema`. From the previous example:\n\n``` ruby\nCar.new.surrealize(camelize: true)\n# =\u003e '{ \"age\": 7, \"brand\": \"Toyota\", \"doors\": null, \"horsepower\": 140, \"fuelSystem\": \"Direct injection\", \"previousOwner\": \"John Doe\" }'\n```\n\n#### Include root\nIf you want to wrap the resulting JSON into a root key, you can pass optional `include_root` argument\nto `#surrealize` or `#build_schema`. The root key in this case will be taken from the class name of the\nsurrealizable object.\n``` ruby\nclass Cat\n  include Surrealist\n\n  json_schema do\n    { weight: String }\n  end\n\n  def weight\n    '3 kilos'\n  end\nend\n\nCat.new.surrealize(include_root: true)\n# =\u003e '{ \"cat\": { \"weight\": \"3 kilos\" } }'\n```\nWith nested classes the last namespace will be taken as root key:\n``` ruby\nclass Animal\n  class Dog\n    include Surrealist\n\n    json_schema do\n      { breed: String }\n    end\n\n    def breed\n      'Collie'\n    end\n  end\nend\n\nAnimal::Dog.new.surrealize(include_root: true)\n# =\u003e '{ \"dog\": { \"breed\": \"Collie\" } }'\n```\n\n#### Root\nIf you want to wrap the resulting JSON into a specified root key, you can pass optional `root` argument\nto `#surrealize` or `#build_schema`. The `root` argument will be stripped of whitespaces.\n``` ruby\nclass Cat\n  include Surrealist\n\n  json_schema do\n    { weight: String }\n  end\n\n  def weight\n    '3 kilos'\n  end\nend\n\nCat.new.surrealize(root: :kitten)\n# =\u003e '{ \"kitten\": { \"weight\": \"3 kilos\" } }'\nCat.new.surrealize(root: ' kitten ')\n# =\u003e '{ \"kitten\": { \"weight\": \"3 kilos\" } }'\n```\nThis overrides the `include_root` and `include_namespaces` arguments.\n``` ruby\nAnimal::Cat.new.surrealize(include_root: true, root: :kitten)\n# =\u003e '{ \"kitten\": { \"weight\": \"3 kilos\" } }'\nAnimal::Cat.new.surrealize(include_namespaces: true, root: 'kitten')\n# =\u003e '{ \"kitten\": { \"weight\": \"3 kilos\" } }'\n```\n\n#### Include namespaces\nYou can build wrap schema into a nested hash from namespaces of the object's class.\n``` ruby\nclass BusinessSystem::Cashout::ReportSystem::Withdraws\n  include Surrealist\n\n  json_schema do\n    { withdraws_amount: Integer }\n  end\n\n  def withdraws_amount\n    34\n  end\nend\n\nwithdraws = BusinessSystem::Cashout::ReportSystem::Withdraws.new\n\nwithdraws.surrealize(include_namespaces: true)\n# =\u003e '{ \"business_system\": { \"cashout\": { \"report_system\": { \"withdraws\": { \"withdraws_amount\": 34 } } } } }'\n```\nBy default all namespaces will be taken. If you want you can explicitly specify the level of nesting:\n``` ruby\nwithdraws.surrealize(include_namespaces: true, namespaces_nesting_level: 2)\n# =\u003e '{ \"report_system\": { \"withdraws\": { \"withdraws_amount\": 34 } } }'\n```\n\n### Configuration\n\nThere are two ways of setting default arguments for serialization,\nby passing a block to `Surrealist.configure`:\n``` ruby\nSurrealist.configure do |config|\n  config.camelize = true\n  config.namespaces_nesting_level = 2\nend\n```\nAnd by passing a hash:\n\n`Surrealist.configure(camelize: true, include_root: true)`\n\nThese arguments will be applied to all calls of `#build_schema` and `#surrealize`.\nIf these methods will be called with arguments, they will be merged with respect to explicitly passed ones:\n\n``` ruby\nSurrealist.configure(camelize: true, include_root: true)\n\nSomething.new.surrealize(camelize: false)\n# will result in Something.new.surrealize(camelize: false, include_root: true)\n```\n\n### Hash serialization\n\nYou can pass a hash to serializer and it will use the keys instead of methods.\n\n``` ruby\nclass HashSerializer \u003c Surrealist::Serializer\n  json_schema { { string: String, int: Integer } }\nend\n\nHashSerializer.new(string: 'string', int: 4).surrealize\n# =\u003e '{ \"string\": \"string\", \"int\": 4}'\n\nHashSerializer.new(string: 'string', int: 'not int').surrealize\n# =\u003e Surrealist::InvalidTypeError: Wrong type for key `int`. Expected Integer, got String.\n```\n\n### Bool and Any\nIf you have a parameter that is of boolean type, or if you don't care about the type, you\ncan use `Bool` and `Any` respectively.\n\n``` ruby\nclass User\n  include Surrealist\n\n  json_schema do\n    {\n      age: Any,\n      admin: Bool,\n    }\n  end\nend\n```\n\n### Type Errors\n`Surrealist::InvalidTypeError` is thrown if types (and dry-types) mismatch.\n\n``` ruby\nclass CreditCard\n  include Surrealist\n\n  json_schema do\n    { number: Integer }\n  end\n\n  def number\n    'string'\n  end\nend\n\nCreditCard.new.surrealize\n# =\u003e Surrealist::InvalidTypeError: Wrong type for key `number`. Expected Integer, got String.\n```\n\n### Undefined methods in schema\n`Surrealist::UndefinedMethodError` is thrown if a key defined in the schema does not have\na corresponding method defined in the object.\n\n``` ruby\nclass Car\n  include Surrealist\n\n  json_schema do\n    { weight: Integer }\n  end\nend\n\nCar.new.surrealize\n# =\u003e Surrealist::UndefinedMethodError: undefined method `weight' for #\u003cCar:0x007f9bc1dc7fa8\u003e. You have probably defined a key in the schema that doesn't have a corresponding method.\n```\n\n### Other notes\n* nil values are allowed by default, so if you have, say, `age: String`, but the actual value is nil,\ntype check will be passed. If you want to be strict about `nil`s consider using `Dry::Types`.\n* Surrealist [officially supports](https://travis-ci.org/nesaulov/surrealist) MRI Ruby 2.3+ but should be working on other platforms as well.\n\n## Roadmap\nHere is a list of features that are not implemented yet (contributions are welcome):\n* Automatic endpoint documentation\n* API for validating (contracts) without actually serializing to JSON (maybe with deserialization from JSON)\n\n## Contributing\nBug reports and pull requests are welcome on GitHub at https://github.com/nesaulov/surrealist.\nThis project is intended to be a safe, welcoming space for collaboration, and contributors are expected\nto adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## Credits\nThe icon was created by [Simon Child from Noun Project](https://thenounproject.com/term/salvador-dali/124566/) and is published under [Creative Commons License](https://creativecommons.org/licenses/by/3.0/us/)\n\n## Authors\nCreated by [Nikita Esaulov](https://github.com/nesaulov) with help from [Alessandro Minali](https://github.com/AlessandroMinali) and [Alexey Bespalov](https://github.com/nulldef).\n\n\u003ca href=\"https://github.com/umbrellio/\"\u003e\n\u003cimg style=\"float: left;\" src=\"https://umbrellio.github.io/Umbrellio/supported_by_umbrellio.svg\" alt=\"Supported by Umbrellio\" width=\"439\" height=\"72\"\u003e\n\u003c/a\u003e\n\n## License\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%2Fnesaulov%2Fsurrealist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnesaulov%2Fsurrealist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnesaulov%2Fsurrealist/lists"}