Class: Mxrb::Semantic::BatchPlan

Inherits:
Object
  • Object
show all
Defined in:
lib/mxrb/semantic/batch_plan.rb

Overview

Wraps multiple plans into a single atomic operation with rollback on failure.

Before applying, a backup of the MPR is taken using SQLite's VACUUM INTO. If any plan raises, the backup is restored and the project state is reset as if the batch never ran.

Usage:

plan1 = project.plan_rename("M.OldName", to: "M.NewName")
plan2 = project.plan_add_attribute("M.NewName", name: :Slug, type: :string)
project.batch_plan([plan1, plan2]).apply!

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project:, plans:) ⇒ BatchPlan

Returns a new instance of BatchPlan.



20
21
22
23
24
25
# File 'lib/mxrb/semantic/batch_plan.rb', line 20

def initialize(project:, plans:)
  @project = project
  @plans   = plans.freeze
  @applied = false
  @changes = @plans.flat_map { |p| p.respond_to?(:changes) ? Array(p.changes) : [] }.freeze
end

Instance Attribute Details

#changesObject (readonly)

Returns the value of attribute changes.



18
19
20
# File 'lib/mxrb/semantic/batch_plan.rb', line 18

def changes
  @changes
end

#plansObject (readonly)

Returns the value of attribute plans.



18
19
20
# File 'lib/mxrb/semantic/batch_plan.rb', line 18

def plans
  @plans
end

Instance Method Details

#applied?Boolean

Returns:

  • (Boolean)


28
# File 'lib/mxrb/semantic/batch_plan.rb', line 28

def applied? = @applied

#apply!Object

Raises:

  • (ArgumentError)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/mxrb/semantic/batch_plan.rb', line 30

def apply!
  raise ArgumentError, "batch plan was already applied" if @applied
  return self if @plans.empty?

  backup_path = "#{@project.mpr.path}.mxrb_batch_backup"
  @project.mpr.backup!(backup_path)

  begin
    @plans.each(&:apply!)
    @applied = true
  rescue => error
    idx = @plans.index { !_1.applied? }.to_i + 1
    rollback_errors = rollback_applied_plans
    @project.mpr.restore_from!(backup_path)
    @project.refresh!
    detail = rollback_errors.empty? ? "" : "; rollback failed: #{rollback_errors.join(', ')}"
    raise BatchError,
          "batch failed at plan #{idx} of #{@plans.size}: #{error.message}#{detail}"
  ensure
    FileUtils.rm_f(backup_path) rescue nil
  end
  self
end

#empty?Boolean

Returns:

  • (Boolean)


27
# File 'lib/mxrb/semantic/batch_plan.rb', line 27

def empty?   = @plans.empty?