Class: InlineFormsSchemaEdit::BatchImport

Inherits:
Object
  • Object
show all
Defined in:
lib/inline_forms_schema_edit/batch_import.rb

Overview

The import/replay half of the pipeline (phase 2): takes an exported batch payload and materializes it into the CURRENT checkout — validates every intent against this checkout's models, then replays them in order through InlineForms::SchemaApply#generate! (+ SchemaLabel for labels).

Deliberately does NOT migrate and does NOT commit: it only writes model edits + migration files and reports what it did, so the same class serves a developer replaying by hand and CI replaying automatically (CI's job then runs the test gate, commits, builds).

Defined Under Namespace

Classes: ImportError, Result

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(payload, executor: nil, label_writer: nil) ⇒ BatchImport

Injectable for tests: executor replaces the addto generator run, label_writer replaces SchemaLabel.write.



25
26
27
28
29
30
31
# File 'lib/inline_forms_schema_edit/batch_import.rb', line 25

def initialize(payload, executor: nil, label_writer: nil)
  @payload = payload.is_a?(String) ? JSON.parse(payload) : payload
  @executor = executor
  @label_writer = label_writer || InlineForms::SchemaLabel.method(:write)
rescue JSON::ParserError => e
  raise ImportError, "not valid JSON: #{e.message}"
end

Instance Attribute Details

#payloadObject (readonly)

Returns the value of attribute payload.



21
22
23
# File 'lib/inline_forms_schema_edit/batch_import.rb', line 21

def payload
  @payload
end

Class Method Details

.from_file(path, **kwargs) ⇒ Object



33
34
35
# File 'lib/inline_forms_schema_edit/batch_import.rb', line 33

def self.from_file(path, **kwargs)
  new(File.read(path), **kwargs)
end

Instance Method Details

#apply!(destination_root: Rails.root) ⇒ Object

Replay all intents into destination_root. Stops at the first failure (raises), leaving earlier generated files in place for inspection — the caller (CI) works on a clean tree it can reset.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/inline_forms_schema_edit/batch_import.rb', line 62

def apply!(destination_root: Rails.root)
  intents = verify!
  migrations = []
  labels = []
  plan = []

  intents.each do |intent_hash|
    intent = build_intent(intent_hash)
    apply = InlineForms::SchemaApply.new(intent)
    plan.concat(apply.preview_plan)

    migration = apply.generate!(
      destination_root: destination_root,
      executor: @executor || InlineForms::SchemaApply::DEFAULT_GENERATE_EXECUTOR
    )
    migrations << migration if migration

    if intent_hash["label"].present?
      labels << @label_writer.call(
        destination_root: destination_root,
        model_class: intent.model_class,
        attribute: intent.attribute,
        label: intent_hash["label"],
        locale: intent_hash["locale"].presence || I18n.default_locale.to_s
      )
    end
  end

  Result.new(applied: intents.size, migrations: migrations, labels: labels, plan: plan)
end

#verify!Object

Verify structure + digest + per-intent validity against this checkout. Returns the intents array (raises ImportError on any problem).

Raises:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/inline_forms_schema_edit/batch_import.rb', line 39

def verify!
  raise ImportError, "unsupported format #{payload['format'].inspect}" unless payload["format"] == BatchExport::FORMAT

  intents = payload["intents"]
  raise ImportError, "batch has no intents" if intents.blank?

  canonical = JSON.generate(intents)
  digest = "sha256:#{Digest::SHA256.hexdigest(canonical)}"
  unless digest == payload["digest"]
    raise ImportError, "digest mismatch: payload says #{payload['digest'].inspect}, content is #{digest.inspect} — batch changed after submit?"
  end

  intents.each_with_index do |intent, i|
    error = IntentValidator.error_for(intent)
    raise ImportError, "intent ##{i + 1} (#{intent['model_name']}##{intent['attribute']}): #{error}" if error
  end

  intents
end