{"id":13878649,"url":"https://github.com/bkuhlmann/versionaire","last_synced_at":"2025-04-12T18:49:01.713Z","repository":{"id":45695550,"uuid":"54278637","full_name":"bkuhlmann/versionaire","owner":"bkuhlmann","description":"An immutable, thread-safe, and strict semantic version type.","archived":false,"fork":false,"pushed_at":"2025-03-25T02:13:19.000Z","size":653,"stargazers_count":95,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-03T21:13:44.904Z","etag":null,"topics":["version-control","versioning"],"latest_commit_sha":null,"homepage":"https://alchemists.io/projects/versionaire","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bkuhlmann.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.adoc","code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["bkuhlmann"]}},"created_at":"2016-03-19T17:34:08.000Z","updated_at":"2025-03-25T02:13:23.000Z","dependencies_parsed_at":"2023-02-12T17:25:13.554Z","dependency_job_id":"ec9b8191-6962-41b6-b13f-3b5297f38952","html_url":"https://github.com/bkuhlmann/versionaire","commit_stats":{"total_commits":572,"total_committers":1,"mean_commits":572.0,"dds":0.0,"last_synced_commit":"885134ddfe012f418cf10ea391e87b4597b5ce12"},"previous_names":[],"tags_count":91,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bkuhlmann%2Fversionaire","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bkuhlmann%2Fversionaire/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bkuhlmann%2Fversionaire/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bkuhlmann%2Fversionaire/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bkuhlmann","download_url":"https://codeload.github.com/bkuhlmann/versionaire/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248618218,"owners_count":21134199,"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":["version-control","versioning"],"created_at":"2024-08-06T08:01:55.655Z","updated_at":"2025-04-12T18:49:01.686Z","avatar_url":"https://github.com/bkuhlmann.png","language":"Ruby","funding_links":["https://github.com/sponsors/bkuhlmann"],"categories":["Ruby"],"sub_categories":[],"readme":":toc: macro\n:toclevels: 5\n:figure-caption!:\n\n:option_parser_link: link:https://alchemists.io/articles/ruby_option_parser[OptionParser]\n:semver_link: link:https://semver.org[Semantic Versioning]\n:strict_semver_link: link:https://alchemists.io/articles/strict_semantic_versioning[Strict Semantic Versioning]\n\n= Versionaire\n\nRuby doesn't provide a primitive version type by default so Versionaire fills this gap by providing immutable and thread-safe {strict_semver_link} so you can leverage versions within your applications. This new `Version` type behaves and feels a lot like other primitives (i.e. `String`, `Array`, `Hash`, `Proc`, etc) and can be cast/converted from other primitives.\n\ntoc::[]\n\n== Features\n\n* Provides {strict_semver_link} which means `\u003cmajor\u003e.\u003cminor\u003e.\u003cpatch\u003e`.\n* Provides immutable, thread-safe version instances.\n* Converts (casts) from a `String`, `Array`, `Hash`, `Proc`, or `Version` to a `Version`.\n* Disallows `\u003cmajor\u003e.\u003cminor\u003e.\u003cpatch\u003e-\u003cpre-release\u003e` usage even though {semver_link} suggests you _may_ use pre-release information.\n* Disallows `\u003cmajor\u003e.\u003cminor\u003e.\u003cpatch\u003e+\u003cbuild_metadata\u003e` usage even though {semver_link} suggests you _may_ use build metadata.\n\n== Requirements\n\n. https://www.ruby-lang.org[Ruby].\n\n== Setup\n\nTo install _with_ security, run:\n\n[source,bash]\n----\n# 💡 Skip this line if you already have the public certificate installed.\ngem cert --add \u003c(curl --compressed --location https://alchemists.io/gems.pem)\ngem install versionaire --trust-policy HighSecurity\n----\n\nTo install _without_ security, run:\n\n[source,bash]\n----\ngem install versionaire\n----\n\nYou can also add the gem directly to your project:\n\n[source,bash]\n----\nbundle add versionaire\n----\n\nOnce the gem is installed, you only need to require it:\n\n[source,ruby]\n----\nrequire \"versionaire\"\n----\n\n== Usage\n\n=== Initialization\n\nA new version can be initialized in a variety of ways:\n\n[source,ruby]\n----\nVersionaire::Version.new                            # \"0.0.0\"\nVersionaire::Version[major: 1]                      # \"1.0.0\"\nVersionaire::Version[major: 1, minor: 2]            # \"1.2.0\"\nVersionaire::Version[major: 1, minor: 2, patch: 3]  # \"1.2.3\"\n----\n\n=== Equality\n\n==== Value (`+#==+`)\n\nEquality is determined by the state of the object. This means that a version is equal to another version as long as all of the values (i.e. state) are equal to each other. Example:\n\n[source,ruby]\n----\nversion_a = Versionaire::Version[major: 1]\nversion_b = Versionaire::Version[major: 2]\nversion_c = Versionaire::Version[major: 1]\n\nversion_a == version_a  # true\nversion_a == version_b  # false\nversion_a == version_c  # true\n----\n\nKnowing this, versions can be compared against one another too:\n\n[source,ruby]\n----\nversion_a \u003e version_b                    # false\nversion_a \u003c version_b                    # true\nversion_a.between? version_c, version_b  # true\n----\n\n==== Hash (`#eql?`)\n\nBehaves exactly as `#==`.\n\n==== Case (`#===`)\n\nBehaves exactly as `#==`.\n\n==== Identity (`#equal?`)\n\nWorks like any other standard Ruby object where an object is equal only to itself.\n\n[source,ruby]\n----\nversion_a = Versionaire::Version[major: 1]\nversion_b = Versionaire::Version[major: 2]\nversion_c = Versionaire::Version[major: 1]\n\nversion_a.equal? version_a  # true\nversion_a.equal? version_b  # false\nversion_a.equal? version_c  # false\n----\n\n=== Conversions\n\n==== Function\n\nUse the `Versionaire::Version` function to explicitly cast to a version:\n\n[source,ruby]\n----\nversion = Versionaire::Version[major: 1]\n\nVersionaire::Version \"1.0.0\"\nVersionaire::Version [1, 0, 0]\nVersionaire::Version major: 1, minor: 0, patch: 0\nVersionaire::Version version\n----\n\nEach of these conversions will result in a version object that represents \"`1.0.0`\".\n\nWhen attempting to convert an unsupported type, a `Versionaire::Error` exception will be thrown.\n\n==== Refinement\n\nBuilding upon the above examples, a more elegant solution is to use a link:https://alchemists.io/articles/ruby_refinements[refinement]:\n\n[source,ruby]\n----\nusing Versionaire::Cast\n\nversion = Versionaire::Version[major: 1]\n\nVersion \"1.0.0\"\nVersion [1, 0, 0]\nVersion major: 1, minor: 0, patch: 0\nVersion version\n----\n\nBy adding `using Versionaire::Cast` to your implementation, this allows Versionaire to refine\n`Kernel` so you have a top-level `Version` conversion function much like Kernel's native support for\n`Integer`, `String`, `Array`, `Hash`, etc. The benefit to this approach is to reduce the amount of\ntyping so you don't pollute your entire object space, like a monkey patch, while providing an idiomatic approach to casting like any other primitive.\n\n==== Implicit\n\nImplicit conversion to a `String` is supported:\n\n[source,ruby]\n----\n\"1.0.0\".match Versionaire::Version[major: 1]  # \u003cMatchData \"1.0.0\"\u003e\n----\n\n==== Explicit\n\nExplicit conversion to a `String`, `Array`, `Hash`, or `Proc` is supported:\n\n[source,ruby]\n----\nversion = Versionaire::Version.new\n\nversion.to_s     # \"0.0.0\"\nversion.to_a     # [0, 0, 0]\nversion.to_h     # {major: 0, minor: 0, patch: 0}\nversion.to_proc  # #\u003cProc:0x000000010b015b88 (lambda)\u003e\n----\n\nTo elaborate on procs, this means the following is possible where you might want to collect all minor verions values or make use of version information in other useful ways:\n\n[source,ruby]\n----\nusing Versionaire::Cast\n\nversion = Version \"1.2.3\"\n\nversion.to_proc.call :major               # 1\n[version, version, version].map(\u0026:minor)  # [2, 2, 2]\n----\n\n=== Inspections\n\nYou can inspect a version which is the equivalent of an escaped string representation. Example:\n\n[source,ruby]\n----\nusing Versionaire::Cast\n\nVersion(\"1.2.3\").inspect  # \"\\\"1.2.3\\\"\"\n----\n\n=== Comparisons\n\nAll versions are comparable which means any of the operators from the `+Comparable+` module will\nwork. Example:\n\n[source,ruby]\n----\nversion_1 = Versionaire::Version \"1.0.0\"\nversion_2 = Versionaire::Version \"2.0.0\"\n\nversion_1 \u003c version_2                    # true\nversion_1 \u003c= version_2                   # true\nversion_1 == version_2                   # false (see Equality section above for details)\nversion_1 \u003e version_2                    # false\nversion_1 \u003e= version_2                   # false\nversion_1.between? version_1, version_2  # true\nversion_1.clamp version_1, version_2     # version_1 (added in Ruby 2.4.0)\n----\n\n=== Bumping\n\nVersions can be bumped to next logical version with respect current version. Example:\n\n[source,ruby]\n----\nversion = Versionaire::Version.new  # \"0.0.0\"\nversion.bump :patch                 # \"0.0.1\"\nversion.bump :minor                 # \"0.1.0\"\nversion.bump :major                 # \"1.0.0\"\n\nVersionaire::Version[major: 1, minor: 2, patch: 3].bump :major  # \"2.0.0\"\nVersionaire::Version[major: 1, minor: 2, patch: 3].bump :minor  # \"1.3.0\"\nVersionaire::Version[major: 1, minor: 2, patch: 3].bump :patch  # \"1.2.4\"\n----\n\nYou'll notice, when bumping the major or minor versions, lower precision gets zeroed out in order to provide the next logical version.\n\n=== Math\n\nVersions can be added, subtracted, sequentially increased, or sequentially decreased from each\nother.\n\n==== Addition\n\nVersions can be added together to produce a resulting version sum.\n\n[source,ruby]\n----\nversion_1 = Versionaire::Version[major: 1, minor: 2, patch: 3]\nversion_2 = Versionaire::Version[major: 2, minor: 5, patch: 7]\nversion_1 + version_2  # \"3.7.10\"\n----\n\n==== Subtraction\n\nVersions can be substracted from each other as long as there isn't a negative result.\n\n[source,ruby]\n----\nversion_1 = Versionaire::Version[major: 1, minor: 2, patch: 3]\nversion_2 = Versionaire::Version[major: 1, minor: 1, patch: 1]\nversion_1 - version_2  # \"0.1.2\"\n\nversion_1 = Versionaire::Version[major: 1]\nversion_2 = Versionaire::Version[major: 5]\nversion_1 - version_2  # Versionaire::Error\n----\n\n==== Up\n\nVersions can be sequentially increased or given a specific version to jump to.\n\n[source,ruby]\n----\nversion = Versionaire::Version[major: 1, minor: 1, patch: 1]\nversion.up :major     # =\u003e \"2.1.1\"\nversion.up :major, 3  # =\u003e \"4.1.1\"\nversion.up :minor     # =\u003e \"1.2.1\"\nversion.up :minor, 3  # =\u003e \"1.4.1\"\nversion.up :patch     # =\u003e \"1.1.2\"\nversion.up :patch, 3  # =\u003e \"1.1.4\"\n----\n\n==== Down\n\nVersions can be sequentially decreased or given a specific version to jump to as long as the result\nis not negative.\n\n[source,ruby]\n----\nversion = Versionaire::Version[major: 5, minor: 5, patch: 5]\nversion.down :major     # =\u003e \"4.5.5\"\nversion.down :major, 3  # =\u003e \"2.5.5\"\nversion.down :minor     # =\u003e \"5.4.5\"\nversion.down :minor, 3  # =\u003e \"5.2.5\"\nversion.down :patch     # =\u003e \"5.5.4\"\nversion.down :patch, 3  # =\u003e \"5.5.2\"\nversion.down :major, 6  # =\u003e Versionaire::Error\n----\n\n=== Extensions\n\nThis project supports libraries which might desire native `Version` types. Each extension _must be\nexplicitly required_ in order to be used since they are _optional_ by default. See below for\ndetails.\n\n==== OptionParser\n\n{option_parser_link} is one of Ruby's link:https://stdgems.org[default gems] which can accept additional types not native to Ruby by default. To extend `OptionParser` with the `Version` type, all you need to do is add these two lines to your implementation:\n\n. `require \"versionaire/extensions/option_parser\"`: This will load dependencies and register the `Version` type with `OptionParser`.\n. `act.on \"--tag VERSION\", Versionaire::Version`: Specifying `Versionaire::Version` as the second argument will ensure `OptionParser` properly casts command line input as a `Version` type.\n\nHere's an example implementation that demonstrates full usage:\n\n[source,ruby]\n----\nrequire \"versionaire/extensions/option_parser\"\n\noptions = {}\n\nparser = OptionParser.new do |act|\n  act.on \"--tag VERSION\", Versionaire::Version, \"Casts to version.\" do |value|\n    options[:version] = value\n  end\nend\n\nparser.parse %w[--tag 1.2.3]\nputs options\n----\n\nThe above will ensure `--tag 1.2.3` is parsed as `{version: \"1.2.3\"}` within your `options` variable. Should `OptionParser` parse an invalid version, you'll get a `OptionParser::InvalidArgument` instead.\n\n== Development\n\nTo contribute, run:\n\n[source,bash]\n----\ngit clone https://github.com/bkuhlmann/versionaire\ncd versionaire\nbin/setup\n----\n\nYou can also use the IRB console for direct access to all objects:\n\n[source,bash]\n----\nbin/console\n----\n\n== Tests\n\nTo test, run:\n\n[source,bash]\n----\nbin/rake\n----\n\n== link:https://alchemists.io/policies/license[License]\n\n== link:https://alchemists.io/policies/security[Security]\n\n== link:https://alchemists.io/policies/code_of_conduct[Code of Conduct]\n\n== link:https://alchemists.io/policies/contributions[Contributions]\n\n== link:https://alchemists.io/policies/developer_certificate_of_origin[Developer Certificate of Origin]\n\n== link:https://alchemists.io/projects/versionaire/versions[Versions]\n\n== link:https://alchemists.io/community[Community]\n\n== Credits\n\n* Built with link:https://alchemists.io/projects/gemsmith[Gemsmith].\n* Engineered by link:https://alchemists.io/team/brooke_kuhlmann[Brooke Kuhlmann].\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbkuhlmann%2Fversionaire","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbkuhlmann%2Fversionaire","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbkuhlmann%2Fversionaire/lists"}