{"id":13463519,"url":"https://github.com/savonrb/gyoku","last_synced_at":"2025-05-15T10:00:50.086Z","repository":{"id":42506095,"uuid":"1122242","full_name":"savonrb/gyoku","owner":"savonrb","description":"Translates Ruby Hashes to XML","archived":false,"fork":false,"pushed_at":"2024-02-17T15:52:54.000Z","size":179,"stargazers_count":229,"open_issues_count":9,"forks_count":57,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-04-29T06:32:07.730Z","etag":null,"topics":[],"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/savonrb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"MIT-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":"2010-11-29T14:08:09.000Z","updated_at":"2025-02-04T16:34:39.000Z","dependencies_parsed_at":"2024-01-13T17:56:30.819Z","dependency_job_id":"1ff5173b-5fba-419d-b453-660ec7024137","html_url":"https://github.com/savonrb/gyoku","commit_stats":{"total_commits":159,"total_committers":25,"mean_commits":6.36,"dds":0.5786163522012578,"last_synced_commit":"fa3dc050e7f6932b70ff84446b4aa853ebae6568"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/savonrb%2Fgyoku","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/savonrb%2Fgyoku/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/savonrb%2Fgyoku/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/savonrb%2Fgyoku/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/savonrb","download_url":"https://codeload.github.com/savonrb/gyoku/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252261033,"owners_count":21719959,"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-07-31T13:00:54.733Z","updated_at":"2025-05-15T10:00:49.175Z","avatar_url":"https://github.com/savonrb.png","language":"Ruby","funding_links":[],"categories":["Web Apps, Services \u0026 Interaction"],"sub_categories":["XML Mapping"],"readme":"# Gyoku\n\nGyoku translates Ruby Hashes to XML.\n\n``` ruby\nGyoku.xml(:find_user =\u003e { :id =\u003e 123, \"v1:Key\" =\u003e \"api\" })\n# =\u003e \"\u003cfindUser\u003e\u003cid\u003e123\u003c/id\u003e\u003cv1:Key\u003eapi\u003c/v1:Key\u003e\u003c/findUser\u003e\"\n```\n\n[![Build status](https://github.com/savonrb/gyoku/actions/workflows/ci.yml/badge.svg)](https://github.com/savonrb/gyoku/actions/workflows/ci.yml)\n[![Gem Version](https://badge.fury.io/rb/gyoku.svg)](http://badge.fury.io/rb/gyoku)\n[![Code Climate](https://codeclimate.com/github/savonrb/gyoku.svg)](https://codeclimate.com/github/savonrb/gyoku)\n[![Coverage Status](https://coveralls.io/repos/savonrb/gyoku/badge.svg?branch=master)](https://coveralls.io/r/savonrb/gyoku)\n\n\n## Installation\n\nGyoku is available through [Rubygems](http://rubygems.org/gems/gyoku) and can be installed via:\n\n``` bash\n$ gem install gyoku\n```\n\nor add it to your Gemfile like this:\n\n``` ruby\ngem 'gyoku', '~\u003e 1.0'\n```\n\n\n## Hash keys\n\nHash key Symbols are converted to lowerCamelCase Strings.\n\n``` ruby\nGyoku.xml(:lower_camel_case =\u003e \"key\")\n# =\u003e \"\u003clowerCamelCase\u003ekey\u003c/lowerCamelCase\u003e\"\n```\n\nYou can change the default conversion formula to `:camelcase`, `:upcase` or `:none`.  \nNote that options are passed as a second Hash to the `.xml` method.\n\n``` ruby\nGyoku.xml({ :camel_case =\u003e \"key\" }, { :key_converter =\u003e :camelcase })\n# =\u003e \"\u003cCamelCase\u003ekey\u003c/CamelCase\u003e\"\n```\n\nCustom key converters. You can use a lambda/Proc to provide customer key converters.\nThis is a great way to leverage active support inflections for domain specific acronyms.\n\n``` ruby\n# Use camelize lower which will hook into active support if installed.\nGyoku.xml({ acronym_abc: \"value\" }, key_converter: lambda { |key| key.camelize(:lower) })\n# =\u003e \"\u003cacronymABC\u003evalue\u003c/acronymABC\u003e\"\n\n```\n\nHash key Strings are not converted and may contain namespaces.\n\n``` ruby\nGyoku.xml(\"XML\" =\u003e \"key\")\n# =\u003e \"\u003cXML\u003ekey\u003c/XML\u003e\"\n```\n\n\n## Hash values\n\n* DateTime objects are converted to xs:dateTime Strings\n* Objects responding to :to_datetime (except Strings) are converted to xs:dateTime Strings\n* TrueClass and FalseClass objects are converted to \"true\" and \"false\" Strings\n* NilClass objects are converted to xsi:nil tags\n* These conventions are also applied to the return value of objects responding to :call\n* All other objects are converted to Strings using :to_s\n\n## Array values\n\nArray items are by default wrapped with the containiner tag, which may be unexpected.\n\n``` ruby\n\u003e Gyoku.xml({languages: [{language: 'ruby'},{language: 'java'}]})\n# =\u003e \"\u003clanguages\u003e\u003clanguage\u003eruby\u003c/language\u003e\u003c/languages\u003e\u003clanguages\u003e\u003clanguage\u003ejava\u003c/language\u003e\u003c/languages\u003e\"\n```\n\nYou can set the `unwrap` option to remove this behavior.\n\n``` ruby\n\u003e Gyoku.xml({languages: [{language: 'ruby'},{language: 'java'}]}, { unwrap: true})\n# =\u003e \"\u003clanguages\u003e\u003clanguage\u003eruby\u003c/language\u003e\u003clanguage\u003ejava\u003c/language\u003e\u003c/languages\u003e\"\n```\n\n## Special characters\n\nGyoku escapes special characters unless the Hash key ends with an exclamation mark.\n\n``` ruby\nGyoku.xml(:escaped =\u003e \"\u003ctag /\u003e\", :not_escaped! =\u003e \"\u003ctag /\u003e\")\n# =\u003e \"\u003cescaped\u003e\u0026lt;tag /\u0026gt;\u003c/escaped\u003e\u003cnotEscaped\u003e\u003ctag /\u003e\u003c/notEscaped\u003e\"\n```\n\n\n## Self-closing tags\n\nHash Keys ending with a forward slash create self-closing tags.\n\n``` ruby\nGyoku.xml(:\"self_closing/\" =\u003e \"\", \"selfClosing/\" =\u003e nil)\n# =\u003e \"\u003cselfClosing/\u003e\u003cselfClosing/\u003e\"\n```\n\n\n## Sort XML tags\n\nIn case you need the XML tags to be in a specific order, you can specify the order  \nthrough an additional Array stored under the `:order!` key.\n\n``` ruby\nGyoku.xml(:name =\u003e \"Eve\", :id =\u003e 1, :order! =\u003e [:id, :name])\n# =\u003e \"\u003cid\u003e1\u003c/id\u003e\u003cname\u003eEve\u003c/name\u003e\"\n```\n\n\n## XML attributes\n\nAdding XML attributes is rather ugly, but it can be done by specifying an additional  \nHash stored under the`:attributes!` key.\n\n``` ruby\nGyoku.xml(:person =\u003e \"Eve\", :attributes! =\u003e { :person =\u003e { :id =\u003e 1 } })\n# =\u003e \"\u003cperson id=\\\"1\\\"\u003eEve\u003c/person\u003e\"\n```\n\n## Explicit XML Attributes\n\nIn addition to using the `:attributes!` key, you may also specify attributes through keys beginning with an \"@\" sign.\nSince you'll need to set the attribute within the hash containing the node's contents, a `:content!` key can be used\nto explicity set the content of the node. The `:content!` value may be a String, Hash, or Array.\n\nThis is particularly useful for self-closing tags.\n\n**Using :attributes!**\n\n``` ruby\nGyoku.xml(\n  \"foo/\" =\u003e \"\", \n  :attributes! =\u003e {\n    \"foo/\" =\u003e {\n      \"bar\" =\u003e \"1\", \n      \"biz\" =\u003e \"2\", \n      \"baz\" =\u003e \"3\"\n    }\n  }\n)\n# =\u003e \"\u003cfoo baz=\\\"3\\\" bar=\\\"1\\\" biz=\\\"2\\\"/\u003e\"\n```\n\n**Using \"@\" keys and \":content!\"**\n\n``` ruby\nGyoku.xml(\n  \"foo/\" =\u003e {\n    :@bar =\u003e \"1\",\n    :@biz =\u003e \"2\",\n    :@baz =\u003e \"3\",\n    :content! =\u003e \"\"\n  })\n# =\u003e \"\u003cfoo baz=\\\"3\\\" bar=\\\"1\\\" biz=\\\"2\\\"/\u003e\"\n```\n\n**Example using \"@\" to get Array of parent tags each with @attributes \u0026 :content!**\n\n``` ruby\nGyoku.xml(\n  \"foo\" =\u003e [\n    {:@name =\u003e \"bar\", :content! =\u003e 'gyoku'}\n    {:@name =\u003e \"baz\", :@some =\u003e \"attr\", :content! =\u003e 'rocks!'}\n  ])\n# =\u003e \"\u003cfoo name=\\\"bar\\\"\u003egyoku\u003c/foo\u003e\u003cfoo name=\\\"baz\\\" some=\\\"attr\\\"\u003erocks!\u003c/foo\u003e\"\n```\n\nUnwrapping Arrays. You can specify an optional `unwrap` argument to modify the default Array\nbehavior. `unwrap` accepts a boolean flag (false by default) or an Array whitelist of keys to unwrap.\n``` ruby\n# Default Array behavior\nGyoku.xml({\n  \"foo\" =\u003e [\n    {:is =\u003e 'great' },\n    {:is =\u003e 'awesome'}\n  ]\n})\n# =\u003e \"\u003cfoo\u003e\u003cis\u003egreat\u003c/is\u003e\u003c/foo\u003e\u003cfoo\u003e\u003cis\u003eawesome\u003c/is\u003e\u003c/foo\u003e\"\n\n# Unwrap Array behavior\nGyoku.xml({\n  \"foo\" =\u003e [\n    {:is =\u003e 'great' },\n    {:is =\u003e 'awesome'}\n  ]\n}, unwrap: true)\n# =\u003e \"\u003cfoo\u003e\u003cis\u003egreat\u003c/is\u003e\u003cis\u003eawesome\u003c/is\u003e\u003c/foo\u003e\"\n\n# Unwrap Array, whitelist.\n# foo is not unwrapped, bar is.\nGyoku.xml({\n  \"foo\" =\u003e [\n    {:is =\u003e 'great' },\n    {:is =\u003e 'awesome'}\n  ],\n  \"bar\" =\u003e [\n      {:is =\u003e 'rad' },\n      {:is =\u003e 'cool'}\n  ]\n}, unwrap: [:bar])\n# =\u003e \"\u003cfoo\u003e\u003cis\u003egreat\u003c/is\u003e\u003c/foo\u003e\u003cfoo\u003e\u003cis\u003eawesome\u003c/is\u003e\u003c/foo\u003e\u003cbar\u003e\u003cis\u003erad\u003c/is\u003e\u003cis\u003ecool\u003c/is\u003e\u003c/bar\u003e\"\n```\n\nNaturally, it would ignore :content! if tag is self-closing:\n\n``` ruby\nGyoku.xml(\n  \"foo/\" =\u003e [\n    {:@name =\u003e \"bar\", :content! =\u003e 'gyoku'}\n    {:@name =\u003e \"baz\", :@some =\u003e \"attr\", :content! =\u003e 'rocks!'}\n  ])\n# =\u003e \"\u003cfoo name=\\\"bar\\\"/\u003e\u003cfoo name=\\\"baz\\\" some=\\\"attr\\\"/\u003e\"\n```\n\nThis seems a bit more explicit with the attributes rather than having to maintain a hash of attributes.\n\nFor backward compatibility, `:attributes!` will still work. However, \"@\" keys will override `:attributes!` keys\nif there is a conflict.\n\n``` ruby\nGyoku.xml(:person =\u003e {:content! =\u003e \"Adam\", :@id! =\u003e 0})\n# =\u003e \"\u003cperson id=\\\"0\\\"\u003eAdam\u003c/person\u003e\"\n```\n\n**Example with \":content!\", :attributes! and \"@\" keys**\n\n``` ruby\nGyoku.xml({ \n  :subtitle =\u003e { \n    :@lang =\u003e \"en\", \n    :content! =\u003e \"It's Godzilla!\" \n  }, \n  :attributes! =\u003e { :subtitle =\u003e { \"lang\" =\u003e \"jp\" } } \n}\n# =\u003e \"\u003csubtitle lang=\\\"en\\\"\u003eIt's Godzilla!\u003c/subtitle\u003e\"\n```\n\nThe example above shows an example of how you can use all three at the same time. \n\nNotice that we have the attribute \"lang\" defined twice.\nThe `@lang` value takes precedence over the `:attribute![:subtitle][\"lang\"]` value.\n\n## Pretty Print\n\nYou can prettify the output XML to make it more readable. Use these options:\n* `pretty_print` – controls pretty mode (default: `false`)\n* `indent` – specifies indentation in spaces (default: `2`)\n* `compact` – controls compact mode (default: `true`)\n\n**This feature is not available for XML documents generated from arrays with unwrap option set to false as such documents are not valid**\n\n**Examples**\n\n``` ruby\nputs Gyoku.xml({user: { name: 'John', job: { title: 'Programmer' }, :@status =\u003e 'active' }}, pretty_print: true)\n#\u003cuser status='active'\u003e\n#  \u003cname\u003eJohn\u003c/name\u003e\n#  \u003cjob\u003e\n#    \u003ctitle\u003eProgrammer\u003c/title\u003e\n#  \u003c/job\u003e\n#\u003c/user\u003e\n```\n\n``` ruby\nputs Gyoku.xml({user: { name: 'John', job: { title: 'Programmer' }, :@status =\u003e 'active' }}, pretty_print: true, indent: 4)\n#\u003cuser status='active'\u003e\n#    \u003cname\u003eJohn\u003c/name\u003e\n#    \u003cjob\u003e\n#        \u003ctitle\u003eProgrammer\u003c/title\u003e\n#    \u003c/job\u003e\n#\u003c/user\u003e\n```\n\n``` ruby\nputs Gyoku.xml({user: { name: 'John', job: { title: 'Programmer' }, :@status =\u003e 'active' }}, pretty_print: true, compact: false)\n#\u003cuser status='active'\u003e\n#  \u003cname\u003e\n#    John\n#  \u003c/name\u003e\n#  \u003cjob\u003e\n#    \u003ctitle\u003e\n#      Programmer\n#    \u003c/title\u003e\n#  \u003c/job\u003e\n#\u003c/user\u003e\n```\n\n**Generate XML from an array with `unwrap` option set to `true`**\n``` ruby\nputs Gyoku::Array.to_xml([\"john\", \"jane\"], \"user\", true, {}, pretty_print: true, unwrap: true)\n#\u003cuser\u003e\n#  \u003cuser\u003ejohn\u003c/user\u003e\n#  \u003cuser\u003ejane\u003c/user\u003e\n#\u003c/user\u003e\n```\n\n**Generate XML from an array with `unwrap` option unset (`false` by default)**\n``` ruby\nputs Gyoku::Array.to_xml([\"john\", \"jane\"], \"user\", true, {}, pretty_print: true)\n#\u003cuser\u003ejohn\u003c/user\u003e\u003cuser\u003ejane\u003c/user\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsavonrb%2Fgyoku","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsavonrb%2Fgyoku","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsavonrb%2Fgyoku/lists"}