{"id":17723546,"url":"https://github.com/doriantaylor/rb-uuid-ncname","last_synced_at":"2026-03-09T11:32:45.193Z","repository":{"id":59158900,"uuid":"132668543","full_name":"doriantaylor/rb-uuid-ncname","owner":"doriantaylor","description":"Turn UUIDs into NCNames and back.","archived":false,"fork":false,"pushed_at":"2024-02-17T20:28:16.000Z","size":199,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-06T05:09:36.248Z","etag":null,"topics":["identifier","identifier-mapping","ncname","uuid"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/doriantaylor.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":"2018-05-08T21:44:41.000Z","updated_at":"2021-11-25T13:25:10.000Z","dependencies_parsed_at":"2024-10-26T02:26:35.628Z","dependency_job_id":null,"html_url":"https://github.com/doriantaylor/rb-uuid-ncname","commit_stats":{"total_commits":25,"total_committers":1,"mean_commits":25.0,"dds":0.0,"last_synced_commit":"5b5314927381b94670b5f2db6d54aea35ed1705e"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/doriantaylor/rb-uuid-ncname","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doriantaylor%2Frb-uuid-ncname","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doriantaylor%2Frb-uuid-ncname/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doriantaylor%2Frb-uuid-ncname/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doriantaylor%2Frb-uuid-ncname/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/doriantaylor","download_url":"https://codeload.github.com/doriantaylor/rb-uuid-ncname/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doriantaylor%2Frb-uuid-ncname/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30292441,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T11:12:22.024Z","status":"ssl_error","status_checked_at":"2026-03-09T11:10:54.577Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["identifier","identifier-mapping","ncname","uuid"],"created_at":"2024-10-25T15:43:20.081Z","updated_at":"2026-03-09T11:32:45.175Z","avatar_url":"https://github.com/doriantaylor.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# UUID::NCName: Turn UUIDs into NCNames (and back)\n\n```ruby\nrequire 'uuid-ncname'\nrequire 'uuidtools'\n\nuu = UUIDTools::UUID.random_create\n# =\u003e #\u003cUUID:0x3fff0e597ef8 UUID:df521e0a-9d57-4f04-9a95-fc2888decc5a\u003e\n\nnc64 = UUID::NCName.to_ncname uu\n# =\u003e \"E31IeCp1X8EqV_CiI3sxaJ\"\n\nnc32 = UUID::NCName.to_ncname_32 uu\n# =\u003e \"E35jb4cu5k7yevfp4fcen5tc2j\"\n\norig = UUID::NCName.from_ncname nc64\n# =\u003e \"df521e0a-9d57-4f04-9a95-fc2888decc5a\"\n\norig == UUID::NCName.from_ncname nc32 # =\u003e true\norig == uu.to_s                       # =\u003e true\n\n# then you can turn it back into an object or whatever\nuu == UUIDTools::UUID.parse(orig)     # =\u003e true\n```\n\n## Description\n\nThe purpose of this module is to [devise an alternative\nrepresentation](https://datatracker.ietf.org/doc/html/draft-taylor-uuid-ncname)\nof the [UUID](http://tools.ietf.org/html/rfc4122) which conforms to\nthe constraints of various other identifiers such as NCName, and\ncreate an [isomorphic](http://en.wikipedia.org/wiki/Isomorphism)\nmapping between them.\n\n## Rationale \u0026 Method\n\nThe UUID is a generic identifier which is large enough to be globally\nunique. This makes it useful as a canonical name for data objects in\ndistributed systems, especially those that cross administrative\njurisdictions, such as the World-Wide Web. The\n[representation](http://tools.ietf.org/html/rfc4122#section-3),\nhowever, of the UUID, precludes it from being used in many places\nwhere it would be useful to do so.\n\nIn particular, there are grammars for many types of identifiers which\nmust not begin with a digit. Others are case-insensitive, or\nprohibited from containing hyphens (present in both the standard\nnotation and Base64URL), or indeed anything outside of\n`^[A-Za-z_][0-9A-Za-z_]*$`.\n\nThe hexadecimal notation of the UUID has a 5/8 chance of beginning\nwith a digit, Base64 has a 5/32 chance, and Base32 has a 3/16\nchance. As such, the identifier must be modified in such a way as to\nguarantee beginning with an alphabetic letter (or underscore `_`, but\nsome grammars even prohibit that, so we omit it as well).\n\nWhile it is conceivable to simply add a padding character, there are a\nfew considerations which make it more appealing to derive the initial\ncharacter from the content of the UUID itself:\n\n* UUIDs are large (128-bit) identifiers as it is, and it is\n  undesirable to add meaningless syntax to them if we can avoid doing\n  so.\n\n* 128 bits is an inconvenient number for aligning to both Base32 (130)\n  and Base64 (132), though 120 divides cleanly into 5, 6 and 8.\n\n* The 13th quartet, or higher four bits of the\n  `time_hi_and_version_field` of the UUID is constant, as it indicates\n  the UUID's version. If we encode this value using the scheme common\n  to both Base64 and Base32, we get values between `A` and `P`, with\n  the valid subset between `B` and `F`.\n\n**Therefore:** extract the UUID's version quartet, shift all\nsubsequent data 4 bits to the left, zero-pad to the octet, encode with\neither _base64url_ or _base32_, truncate, and finally prepend the\nencoded version character. Voilà, one token-safe UUID.\n\n## Applications\n\n### XML IDs\n\nThe `ID` production appears to have been constricted, inadvertently or\notherwise, from [Name](http://www.w3.org/TR/xml11/#NT-Name) in both\nthe XML 1.0 and 1.1 specifications,\nto [NCName](http://www.w3.org/TR/xml-names/#NT-NCName)\nby [XML Schema Part 2](http://www.w3.org/TR/xmlschema-2/#ID). This\nremoves the colon character `:` from the grammar. The net effect is\nthat\n\n    \u003cfoo id=\"urn:uuid:b07caf81-baae-449d-8a2e-48c0f5fa5538\"/\u003e\n\nwhile being a _well-formed_ ID _and_ valid under DTD validation, is\n_not_ valid per XML Schema Part 2 or anything that uses it (e.g. Relax\nNG).\n\n### RDF blank node identifiers\n\nBlank node identifiers in RDF are intended for serialization, to act\nas a handle so that multiple RDF statements can refer to the same\nblank\nnode. The\n[RDF abstract syntax specifies](http://www.w3.org/TR/rdf-concepts/#section-URI-Vocabulary) that\nthe validity constraints of blank node identifiers be delegated to the\nconcrete syntax\nspecifications. The\n[RDF/XML syntax specification](http://www.w3.org/TR/rdf-syntax-grammar/#rdf-id) lists\nthe blank node identifier as NCName. However, according\nto [the Turtle spec](http://www.w3.org/TR/turtle/#BNodes), this is a\nvalid blank node identifier:\n\n    _:42df00ec-30a2-431f-be9e-e3a612b325db\n\ndespite\n[an older version](http://www.w3.org/TeamSubmission/turtle/#nodeID)\nlisting a production equivalent to the more conservative\nNCName. NTriples syntax is\n[even more constrained](http://www.w3.org/TR/rdf-testcases/#ntriples),\ngiven as `^[A-Za-z][0-9A-Za-z]*$`.\n\n### Generated symbols\n\n\u003e There are only two hard things in computer science: cache\n\u003e invalidation and naming things [and off-by-one errors].\n\u003e\n\u003e -- Phil Karlton [extension of unknown origin]\n\nSuppose you wanted to create a [literate\nprogramming](http://en.wikipedia.org/wiki/Literate_programming) system\n(I do). One of your (my) stipulations is that the symbols get defined\nin the *prose*, rather than the _code_. However, you (I) still want\nto be able to validate the code's syntax, and potentially even run the\ncode, without having to commit to naming anything. You are (I am) also\ninterested in creating a global map of classes, datatypes and code\nfragments, which can be operated on and tested in isolation, ported to\nother languages, or transplanted into the more conventional packages\nof programs, libraries and frameworks. The Base32 UUID NCName\nrepresentation should be adequate for placeholder symbols in just\nabout any programming language, save for those which do not permit\nidentifiers as long as 26 characters (which are extremely scarce).\n\n## _FORMAT DEPRECATION NOTICE_\n\nAfter careful consideration, I have decided to change the UUID-NCName\nformat in a minor yet incompatible way. In particular, I have moved\nthe nybble containing\nthe [`variant`](https://tools.ietf.org/html/rfc4122#section-4.1.1) to\nthe very end of the identifier, whereas it previously was mixed into\nthe middle somewhere.\n\nThis can be considered an application\nof [Postel's Law](https://en.wikipedia.org/wiki/Postel%27s_law), based\non the assumption that these identifiers will be generated through\nother methods, and potentially naïvely. Like the `version` field, the\n`variant` field has a limited acceptable range of values. If, for\nexample, one were to attempt to generate a conforming identifier by\nsimply generating a random Base32 or Base64 string, it will be\ndifficult to ensure that the `variant` field will indeed conform when\nthe identifier is converted to a standard UUID. By moving the\n`variant` field out to the end of the identifier, everything between\nthe `version` and `variant` bookends can be generated randomly without\nany further consideration, like so:\n\n```ruby\nB64_ALPHA = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + %w(- _)\n\ndef make_cheapo_b64_uuid_ncname\n  vals = (1..20).map { rand 64 }               # generate the content\n  vals.push(rand(4) + 8)                       # last digit is special\n  'E' + vals.map { |v| B64_ALPHA[v] }.join('') # 'E' for UUID v4\nend\n\n# voilà:\n\ncheap = make_cheapo_b64_uuid_ncname\n# =\u003e \"EXSVv8ezPbSKWoKOkBNWKL\"\n\n# now try changing it to a standard UUID:\n\nUUID::NCName.from_ncname cheap, version: 1\n# =\u003e \"5d256ff1-eccf-46d2-b296-a0a3a404d58a\"\n```\n\nFurthermore, since the default behaviour is to align the bits of the\nlast byte to the size of the encoding symbol, and since the `variant`\nbits are masked, a compliant RFC4122 UUID will _always_ end with `I`,\n`J`, `K`, or `L`, in _both_ Base32 (case-insensitive) and Base64\nvariants.\n\nSince I have already released this gem prior to this format change, I\nhave added a `:version` parameter to both `to_ncname` and\n`from_ncname`. This parameter, which controls the compact UUID spec\nbehaviour, defaults to `1`, as of the _module_ version 0.2.4.\n\n\u003e Unless you have to support identifiers generated from version 0.1.3\n\u003e or older, you should be running these methods with `version: 1`.\n\n## Documentation\n\nGenerated and deposited\n[in the usual place](http://www.rubydoc.info/gems/uuid-ncname/).\n\n## Installation\n\nYou know how to do this:\n\n    $ gem install uuid-ncname\n\nOr, [download it off rubygems.org](https://rubygems.org/gems/uuid-ncname).\n\n## Contributing\n\nBug reports and pull requests are welcome at\n[the GitHub repository](https://github.com/doriantaylor/rb-uuid-ncname).\n\n## Copyright \u0026 License\n\n©2018 [Dorian Taylor](https://doriantaylor.com/)\n\nThis software is provided under\nthe [Apache License, 2.0](https://www.apache.org/licenses/LICENSE-2.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoriantaylor%2Frb-uuid-ncname","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoriantaylor%2Frb-uuid-ncname","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoriantaylor%2Frb-uuid-ncname/lists"}