diff options
Diffstat (limited to 'app/models/concerns/atomic_internal_id.rb')
-rw-r--r-- | app/models/concerns/atomic_internal_id.rb | 32 |
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 |