gitlab-org--gitlab-foss/app/models/concerns/atomic_internal_id.rb

47 lines
1.7 KiB
Ruby
Raw Normal View History

# 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
2018-03-14 12:42:03 +00:00
module ClassMethods
def has_internal_id(on, scope:, usage: nil, init:) # rubocop:disable Naming/PredicateName
before_validation(on: :create) do
if self.public_send(on).blank? # rubocop:disable GitlabSecurity/PublicSend
scope_attrs = { scope => self.public_send(scope) } # rubocop:disable GitlabSecurity/PublicSend
2018-03-14 12:42:03 +00:00
usage = (usage || self.class.table_name).to_sym
2018-03-14 12:42:03 +00:00
new_iid = InternalId.generate_next(self, scope_attrs, usage, init)
self.public_send("#{on}=", new_iid) # rubocop:disable GitlabSecurity/PublicSend
end
end
2018-03-14 12:42:03 +00:00
validates on, presence: true, numericality: true
end
end
def to_param
iid.to_s
end
end