versionian 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +8 -0
  4. data/CODE_OF_CONDUCT.md +132 -0
  5. data/README.adoc +874 -0
  6. data/Rakefile +12 -0
  7. data/docs/Gemfile +8 -0
  8. data/docs/_guides/custom-schemes.adoc +110 -0
  9. data/docs/_guides/index.adoc +12 -0
  10. data/docs/_pages/component-types.adoc +151 -0
  11. data/docs/_pages/declarative-schemes.adoc +260 -0
  12. data/docs/_pages/index.adoc +15 -0
  13. data/docs/_pages/range-matching.adoc +102 -0
  14. data/docs/_pages/schemes.adoc +68 -0
  15. data/docs/_references/api.adoc +251 -0
  16. data/docs/_references/index.adoc +13 -0
  17. data/docs/_references/schemes.adoc +410 -0
  18. data/docs/_tutorials/getting-started.adoc +119 -0
  19. data/docs/_tutorials/index.adoc +11 -0
  20. data/docs/index.adoc +287 -0
  21. data/lib/versionian/component_definition.rb +71 -0
  22. data/lib/versionian/component_types/base.rb +19 -0
  23. data/lib/versionian/component_types/date_part.rb +41 -0
  24. data/lib/versionian/component_types/enum.rb +32 -0
  25. data/lib/versionian/component_types/float.rb +23 -0
  26. data/lib/versionian/component_types/hash.rb +21 -0
  27. data/lib/versionian/component_types/integer.rb +23 -0
  28. data/lib/versionian/component_types/postfix.rb +51 -0
  29. data/lib/versionian/component_types/prerelease.rb +70 -0
  30. data/lib/versionian/component_types/registry.rb +35 -0
  31. data/lib/versionian/component_types/string.rb +21 -0
  32. data/lib/versionian/errors/invalid_scheme_error.rb +7 -0
  33. data/lib/versionian/errors/invalid_version_error.rb +7 -0
  34. data/lib/versionian/errors/parse_error.rb +7 -0
  35. data/lib/versionian/parsers/declarative.rb +214 -0
  36. data/lib/versionian/scheme_loader.rb +102 -0
  37. data/lib/versionian/scheme_registry.rb +34 -0
  38. data/lib/versionian/schemes/calver.rb +94 -0
  39. data/lib/versionian/schemes/composite.rb +82 -0
  40. data/lib/versionian/schemes/declarative.rb +138 -0
  41. data/lib/versionian/schemes/pattern.rb +236 -0
  42. data/lib/versionian/schemes/semantic.rb +136 -0
  43. data/lib/versionian/schemes/solover.rb +121 -0
  44. data/lib/versionian/schemes/wendtver.rb +141 -0
  45. data/lib/versionian/version.rb +6 -0
  46. data/lib/versionian/version_component.rb +28 -0
  47. data/lib/versionian/version_identifier.rb +121 -0
  48. data/lib/versionian/version_range.rb +61 -0
  49. data/lib/versionian/version_scheme.rb +68 -0
  50. data/lib/versionian.rb +64 -0
  51. data/lib/versius.rb +5 -0
  52. data/sig/versius.rbs +4 -0
  53. metadata +157 -0
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Versionian
4
+ class VersionIdentifier
5
+ include Comparable
6
+
7
+ attr_reader :raw_string, :scheme, :components, :comparable_array
8
+
9
+ def initialize(raw_string:, scheme:, components:, comparable_array:)
10
+ @raw_string = raw_string
11
+ @scheme = scheme
12
+ @components = components.freeze
13
+ @comparable_array = comparable_array.freeze
14
+ freeze
15
+ end
16
+
17
+ def <=>(other)
18
+ raise ArgumentError, "Cannot compare versions from different schemes" unless @scheme == other.scheme
19
+
20
+ @scheme.compare_arrays(@comparable_array, other.comparable_array)
21
+ end
22
+
23
+ def component(name)
24
+ @components.find { |c| c.name == name }
25
+ end
26
+
27
+ def matches_range?(range)
28
+ @scheme.matches_range?(@raw_string, range)
29
+ end
30
+
31
+ def to_s
32
+ @scheme.render(self)
33
+ end
34
+
35
+ def inspect
36
+ "#<Versionian::VersionIdentifier #{@raw_string} scheme=#{@scheme.name}>"
37
+ end
38
+
39
+ # Build a version programmatically from component values
40
+ #
41
+ # @param scheme [VersionScheme] The scheme to use for this version
42
+ # @param components [Hash] Hash of component_name => value pairs
43
+ # @return [VersionIdentifier] A new version identifier object
44
+ #
45
+ # @example
46
+ # scheme = Versionian.get_scheme(:semantic)
47
+ # version = Versionian::VersionIdentifier.build(
48
+ # scheme: scheme,
49
+ # components: { major: 1, minor: 2, patch: 3 }
50
+ # )
51
+ def self.build(scheme:, components:)
52
+ # Build raw_string from components using format_template if available
53
+ raw_string = if scheme.format_template
54
+ render_from_template(scheme.format_template, scheme.component_definitions, components)
55
+ else
56
+ # Fallback: join components with dots
57
+ components.map { |name, value| value.to_s }.join(".")
58
+ end
59
+
60
+ # Build component objects
61
+ component_objects = components.map do |name, value|
62
+ comp_def = scheme.component_definitions.find { |cd| cd.name == name.to_sym }
63
+
64
+ VersionComponent.new(
65
+ name: name.to_sym,
66
+ type: comp_def&.type || :string,
67
+ value: value,
68
+ weight: comp_def&.weight || 1,
69
+ values: comp_def&.values || [],
70
+ order: comp_def&.order || [],
71
+ definition: comp_def
72
+ )
73
+ end
74
+
75
+ # Build comparable_array
76
+ # Special handling for Semantic scheme: use Gem::Version for comparison
77
+ comparable_array = if scheme.is_a?(Schemes::Semantic)
78
+ require "rubygems/version"
79
+ version_str = components.map { |name, value| value.to_s }.join(".")
80
+ [::Gem::Version.new(version_str)]
81
+ else
82
+ component_objects.map do |comp|
83
+ type = ComponentTypes.resolve(comp.type)
84
+ type.to_comparable(comp.value, comp)
85
+ end
86
+ end
87
+
88
+ new(
89
+ raw_string: raw_string,
90
+ scheme: scheme,
91
+ components: component_objects,
92
+ comparable_array: comparable_array
93
+ )
94
+ end
95
+
96
+ private
97
+
98
+ def self.render_from_template(format_template, component_definitions, components)
99
+ result = format_template.dup
100
+
101
+ # Process optional segments []
102
+ loop do
103
+ match = result.match(/\[([^\[\]]+)\]/)
104
+ break unless match
105
+
106
+ segment_content = match[1]
107
+ has_value = segment_content.scan(/\{(\w+)\}/).any? { |name| components.key?(name.first.to_sym) }
108
+
109
+ result = result.sub(match[0], has_value ? segment_content : "")
110
+ end
111
+
112
+ # Replace component placeholders
113
+ components.each do |name, value|
114
+ placeholder = "{#{name}}"
115
+ result = result.gsub(placeholder, value.to_s)
116
+ end
117
+
118
+ result
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Versionian
4
+ class VersionRange
5
+ attr_reader :type, :scheme
6
+
7
+ def initialize(type, scheme, **boundaries)
8
+ @type = type
9
+ @scheme = scheme
10
+ @boundaries = boundaries.freeze
11
+ validate!
12
+ freeze
13
+ end
14
+
15
+ def boundary
16
+ @boundaries[:version]
17
+ end
18
+
19
+ def from
20
+ @boundaries[:from]
21
+ end
22
+
23
+ def to
24
+ @boundaries[:to]
25
+ end
26
+
27
+ def matches?(version_string)
28
+ @scheme.matches_range?(version_string, self)
29
+ end
30
+
31
+ def includes?(version)
32
+ if version.is_a?(Version)
33
+ matches?(version.raw_string)
34
+ else
35
+ matches?(version)
36
+ end
37
+ end
38
+
39
+ def to_s
40
+ case @type
41
+ when :equals then "== #{@boundaries[:version]}"
42
+ when :before then "< #{@boundaries[:version]}"
43
+ when :after then ">= #{@boundaries[:version]}"
44
+ when :between then "#{@boundaries[:from]} - #{@boundaries[:to]}"
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def validate!
51
+ case @type
52
+ when :equals, :before, :after
53
+ raise ArgumentError, "#{@type} range requires :version" unless @boundaries[:version]
54
+ when :between
55
+ raise ArgumentError, "between range requires :from and :to" unless @boundaries[:from] && @boundaries[:to]
56
+ else
57
+ raise ArgumentError, "Unknown range type: #{@type}"
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Versionian
4
+ class VersionScheme
5
+ attr_reader :name, :description, :format_template, :component_definitions
6
+
7
+ def initialize(name:, description: nil, format_template: nil, component_definitions: [])
8
+ @name = name
9
+ @description = description
10
+ @format_template = format_template
11
+ @component_definitions = component_definitions
12
+ end
13
+
14
+ # Abstract methods
15
+ def parse(version_string)
16
+ raise NotImplementedError, "#{self.class} must implement #parse"
17
+ end
18
+
19
+ def render(version)
20
+ version.raw_string
21
+ end
22
+
23
+ # Default comparison (can be overridden)
24
+ def compare_arrays(a, b)
25
+ a <=> b
26
+ end
27
+
28
+ def compare(a, b)
29
+ version_a = parse(a)
30
+ version_b = parse(b)
31
+ version_a <=> version_b
32
+ end
33
+
34
+ def matches_range?(version_string, range)
35
+ version = parse(version_string)
36
+ version.matches_range?(range)
37
+ end
38
+
39
+ def valid?(version_string)
40
+ parse(version_string)
41
+ true
42
+ rescue Errors::InvalidVersionError, Errors::ParseError
43
+ false
44
+ end
45
+
46
+ # Alias for valid? - check if this scheme supports a version string
47
+ def supports?(version_string)
48
+ valid?(version_string)
49
+ end
50
+
51
+ # Build a version programmatically from component values
52
+ #
53
+ # @param components [Hash] Hash of component_name => value pairs
54
+ # @return [VersionIdentifier] A new version identifier object
55
+ #
56
+ # @example
57
+ # scheme.build(major: 1, minor: 2, patch: 3)
58
+ def build(**components)
59
+ VersionIdentifier.build(scheme: self, components: components)
60
+ end
61
+
62
+ def ==(other)
63
+ return false unless other.is_a?(VersionScheme)
64
+
65
+ @name == other.name
66
+ end
67
+ end
68
+ end
data/lib/versionian.rb ADDED
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "versionian/version"
4
+
5
+ # Require error classes (they have interdependent module definitions)
6
+ require_relative "versionian/errors/invalid_version_error"
7
+ require_relative "versionian/errors/invalid_scheme_error"
8
+ require_relative "versionian/errors/parse_error"
9
+
10
+ # Autoload models at the Versionian module level
11
+ module Versionian
12
+ # Autoload models
13
+ autoload :VersionComponent, "versionian/version_component"
14
+ autoload :VersionIdentifier, "versionian/version_identifier"
15
+ autoload :VersionScheme, "versionian/version_scheme"
16
+ autoload :VersionRange, "versionian/version_range"
17
+ autoload :ComponentDefinition, "versionian/component_definition"
18
+
19
+ # Autoload component types module
20
+ autoload :ComponentTypes, "versionian/component_types/registry"
21
+ autoload :Parsers, "versionian/parsers/declarative"
22
+
23
+ # Autoload schemes module
24
+ autoload :Schemes, "versionian/schemes/semantic"
25
+
26
+ # Autoload utilities
27
+ autoload :SchemeRegistry, "versionian/scheme_registry"
28
+ autoload :SchemeLoader, "versionian/scheme_loader"
29
+ autoload :PrimitiveLibrary, "versionian/primitive_library"
30
+
31
+ # Register built-in component types
32
+ ComponentTypes.register(:integer, ComponentTypes::Integer)
33
+ ComponentTypes.register(:float, ComponentTypes::Float)
34
+ ComponentTypes.register(:enum, ComponentTypes::Enum)
35
+ ComponentTypes.register(:string, ComponentTypes::String)
36
+ ComponentTypes.register(:date_part, ComponentTypes::DatePart)
37
+ ComponentTypes.register(:prerelease, ComponentTypes::Prerelease)
38
+ ComponentTypes.register(:postfix, ComponentTypes::Postfix)
39
+ ComponentTypes.register(:hash, ComponentTypes::Hash)
40
+
41
+ class << self
42
+ def scheme_registry
43
+ @scheme_registry ||= SchemeRegistry.instance.tap do |registry|
44
+ # Register built-in schemes
45
+ registry.register(:semantic, Schemes::Semantic.new)
46
+ registry.register(:calver, Schemes::CalVer.new)
47
+ registry.register(:solover, Schemes::SoloVer.new)
48
+ registry.register(:wendtver, Schemes::WendtVer.new)
49
+ end
50
+ end
51
+
52
+ def register_scheme(name, scheme)
53
+ scheme_registry.register(name, scheme)
54
+ end
55
+
56
+ def get_scheme(name)
57
+ scheme_registry.get(name)
58
+ end
59
+
60
+ def detect_scheme(version_string)
61
+ scheme_registry.detect_from(version_string)
62
+ end
63
+ end
64
+ end
data/lib/versius.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Versius
4
+ # Your code goes here...
5
+ end
data/sig/versius.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Versius
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: versionian
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ribose Inc.
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-02-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: singleton
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Versionian is a Ruby library for declaring, parsing, comparing, and rendering
70
+ version schemes. It provides model-driven primitives for defining how versions work,
71
+ supporting Semantic Versioning, Calendar Versioning, and unlimited custom schemes
72
+ through YAML configuration or Ruby inheritance.
73
+ email:
74
+ - open.source@ribose.com
75
+ executables: []
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".rspec"
80
+ - ".rubocop.yml"
81
+ - CODE_OF_CONDUCT.md
82
+ - README.adoc
83
+ - Rakefile
84
+ - docs/Gemfile
85
+ - docs/_guides/custom-schemes.adoc
86
+ - docs/_guides/index.adoc
87
+ - docs/_pages/component-types.adoc
88
+ - docs/_pages/declarative-schemes.adoc
89
+ - docs/_pages/index.adoc
90
+ - docs/_pages/range-matching.adoc
91
+ - docs/_pages/schemes.adoc
92
+ - docs/_references/api.adoc
93
+ - docs/_references/index.adoc
94
+ - docs/_references/schemes.adoc
95
+ - docs/_tutorials/getting-started.adoc
96
+ - docs/_tutorials/index.adoc
97
+ - docs/index.adoc
98
+ - lib/versionian.rb
99
+ - lib/versionian/component_definition.rb
100
+ - lib/versionian/component_types/base.rb
101
+ - lib/versionian/component_types/date_part.rb
102
+ - lib/versionian/component_types/enum.rb
103
+ - lib/versionian/component_types/float.rb
104
+ - lib/versionian/component_types/hash.rb
105
+ - lib/versionian/component_types/integer.rb
106
+ - lib/versionian/component_types/postfix.rb
107
+ - lib/versionian/component_types/prerelease.rb
108
+ - lib/versionian/component_types/registry.rb
109
+ - lib/versionian/component_types/string.rb
110
+ - lib/versionian/errors/invalid_scheme_error.rb
111
+ - lib/versionian/errors/invalid_version_error.rb
112
+ - lib/versionian/errors/parse_error.rb
113
+ - lib/versionian/parsers/declarative.rb
114
+ - lib/versionian/scheme_loader.rb
115
+ - lib/versionian/scheme_registry.rb
116
+ - lib/versionian/schemes/calver.rb
117
+ - lib/versionian/schemes/composite.rb
118
+ - lib/versionian/schemes/declarative.rb
119
+ - lib/versionian/schemes/pattern.rb
120
+ - lib/versionian/schemes/semantic.rb
121
+ - lib/versionian/schemes/solover.rb
122
+ - lib/versionian/schemes/wendtver.rb
123
+ - lib/versionian/version.rb
124
+ - lib/versionian/version_component.rb
125
+ - lib/versionian/version_identifier.rb
126
+ - lib/versionian/version_range.rb
127
+ - lib/versionian/version_scheme.rb
128
+ - lib/versius.rb
129
+ - sig/versius.rbs
130
+ homepage: https://github.com/lutaml/versionian
131
+ licenses: []
132
+ metadata:
133
+ allowed_push_host: https://rubygems.org
134
+ homepage_uri: https://github.com/lutaml/versionian
135
+ source_code_uri: https://github.com/lutaml/versionian
136
+ changelog_uri: https://github.com/lutaml/versionian/blob/main/CHANGELOG.md
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 3.1.0
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubygems_version: 3.5.22
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Version scheme declaration and comparison library supporting 20+ versioning
156
+ patterns
157
+ test_files: []