{"id":13508961,"url":"https://github.com/gaynetdinov/ex_marshal","last_synced_at":"2025-10-05T01:56:00.235Z","repository":{"id":2295255,"uuid":"45642603","full_name":"gaynetdinov/ex_marshal","owner":"gaynetdinov","description":"Ruby Marshal format implemented in Elixir","archived":false,"fork":false,"pushed_at":"2024-07-11T16:45:28.000Z","size":180,"stargazers_count":39,"open_issues_count":7,"forks_count":16,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-04T13:59:17.373Z","etag":null,"topics":["elixir","marshal","ruby"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gaynetdinov.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,"publiccode":null,"codemeta":null}},"created_at":"2015-11-05T22:08:33.000Z","updated_at":"2023-09-01T12:07:35.000Z","dependencies_parsed_at":"2025-02-17T00:08:04.672Z","dependency_job_id":"fa5d3d4b-2605-444d-a8aa-66568600b181","html_url":"https://github.com/gaynetdinov/ex_marshal","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/gaynetdinov/ex_marshal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaynetdinov%2Fex_marshal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaynetdinov%2Fex_marshal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaynetdinov%2Fex_marshal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaynetdinov%2Fex_marshal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gaynetdinov","download_url":"https://codeload.github.com/gaynetdinov/ex_marshal/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaynetdinov%2Fex_marshal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278399692,"owners_count":25980332,"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","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["elixir","marshal","ruby"],"created_at":"2024-08-01T02:01:01.071Z","updated_at":"2025-10-05T01:56:00.215Z","avatar_url":"https://github.com/gaynetdinov.png","language":"Elixir","funding_links":[],"categories":["Protocols"],"sub_categories":[],"readme":"# ExMarshal [![Build Status](https://travis-ci.org/gaynetdinov/ex_marshal.svg?branch=master)](https://travis-ci.org/gaynetdinov/ex_marshal) [![Hex pm](http://img.shields.io/hexpm/v/ex_marshal.svg?style=flat)](https://hex.pm/packages/ex_marshal)\n\n`ExMarshal` encodes and decodes Elixir terms according to [Ruby Marshal](http://docs.ruby-lang.org/en/2.2.0/marshal_rdoc.html) format.\n\nCurrently supported Ruby types are `nil`, `false`, `true`, `Fixnum`, `Bignum`, `BigDecimal`, `Float`, `Symbol`, `String`, `Array`, `Hash` as well as user-defined types such as `Date`.\n\n## Why?\n\nOnce you decide to integrate small Elixir tool into big-old-legacy Ruby system, chances are that you need to interact with [Memcached](http://memcached.org). As soon as Ruby code writes something into Memcached, most likely Ruby uses [dalli](https://github.com/mperham/dalli) gem. And `Dalli` uses [Ruby Marshal](http://docs.ruby-lang.org/en/2.2.0/marshal_rdoc.html) by default.\n\nAnother use case is to decode Rails session encoded using Ruby Marshal. For this you can use [plug_rails_cookie_session_store](https://github.com/cconstantin/plug_rails_cookie_session_store) or [plug_session_redis](https://github.com/aposto/plug_session_redis#custom-serializers) if you want to store session data in Redis.\n\n## Installation\n\nAdd ExMarshal as a dependency to your `mix.exs` file:\n\n```elixir\ndef deps do\n  [{:ex_marshal, \"0.0.13\"}]\nend\n```\n\n## Usage\n\n```elixir\niex(1)\u003e ExMarshal.decode(\u003c\u003c4, 8, 91, 8, 105, 6, 105, 7, 105, 8\u003e\u003e)\n[1, 2, 3]\niex(2)\u003e ExMarshal.encode([1, 2, 3])\n\u003c\u003c4, 8, 91, 8, 105, 6, 105, 7, 105, 8\u003e\u003e\niex(3)\u003e\n```\n\nIt is also possible to decode user objects such as Ruby dates using custom parsers which can be specified via the `user_object_parsers` option:\n\n```elixir\niex(1)\u003e value = \u003c\u003c4, 8, 85, 58, 9, 68, 97, 116, 101, 91, 11, 105, 0, 105, 3, 72, 136, 37, 105, 0, 105, 0, 105, 0, 102, 12, 50, 50, 57, 57, 49, 54, 49\u003e\u003e\n\u003c\u003c4, 8, 85, 58, 9, 68, 97, 116, 101, 91, 11, 105, 0, 105, 3, 72, 136, 37, 105,\n  0, 105, 0, 105, 0, 102, 12, 50, 50, 57, 57, 49, 54, 49\u003e\u003e\niex(2)\u003e ExMarshal.decode(value, user_object_parsers: %{Date: fn [_, julian_day, _, _, _, _] -\u003e Date.from_gregorian_days(julian_day - 1721425) end})\n~D[2021-05-20]\n```\n\n## Nullify Ruby Objects\n\nThe default behaviour of ExMarshal is to raise an error when trying to decode an serilized ruby object. This config option can be used to nullify the ruby object without raising an error:\n\n```elixir\nconfig :ex_marshal,\n  nullify_objects: true\n```\n\n__Use Case__ Rails 3.2 puts Ruby Objects into session(i.e. Flash messages wrapped in an object). When Phoenix is trying to read Rails session, this will cause ExMarshal to raise an error and a 500 error in Phoenix. If you don't need to read these values from Elixir/Phoenix, this option allows ExMarshal to skip objects.\n\n## ExMarshal with Memcache.Client\n\nOne of the reasons why `ExMarshal` was created is to work with `Memcached`. Here is how `ExMarshal` can be used with [Memcache.Client](https://github.com/tsharju/memcache_client):\n\n```elixir\n\ndefmodule Memcache.Client.Transcoder.Ruby do\n  @behaviour Memcache.Client.Transcoder\n\n  @ruby_type_flag 0x0001\n\n  def encode_value(value) do\n    {ExMarshal.encode(value), @ruby_type_flag}\n  end\n\n  def decode_value(value, @ruby_type_flag) do\n    ExMarshal.decode(value)\n  end\n\n  def decode_value(_value, data_type) do\n    {:error, {:invalid_data_type, data_type}}\n  end\nend\n```\n\nThen tell `Memcache.Client` to use this transcoder:\n\n```elixir\nconfig :memcache_client,\n  transcoder: Memcache.Client.Transcoder.Ruby\n```\n\n### Examples\n\n#### Read\n\nRuby side\n\n```ruby\n:1 \u003e dc = Dalli::Client.new('localhost:11211')\n:2 \u003e dc.set(\"str\", \"hello elixir\")\n =\u003e true\n\n```\n\nElixir side\n\n```elixir\niex(1)\u003e Memcache.Client.get(\"str\")\n%Memcache.Client.Response{cas: 184, data_type: 1, extras: \u003c\u003c0, 0, 0, 1\u003e\u003e,\n key: \"\", status: :ok, value: \"hello elixir\"}\n```\n\n#### Write\n\nElixir side\n\n```elixir\niex(1)\u003e Memcache.Client.set(\"str\", \"hello ruby\")\n%Memcache.Client.Response{cas: 185, data_type: nil, extras: \"\", key: \"\",\n status: :ok, value: \"\"}\n```\n\nRuby side\n\n```ruby\n:1 \u003e dc = Dalli::Client.new('localhost:11211')\n:2 \u003e dc.get(\"str\")\n =\u003e \"hello ruby\"\n```\n\n## Kudos\n\nThanks to [@tsharju](https://github.com/tsharju) for ability to use custom transcoders in [Memcache.Client](https://github.com/tsharju/memcache_client)\n\nThanks to [@shepmaster](https://github.com/shepmaster) for [series](http://jakegoulding.com/blog/2013/01/15/a-little-dip-into-rubys-marshal-format/) [of](http://jakegoulding.com/blog/2013/01/16/another-dip-into-rubys-marshal-format/) [posts](http://jakegoulding.com/blog/2013/01/20/a-final-dip-into-rubys-marshal-format/) about Ruby Marshal format.\n\nSpecial thanks to [@lexmag](https://github.com/lexmag) for help in writing this tool and for guiding me through Elixir world.\n\n## License\n\nThis software is licensed under [the ISC license](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgaynetdinov%2Fex_marshal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgaynetdinov%2Fex_marshal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgaynetdinov%2Fex_marshal/lists"}