class Saviour::LifeCycle

Constants

SHOULD_USE_INTERLOCK

Public Class Methods

new(model, persistence_klass) click to toggle source
# File lib/saviour/life_cycle.rb, line 80
def initialize(model, persistence_klass)
  raise ConfigurationError, "Please provide an object compatible with Saviour." unless model.class.respond_to?(:attached_files)

  @persistence_klass = persistence_klass
  @model = model
end

Public Instance Methods

create!() click to toggle source
# File lib/saviour/life_cycle.rb, line 105
def create!
  process_upload(FileCreator)
end
delete!() click to toggle source
# File lib/saviour/life_cycle.rb, line 87
def delete!
  DbHelpers.run_after_commit do
    pool = Concurrent::Throttle.new Saviour::Config.concurrent_workers

    futures = attached_files.map do |column|
      pool.future(@model.send(column)) do |file|
        path = file.persisted_path
        file.uploader.storage.delete(path) if path
        file.delete
      end
    end

    ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
      futures.each(&:value!)
    end
  end
end
update!() click to toggle source
# File lib/saviour/life_cycle.rb, line 109
def update!
  process_upload(FileUpdater, touch: true)
end

Private Instance Methods

attached_files() click to toggle source
# File lib/saviour/life_cycle.rb, line 165
def attached_files
  @model.class.attached_files
end
process_upload(klass, touch: false) click to toggle source
# File lib/saviour/life_cycle.rb, line 115
def process_upload(klass, touch: false)
  persistence_layer = @persistence_klass.new(@model)

  uploaders = attached_files.map do |column|
    next unless @model.send(column).changed?

    klass.new(
      persistence_layer.read(column),
      @model.send(column),
      column,
      ActiveRecord::Base.connection
    )
  end.compact

  pool = Concurrent::Throttle.new Saviour::Config.concurrent_workers

  futures = uploaders.map { |uploader|
    pool.future(uploader) { |given_uploader|
      if SHOULD_USE_INTERLOCK
        Rails.application.executor.wrap { given_uploader.upload }
      else
        given_uploader.upload
      end
    }
  }

  work = -> { futures.map(&:value!).compact }

  result = if SHOULD_USE_INTERLOCK
             ActiveSupport::Dependencies.interlock.permit_concurrent_loads(&work)
           else
             work.call
           end

  attrs = result.to_h

  uploaders.map(&:uploader).select { |x| x.class.after_upload_hooks.any? }.each do |uploader|
    uploader.class.after_upload_hooks.each do |hook|
      uploader.instance_exec(uploader.stashed, &hook)
    end
  end

  if attrs.length > 0 && touch && @model.class.record_timestamps
    touches = @model.class.send(:timestamp_attributes_for_update_in_model).map { |x| [x, Time.current] }.to_h
    attrs.merge!(touches)
  end

  persistence_layer.write_attrs(attrs) if attrs.length > 0
end