Module: OpenLoam::Bulk

Defined in:
lib/open_loam/bulk.rb

Overview

Datatable bulk actions over selected records — policy-checked PER record and tenant-scoped. The ids are resolved THROUGH the tenant scope (model.where(id: ids)), so a forged cross-tenant id is simply not found — a bulk op can never reach another tenant's rows.

OpenLoam::Bulk.soft_delete(Equipment, params[:ids])
OpenLoam::Bulk.set_field(Equipment, params[:ids], field: "status", value: "retired")

Returns the number of records actually affected (a record the actor may not touch, or an id from another tenant, is silently skipped).

Class Method Summary collapse

Class Method Details

.each_permitted(model, ids) ⇒ Object



40
41
42
43
44
45
46
# File 'lib/open_loam/bulk.rb', line 40

def each_permitted(model, ids)
  count = 0
  selected(model, ids).find_each do |record|
    count += 1 if yield(record, OpenLoam::Policy.for(record))
  end
  count
end

.selected(model, ids) ⇒ Object

A relation for "export selected" — tenant-scoped, so it composes with OpenLoam::Export.csv and can't leak another tenant's rows.



36
37
38
# File 'lib/open_loam/bulk.rb', line 36

def selected(model, ids)
  model.where(id: Array(ids))
end

.set_field(model, ids, field:, value:) ⇒ Object



24
25
26
27
28
29
30
31
32
# File 'lib/open_loam/bulk.rb', line 24

def set_field(model, ids, field:, value:)
  field = field.to_s
  each_permitted(model, ids) do |record, policy|
    next unless policy.update? && policy.writable?(field) && writable_column?(model, field)

    record.update!(field => value)
    true
  end
end

.soft_delete(model, ids) ⇒ Object



15
16
17
18
19
20
21
22
# File 'lib/open_loam/bulk.rb', line 15

def soft_delete(model, ids)
  each_permitted(model, ids) do |record, policy|
    next unless policy.destroy?

    record.soft_delete!
    true
  end
end

.workflow_column?(model, field) ⇒ Boolean

A workflow status column may ONLY change through a transition (role-gated). Skip it here so a bulk set-field can't self-"approve" (the model guard is the backstop; this keeps the bulk op a clean no-op rather than an error).

Returns:

  • (Boolean)


55
56
57
# File 'lib/open_loam/bulk.rb', line 55

def workflow_column?(model, field)
  model.respond_to?(:open_loam_workflow) && model.open_loam_workflow&.column == field
end

.writable_column?(model, field) ⇒ Boolean

Returns:

  • (Boolean)


48
49
50
# File 'lib/open_loam/bulk.rb', line 48

def writable_column?(model, field)
  model.column_names.include?(field) && OpenLoam::Import::PLUMBING.exclude?(field) && !workflow_column?(model, field)
end