{"id":13604906,"url":"https://github.com/ravendb/ravendb-ruby-client","last_synced_at":"2025-04-09T16:15:34.894Z","repository":{"id":66776764,"uuid":"92496130","full_name":"ravendb/ravendb-ruby-client","owner":"ravendb","description":null,"archived":false,"fork":false,"pushed_at":"2018-08-08T06:48:41.000Z","size":612,"stargazers_count":4,"open_issues_count":1,"forks_count":5,"subscribers_count":6,"default_branch":"v4.0","last_synced_at":"2025-04-09T16:15:28.233Z","etag":null,"topics":[],"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/ravendb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2017-05-26T09:42:08.000Z","updated_at":"2024-05-21T10:47:27.000Z","dependencies_parsed_at":null,"dependency_job_id":"6075cb80-cfe5-4d6b-b6cf-c4151a387846","html_url":"https://github.com/ravendb/ravendb-ruby-client","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ravendb%2Fravendb-ruby-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ravendb%2Fravendb-ruby-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ravendb%2Fravendb-ruby-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ravendb%2Fravendb-ruby-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ravendb","download_url":"https://codeload.github.com/ravendb/ravendb-ruby-client/tar.gz/refs/heads/v4.0","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248065282,"owners_count":21041872,"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":[],"created_at":"2024-08-01T19:00:52.575Z","updated_at":"2025-04-09T16:15:34.877Z","avatar_url":"https://github.com/ravendb.png","language":"Ruby","funding_links":[],"categories":["Languages"],"sub_categories":["Ruby"],"readme":"# RavenDB client for Ruby\n\n[![build status](https://travis-ci.org/ravendb/ravendb-ruby-client.svg?branch=v4.0)](https://travis-ci.org/ravendb/ravendb-ruby-client)\n## Installation\n\nFor global install run:\n\n```bash\ngem install --save ravendb\n```\n\nOr require `ravendb` gem in your project's `Gemfile`:\n\n```\nsource 'https://rubygems.org'\ngem 'ravendb'\n```\n\n## Getting started\n\n1. Declare document models as classes. Attributes should be just an instance variables accessible via `attr_accessor()`\n\n```ruby\nclass Product\n  :attr_accessor :id, :title, :price, :currency,\n                 :storage, :manufacturer, :in_stock,\n                 :last_update\n  \n  def initialize(\n    id = nil,\n    title = '',\n    price = 0,\n    currency = 'USD',\n    storage = 0,\n    manufacturer = '',\n    in_stock = false,\n    last_update = nil\n  )\n    @id = id\n    @title = title\n    @price = price\n    @currency = currency\n    @storage\n    @manufacturer = manufacturer\n    @in_stock = in_stock\n    @last_update = last_update\n  end\nend\n```\n\n2. Require `ravendb` gem and models classes in your application:\n\n```ruby\nrequire 'ravendb'\nrequire 'models/product'\n```\n\n3. Configure document store:\n\n```ruby\nRavenDB.store.configure do |config|\n  config.urls = [\"database url\"]\n  config.database = 'database name'\nend\n```\n\n3. Open a session:\n\n```ruby\nsession = RavenDB.store.open_session\n```\n\n4. Do some actions with documents:\n\n```ruby\nproduct = session.load('Products/1-A')\nproduct.in_stock = true\n```\n\n5. Call `save_changes` when you'll finish working with a session:\n\n```ruby\nsession.save_changes\n```\n\n6. Also you can wrap all actions done with a session in a nested block:\n\n```ruby\nRavenDB.store.open_session do |session|\n  product = session.load('Products/1-A')\n  product.in_stock = true\n\n  session.save_changes\nend\n```\n\n## CRUD example\n\n### Creating documents\n\n```ruby\nproduct = Product.new('iPhone X', 999.99, 'USD', 64, 'Apple', true, DateTime.new(2017, 10, 1, 0, 0, 0))\n\nRavenDB.store.open_session do |session|\n  product = session.store(product)\n  \n  puts product.id # will output Products/\u003csome number\u003e-\u003csome letter (server node tag)\u003e e.g. Products/1-A\n  session.save_changes\nend  \n```\n\n### Loading documents\n\n```ruby\nRavenDB.store.open_session do |session|\n  product = session.load('Products/1-A')\n  \n  puts product.title # iPhone X\n  puts product.id # Products/1-A\nend\n```\n\n### Updating documents\n```ruby\nRavenDB.store.open_session do |session|\n  product = session.load('Products/1-A')\n  \n  product.in_stock = false\n  product.last_update = DateTime.now\n\n  session.save_changes\n\n  product = session.load('Products/1-A')\n  \n  puts product.in_stock.inspect # false\n  puts product.last_update # outputs current date\nend  \n```\n\n### Deleting documents\n\n```ruby\nRavenDB.store.open_session do |session|\n  product = session.load('Products/1-A')\n  \n  session.delete(product)\n  # or you can just do\n  # session.delete('Products/1-A')\n  session.save_changes\n\n  product = session.load('Products/1-A')\n  puts product.inspect # nil\nend\n```\n\n## Querying documents\n\n1. Create `RavenDB::DocumentQuery` instance using `query` method of session:\n```ruby\nquery = session.query({\n  :collection =\u003e 'Products', # specify which collection you'd like to query\n  # optionally you may specify an index name for querying\n  # :index_name =\u003e 'PopularProductsWithViewsCount'\n})\n```\n2. Apply conditions, ordering etc. Query supports chaining calls:\n```ruby\nquery\n  .wait_for_non_stale_results\n  .using_default_operator(RavenDB::QueryOperator::AND)\n  .where_equals('manufacturer', 'Apple')\n  .where_equals('in_stock', true)\n  .where_between('last_update', DateTime.strptime('2017-10-01T00:00:00', '%Y-%m-%dT%H:%M:%S'), DateTime::now)\n  .order_by('price')\n```\n3. Finally, you may get query results:\n```\ndocuments = query.all\n```\n\n5. If you used `select_fields` method in in query, pass document class (or class name) as `:document_type` parameter in the query options:\n\n```ruby\nproducts_with_names_only = session.query({\n    :document_type =\u003e Product,\n    :collection =\u003e 'Products'\n  })\n  .select_fields(['name'])\n  .wait_for_non_stale_results\n  .all\n```\n\n#### RavenDB::DocumentQuery methods overview\n\u003ctable\u003e\n    \u003ctr\u003e\n        \u003cth\u003eMethod\u003c/th\u003e\n        \u003cth\u003eRQL / description\u003c/th\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003eselect_fields(fields, projections = nil)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003eselect field1 [as projection1], ...\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003edistinct\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003eselect distinct\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003ewhere_equals(field_name, value, \nexact = false)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere fieldName = \u0026lt;value\u0026gt;\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003ewhere_not_equals(field_name, value, \nexact = false)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere fieldName != \u0026lt;value\u0026gt;\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003ewhere_in(field_name, values, exact = false)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere fieldName in (\u0026lt;value1\u0026gt;, \u0026lt;value2\u0026gt;, ...)\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003ewhere_starts_with(field_name, value)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere startsWith(fieldName, '\u0026lt;value\u0026gt;')\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003ewhere_ends_with(field_name, value)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere endsWith(fieldName, '\u0026lt;value\u0026gt;')\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003ewhere_between(field_name, from, to, \nexact = nil)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere fieldName BETWEEN \u0026lt;start\u0026gt; AND \u0026lt;end\u0026gt;\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003ewhere_greater_than(field_name, value, \nexact = nil)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere fieldName \u003e \u0026lt;value\u0026gt;\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003ewhere_greater_than_or_equal(field_name, \nvalue, exact = nil)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere fieldName \u003e= \u0026lt;value\u0026gt;\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003ewhere_less_than(field_name, value, \nexact = nil)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere fieldName \u003c \u0026lt;value\u0026gt;\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003ewhere_less_than_or_equal(field_name, \nvalue, exact = nil)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere fieldName \u003c= \u0026lt;value\u0026gt;\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003ewhere_exists(field_name)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere exists(fieldName)\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003econtains_any(field_name, values)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere fieldName in (\u0026lt;value1\u0026gt;, \u0026lt;value2\u0026gt;, ...)\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003econtains_all(field_name, values)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003ewhere fieldName all in (\u0026lt;value1\u0026gt;, \u0026lt;value2\u0026gt;, ...)\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003esearch(field_name, search_terms, \noperator = RavenDB::SearchOperator::OR)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003ePerforms full-text search\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003eopen_subclause\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003eOpens subclause \u003ccode\u003e(\u003c/code\u003e\u003c/pre\u003e\n        \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003eclose_subclause\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003eCloses subclause \u003ccode\u003e)\u003c/code\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003enegate_next\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003eAdds \u003ccode\u003enot\u003c/code\u003e before next condition\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003eand_also\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003eAdds \u003ccode\u003eand\u003c/code\u003e before next condition\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003eor_else\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003eAdds \u003ccode\u003eor\u003c/code\u003e before next condition\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003eusing_default_operator(operator)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003eSets default operator (which will be used if no \u003ccode\u003eand_also\u003c/code\u003e / \u003ccode\u003eor_else\u003c/code\u003e was called. Just after query instantiation, \u003ccode\u003eOR\u003c/code\u003e is used as default operator. Default operator can be changed only before adding any conditions\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003eorder_by(field, ordering_type = nil)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003eorder by field\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003eorder_by_descending(field, \nordering_type = nil)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003eorder by field desc\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003erandom_ordering(seed = nil)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003cpre lang=\"sql\"\u003eorder by random()\u003c/pre\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003etake(count)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003eLimits the number of result entries to \u003ccode\u003ecount\u003c/code\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003eskip(count)\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003eSkips first \u003ccode\u003ecount\u003c/code\u003e results\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003efirst\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003eReturns first document from result set\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003esingle\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003eReturns single document matching query criteria. If there are no such document or more then one - throws an Exception\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003eall\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003eReturns all documents from result set (considering \u003ccode\u003etake\u003c/code\u003e / \u003ccode\u003eskip\u003c/code\u003e options)\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003cpre lang=\"ruby\"\u003ecount\u003c/pre\u003e\u003c/td\u003e\n        \u003ctd\u003eReturns count of all documents matching query criteria (non-considering \u003ccode\u003etake\u003c/code\u003e / \u003ccode\u003eskip\u003c/code\u003e options)\u003c/td\u003e\n    \u003c/tr\u003e\n\u003c/table\u003e\n\n\n## Working with secured server\n1. Instantiate `RavenDB::StoreAuthOptions`. Pass contents of the .pem certificate and passphrase (optional) to constructor:\n```ruby\ncertificate = \u003c\u003cCERTIFICATE\n-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----\nCERTIFICATE\nauth_options = RavenDB::StoreAuthOptions.new(certificate)\n\n#or \n\nauth_options = RavenDB::StoreAuthOptions.new(certificate, \"my passphrase\")\n``` \n2. Pass `RavenDB::StoreAuthOptions` instance to `auth_options` config option when you're configuring store:\n\n```ruby\nRavenDB.store.configure do |config|\n  config.urls = [\"database url\"]\n  config.database = 'database name'\n  config.auth_options = RavenDB::StoreAuthOptions.new(certificate)\nend\n```\n\n#### Auth exceptions\n- if no `RavenDB::StoreAuthOptions` was provided and you're trying to work with secured server, an `RavenDB::NotSupportedException` will be raised during store initialization\n- if certificate is invalid or doesn't have permissions for specific operations, an `RavenDB::AuthorizationException` will be raised\n\n## Advanced features\n\n#### Attachments support\n\nYou can attach binary files to documents. \n\n1. To attach a file, use `RavenDB::PutAttachmentOperation`. Pass document id, attachment name (it can be just a file name), content type and file contents:\n\n```ruby\nrequire 'ravendb'\n\n# ...\nfile_name = './iphone-x.png'\n\nstore.operations.send(\n  RavenDB::PutAttachmentOperation.new(\n    document_id: 'Products/1-A', \n    name: File.basename(file_name), \n    stream: File.read(file_name), \n    content_type: 'image/png' \n  )\n)   \n```\n\n2. To read an attachment, use `RavenDB::GetAttachmentOperation`. Pass document id and attachment name. File contents will be stored as `:stream` key of response:\n\n```ruby\nrequire 'ravendb'\n\n# ...\nfile_name = 'iphone-x.png'\n\nattachment_result = store.operations.send(\n  RavenDB::GetAttachmentOperation.new(\n    document_id: 'Products/1-A', \n    name: file_name, \n    type: RavenDB::AttachmentType::DOCUMENT\n  )\n)\n\nfile = File.new(\"./${file_name}\", \"wb\")\nfile.write(attachment_result[:stream])\nfile.close\n```\n\n3. To delete an attachment, use `RavenDB::DeleteAttachmentOperation`. Pass document id and attachment name.\n\n```ruby\nrequire 'ravendb'\n\n# ...\nfile_name = 'iphone-x.png'\n\nstore.operations.send(\n  RavenDB::DeleteAttachmentOperation.new(\n    document_id: 'Products/1-A', \n    name: file_name\n  )\n)\n```\n\n#### Custom document id property\n\nBy default document id is stored onto `id` property of document. But you can define which name of id property should be for specific document types. \n\n1. Call `store.conventions.add_id_property_resolver` and pass an callback block. It accepts document info hash and should return id property name:\n\n```ruby\n# models/item.rb\nclass Item\n  attr_accessor :item_id, :item_title, :item_options\n\n  def initialize(item_id = nil, item_title = \"\", item_options = [])\n    @item_id = item_id\n    @item_title = item_title   \n    @item_options = item_options \n  end  \nend\n\n# main.rb\nrequire 'ravendb'\n    \n# ...\nstore.conventions.add_id_property_resolver do |document_info|\n  case document_info[:document_type]\n  when Item.name\n    'item_id'\n  else\n    'id'  \n  end\nend\n```\n\n2. Now client will read/fill `item_id` property with document id while doing CRUD operations:\n\n```ruby\nsession = store.open_session\n\nsession.store(Item.new(nil, 'First Item', [1, 2, 3]))\nsession.save_changes\n\nputs item.item_id # Items/1-A\n\nsession = store.open_session\nitem = session.load('Items/1-A')\n\nputs item.item_id # Items/1-A\nputs item.item_title # First Item\nputs item.item_options.inspect # [1, 2, 3]\n```\n\n#### Custom attributes serializer\n\nYou can define custom serializers if you need to implement your own logic for convert attributes names/values for specific document types.\n\n1. Define your serializer as an class derived from `RavenDB::AttributeSerializer`.  Override `on_serialized` / `on_unserialized` methods:\n\n```ruby\nclass CustomAttributeSerializer \u003c RavenDB::AttributeSerializer\n  def on_serialized(serialized)\n  end\n\n  def on_unserialized(serialized)\n  end\nend\n```\n\nWhere `serialized` attribute is a hash with the following structure:\n\n```ruby\n{\n  :source =\u003e \u003csource json hash / document model\u003e,\n  :target =\u003e \u003ctarget json hash / document model\u003e,\n  :original_attribute =\u003e \u003coriginal attribute name\u003e,\n  :serialized_attribute =\u003e \u003ctarget attribute name\u003e,\n  :original_value =\u003e \u003coriginal attribute value\u003e,\n  :serialized_value =\u003e \u003ctarget attribute value\u003e,\n  :metadata =\u003e \u003cdocument metadata hash\u003e\n}\n```\n\n2. Store target attribute name/value into the `serialized_attribute`/`serialized_value` properties of `serialized` parameter:\n\n```ruby\n# serializers/custom.rb\n\nclass CustomAttributeSerializer \u003c RavenDB::AttributeSerializer\n  def on_serialized(serialized)\n    metadata = serialized[:metadata]\n\n    case metadata['Raven-Ruby-Type']\n    when TestCustomSerializer.name\n      serialized[:serialized_attribute] = serialized[:original_attribute].camelize(:lower)\n\n      if serialized[:original_attribute] == 'item_options'\n        serialized[:serialized_value] = serialized[:original_value].join(\",\")\n      end\n    else\n      nil  \n    end\n  end\n\n  def on_unserialized(serialized)\n    metadata = serialized[:metadata]\n\n    case metadata['Raven-Ruby-Type']\n    when TestCustomSerializer.name\n      serialized[:serialized_attribute] = serialized[:original_attribute].underscore\n\n      if serialized[:original_attribute] == 'itemOptions'\n        serialized[:serialized_value] = serialized[:original_value].split(\",\").map { |option| option.to_i }\n      end\n    else\n      nil  \n    end\n  end\nend\n```\n\n3. Pass your serializer object to `conventions.add_attribute_serializer`:\n\n```ruby\nrequire 'ravendb'\nrequire './serializers/custom'\n\nstore.conventions.add_attribute_serializer(serializer)\n\nsesssion = store.open_session\n\nsession.store(Item.new(nil, 'First Item', [1, 2, 3]))\nsession.save_changes\n\nsession = store.open_session\nitem = session.load('Items/1-A')\n\nputs item.item_id # Items/1-A\nputs item.item_title # First Item\nputs item.item_options.inspect # [1, 2, 3]\n\nresponse = store.get_request_executor.execute(RavenDB::GetDocumentCommand.new('Items/1-A'))\nraw_document = response['Results'].first\n\nputs raw_document['@metadata']['@id'] # Items/1-A\nputs raw_document['itemTitle'] # First Item\nputs raw_document['itemOptions'] # \"1,2,3\"\n```\n\n## Running tests\n\n```bash\nURL=\u003cRavenDB server url including port\u003e [CERTIFICATE=\u003cpath to .pem certificate\u003e [PASSPHRASE=\u003c.pem certificate passphrase\u003e]] rake test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fravendb%2Fravendb-ruby-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fravendb%2Fravendb-ruby-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fravendb%2Fravendb-ruby-client/lists"}