From eff7b634128aeda0c62283343574c5384d5fd82e Mon Sep 17 00:00:00 2001
From: Laura Montemayor <lmontemayor@gitlab.com>
Date: Fri, 9 Feb 2024 17:10:49 +0000
Subject: [PATCH] Add sort scopes to semantic version concern

---
 app/models/concerns/semantic_versionable.rb   |  3 +-
 doc/development/semver.md                     |  9 +++++
 .../concerns/semantic_versionable_spec.rb     | 38 +++++++++++++++----
 3 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/app/models/concerns/semantic_versionable.rb b/app/models/concerns/semantic_versionable.rb
index 70cc1edae81f6..99865cb15d1e2 100644
--- a/app/models/concerns/semantic_versionable.rb
+++ b/app/models/concerns/semantic_versionable.rb
@@ -4,11 +4,12 @@ module SemanticVersionable
   extend ActiveSupport::Concern
 
   included do
-    # sets the default value for require_valid_semver to false
     self.require_valid_semver = false
 
     validate :semver_format, if: :require_valid_semver?
 
+    scope :order_by_semantic_version_desc, -> { order(semver_major: :desc, semver_minor: :desc, semver_patch: :desc) }
+    scope :order_by_semantic_version_asc, -> { order(semver_major: :asc, semver_minor: :asc, semver_patch: :asc) }
     private
 
     def semver_format
diff --git a/doc/development/semver.md b/doc/development/semver.md
index 4a591e46cdb85..cd7aac5c5d7f1 100644
--- a/doc/development/semver.md
+++ b/doc/development/semver.md
@@ -56,6 +56,15 @@ Depending on the use case, you may want to disable the validation during the rol
 
 Please refer to [this MR](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142228) as a reference.
 
+## Sorting
+
+The concern provides two scopes to sort by semantic versions:
+
+```ruby
+scope :order_by_semantic_version_desc, -> { order(semver_major: :desc, semver_minor: :desc, semver_patch: :desc)}
+scope :order_by_semantic_version_asc, -> { order(semver_major: :asc, semver_minor: :asc, semver_patch: :asc)}
+```
+
 ## Filtering and Searching
 
 TBD
diff --git a/spec/models/concerns/semantic_versionable_spec.rb b/spec/models/concerns/semantic_versionable_spec.rb
index 500b4564fbeff..dc476d603ace0 100644
--- a/spec/models/concerns/semantic_versionable_spec.rb
+++ b/spec/models/concerns/semantic_versionable_spec.rb
@@ -5,19 +5,23 @@
 RSpec.describe SemanticVersionable, feature_category: :mlops do
   using RSpec::Parameterized::TableSyntax
 
+  before_all do
+    ActiveRecord::Schema.define do |_t|
+      create_table :_test_semantic_versions, force: true do |t|
+        t.integer :semver_major
+        t.integer :semver_minor
+        t.integer :semver_patch
+        t.string :semver_prerelease
+      end
+    end
+  end
+
   let(:model_class) do
     Class.new(ActiveRecord::Base) do
       include SemanticVersionable
       semver_method :semver
 
-      # we need a table for the dummy class to operate
-      self.table_name = 'ml_model_versions'
-
-      def self.name
-        'Ml::ModelVersion'
-      end
-
-      attr_accessor :major, :minor, :patch, :prerelease
+      self.table_name = '_test_semantic_versions'
     end
   end
 
@@ -94,4 +98,22 @@ def self.name
       expect(model_instance.valid?).to be true
     end
   end
+
+  describe 'scopes' do
+    let(:first_release) { model_class.create!(semver: '1.0.1') }
+    let(:second_release) { model_class.create!(semver: '3.0.1') }
+    let(:patch) { model_class.create!(semver: '2.0.1') }
+
+    describe '.order_by_semantic_version_asc' do
+      it 'orders the versions by semantic order ascending' do
+        expect(model_class.order_by_semantic_version_asc).to eq([first_release, patch, second_release])
+      end
+    end
+
+    describe '.order_by_semantic_version_desc' do
+      it 'orders the versions by semantic order descending' do
+        expect(model_class.order_by_semantic_version_desc).to eq([second_release, patch, first_release])
+      end
+    end
+  end
 end
-- 
GitLab