summaryrefslogtreecommitdiff
path: root/app/models/concerns/atomic_internal_id.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/concerns/atomic_internal_id.rb')
-rw-r--r--app/models/concerns/atomic_internal_id.rb32
1 files changed, 30 insertions, 2 deletions
diff --git a/app/models/concerns/atomic_internal_id.rb b/app/models/concerns/atomic_internal_id.rb
index eef5c0bfcd1..6a0f29806c4 100644
--- a/app/models/concerns/atomic_internal_id.rb
+++ b/app/models/concerns/atomic_internal_id.rb
@@ -1,13 +1,41 @@
+# Include atomic internal id generation scheme for a model
+#
+# This allows to atomically generate internal ids that are
+# unique within a given scope.
+#
+# For example, let's generate internal ids for Issue per Project:
+# ```
+# class Issue < ActiveRecord::Base
+# has_internal_id :iid, scope: :project, init: ->(s) { s.project.issues.maximum(:iid) }
+# end
+# ```
+#
+# This generates unique internal ids per project for newly created issues.
+# The generated internal id is saved in the `iid` attribute of `Issue`.
+#
+# This concern uses InternalId records to facilitate atomicity.
+# In the absence of a record for the given scope, one will be created automatically.
+# In this situation, the `init` block is called to calculate the initial value.
+# In the example above, we calculate the maximum `iid` of all issues
+# within the given project.
+#
+# Note that a model may have more than one internal id associated with possibly
+# different scopes.
module AtomicInternalId
extend ActiveSupport::Concern
included do
class << self
- def has_internal_id(on, scope:, usage: nil, init: nil)
+ def has_internal_id(on, scope:, usage: nil, init: nil) # rubocop:disable Naming/PredicateName
before_validation(on: :create) do
if self.public_send(on).blank? # rubocop:disable GitlabSecurity/PublicSend
+
+ scope_attrs = [scope].flatten.compact.each_with_object({}) do |e, h|
+ h[e] = self.public_send(e) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
usage = (usage || self.class.name.tableize).to_sym
- new_iid = InternalId.generate_next(self, scope, usage, init)
+ new_iid = InternalId.generate_next(self, scope_attrs, usage, init)
self.public_send("#{on}=", new_iid) # rubocop:disable GitlabSecurity/PublicSend
end
end