diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-10 00:10:04 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-10 00:10:04 +0000 |
commit | 1bf4fece121298260663c6ca73d39716d3548a03 (patch) | |
tree | 5c82dd2fa63552ecb67fcb034740ec7e8fdba25a /app/models | |
parent | 254ec28f5448f6f353cd98f637985de3d1405854 (diff) | |
download | gitlab-ce-1bf4fece121298260663c6ca73d39716d3548a03.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/blob.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/where_composite.rb | 81 | ||||
-rw-r--r-- | app/models/issue.rb | 21 |
3 files changed, 102 insertions, 2 deletions
diff --git a/app/models/blob.rb b/app/models/blob.rb index d8282c918b7..cdc5838797b 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -254,5 +254,3 @@ class Blob < SimpleDelegator classes.find { |viewer_class| viewer_class.can_render?(self, verify_binary: verify_binary) } end end - -Blob.prepend_if_ee('EE::Blob') diff --git a/app/models/concerns/where_composite.rb b/app/models/concerns/where_composite.rb new file mode 100644 index 00000000000..3b66efc1c77 --- /dev/null +++ b/app/models/concerns/where_composite.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +module WhereComposite + extend ActiveSupport::Concern + + class TooManyIds < ArgumentError + LIMIT = 100 + + def initialize(no_of_ids) + super(<<~MSG) + At most #{LIMIT} identifier sets at a time please! Got #{no_of_ids}. + Have you considered splitting your request into batches? + MSG + end + + def self.guard(collection) + n = collection.size + return collection if n <= LIMIT + + raise self, n + end + end + + class_methods do + # Apply a set of constraints that function as composite IDs. + # + # This is the plural form of the standard ActiveRecord idiom: + # `where(foo: x, bar: y)`, except it allows multiple pairs of `x` and + # `y` to be specified, with the semantics that translate to: + # + # ```sql + # WHERE + # (foo = x_0 AND bar = y_0) + # OR (foo = x_1 AND bar = y_1) + # OR ... + # ``` + # + # or the equivalent: + # + # ```sql + # WHERE + # (foo, bar) IN ((x_0, y_0), (x_1, y_1), ...) + # ``` + # + # @param permitted_keys [Array<Symbol>] The keys each hash must have. There + # must be at least one key (but really, + # it ought to be at least two) + # @param hashes [Array<#to_h>|#to_h] The constraints. Each parameter must have a + # value for the keys named in `permitted_keys` + # + # e.g.: + # ``` + # where_composite(%i[foo bar], [{foo: 1, bar: 2}, {foo: 1, bar: 3}]) + # ``` + # + def where_composite(permitted_keys, hashes) + raise ArgumentError, 'no permitted_keys' unless permitted_keys.present? + + # accept any hash-like thing, such as Structs + hashes = TooManyIds.guard(Array.wrap(hashes)).map(&:to_h) + + return none if hashes.empty? + + case permitted_keys.size + when 1 + key = permitted_keys.first + where(key => hashes.map { |hash| hash.fetch(key) }) + else + clauses = hashes.map do |hash| + permitted_keys.map do |key| + arel_table[key].eq(hash.fetch(key)) + end.reduce(:and) + end + + where(clauses.reduce(:or)) + end + rescue KeyError + raise ArgumentError, "all arguments must contain #{permitted_keys}" + end + end +end diff --git a/app/models/issue.rb b/app/models/issue.rb index f414be51065..cdd7429bc58 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -16,6 +16,7 @@ class Issue < ApplicationRecord include LabelEventable include IgnorableColumns include MilestoneEventable + include WhereComposite DueDateStruct = Struct.new(:title, :name).freeze NoDueDate = DueDateStruct.new('No Due Date', '0').freeze @@ -78,6 +79,26 @@ class Issue < ApplicationRecord scope :counts_by_state, -> { reorder(nil).group(:state_id).count } + # An issue can be uniquely identified by project_id and iid + # Takes one or more sets of composite IDs, expressed as hash-like records of + # `{project_id: x, iid: y}`. + # + # @see WhereComposite::where_composite + # + # e.g: + # + # .by_project_id_and_iid({project_id: 1, iid: 2}) + # .by_project_id_and_iid([]) # returns ActiveRecord::NullRelation + # .by_project_id_and_iid([ + # {project_id: 1, iid: 1}, + # {project_id: 2, iid: 1}, + # {project_id: 1, iid: 2} + # ]) + # + scope :by_project_id_and_iid, ->(composites) do + where_composite(%i[project_id iid], composites) + end + after_commit :expire_etag_cache, unless: :importing? after_save :ensure_metrics, unless: :importing? |