summaryrefslogtreecommitdiff
path: root/app/models/concerns/with_uploads.rb
blob: d79c0eae77e217c561d1c62b032cccd78021151e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# frozen_string_literal: true

# Mounted uploaders are destroyed by carrierwave's after_commit
# hook. This hook fetches upload location (local vs remote) from
# Upload model. So it's necessary to make sure that during that
# after_commit hook model's associated uploads are not deleted yet.
# IOW we can not use dependent: :destroy :
# has_many :uploads, as: :model, dependent: :destroy
#
# And because not-mounted uploads require presence of upload's
# object model when destroying them (FileUploader's `build_upload` method
# references `model` on delete), we can not use after_commit hook for these
# uploads.
#
# Instead FileUploads are destroyed in before_destroy hook and remaining uploads
# are destroyed by the carrierwave's after_commit hook.

module WithUploads
  extend ActiveSupport::Concern
  include FastDestroyAll::Helpers
  include FeatureGate

  # Currently there is no simple way how to select only not-mounted
  # uploads, it should be all FileUploaders so we select them by
  # `uploader` class
  FILE_UPLOADERS = %w(PersonalFileUploader NamespaceFileUploader FileUploader).freeze

  included do
    has_many :uploads, as: :model
    has_many :file_uploads, -> { where(uploader: FILE_UPLOADERS) }, class_name: 'Upload', as: :model

    # TODO: when feature flag is removed, we can use just dependent: destroy
    # option on :file_uploads
    before_destroy :remove_file_uploads

    use_fast_destroy :file_uploads, if: :fast_destroy_enabled?
  end

  def retrieve_upload(_identifier, paths)
    uploads.find_by(path: paths)
  end

  private

  # mounted uploads are deleted in carrierwave's after_commit hook,
  # but FileUploaders which are not mounted must be deleted explicitly and
  # it can not be done in after_commit because FileUploader requires loads
  # associated model on destroy (which is already deleted in after_commit)
  def remove_file_uploads
    fast_destroy_enabled? ? delete_uploads : destroy_uploads
  end

  def delete_uploads
    file_uploads.delete_all(:delete_all)
  end

  def destroy_uploads
    file_uploads.find_each do |upload|
      upload.destroy
    end
  end

  def fast_destroy_enabled?
    Feature.enabled?(:fast_destroy_uploads, self)
  end
end