Module: TypedEAV::BulkWrite
- Defined in:
- lib/typed_eav/bulk_write.rb
Overview
Internal executor for host-class bulk typed-value writes.
Host models keep the public bulk_set_typed_eav_values (uniform
values-per-record) and bulk_set_typed_eav_values_per_record
(per-record-varying values) APIs; this module owns the transaction
shape, savepoint isolation, error aggregation, field name resolution
delegation, and version-group stamping.
Internal shape (G1, issue #18)
Both public executors (execute and execute_per_record) are thin
adapters: they validate their inputs, resolve the version grouping,
allocate field UUIDs, and then hand off to execute_pairs(pairs, effective_grouping, field_uuids) — a single shared loop that takes
ordered [record, vbn] pairs and runs the outer-transaction-plus-
savepoint-per-record envelope; transaction: :chunks repeats that
envelope per chunk so completed chunks can commit independently.
Pair-shaped (not Hash-shaped) so execute's [record, vbn] list can
carry duplicate in-memory instances of the same persisted row without
silently collapsing them via Hash-key collision — preserving
execute's byte-for-byte behavior contract.
Class Method Summary collapse
-
.apply_record_save(record:, vbn:, effective_grouping:, uuids:, accumulator:) ⇒ Object
rubocop:enable Metrics/ParameterLists.
-
.execute(host_class:, records:, values_by_field_name:, version_grouping: :default, transaction: :all, chunk_size: nil) ⇒ Object
rubocop:disable Metrics/ParameterLists -- transaction controls are explicit API contract.
-
.execute_per_record(host_class:, values_by_record:, version_grouping: :default, transaction: :all, chunk_size: nil) ⇒ Object
Per-record-varying sibling to
execute.
Class Method Details
.apply_record_save(record:, vbn:, effective_grouping:, uuids:, accumulator:) ⇒ Object
rubocop:enable Metrics/ParameterLists
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/typed_eav/bulk_write.rb', line 75 def apply_record_save(record:, vbn:, effective_grouping:, uuids:, accumulator:) do_save = lambda do record.typed_eav_attributes = vbn.map { |name, value| typed_eav_entry_for(name, value) } stamp_pending_version_group_ids(record, effective_grouping, uuids) if record.save accumulator[:successes] << record else accumulator[:errors_by_record][record] = record.errors..transform_keys(&:to_s) raise ActiveRecord::Rollback end end do_save.call end |
.execute(host_class:, records:, values_by_field_name:, version_grouping: :default, transaction: :all, chunk_size: nil) ⇒ Object
rubocop:disable Metrics/ParameterLists -- transaction controls are explicit API contract.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/typed_eav/bulk_write.rb', line 29 def execute( host_class:, records:, values_by_field_name:, version_grouping: :default, transaction: :all, chunk_size: nil ) validate_inputs!(records, values_by_field_name, version_grouping, transaction, chunk_size) records = records.to_a return { successes: [], errors_by_record: {} } if records.empty? validate_record_classes!(host_class, records) ensure_connection_owner!(host_class) effective_grouping = resolve_grouping(version_grouping) vbn = values_by_field_name.transform_keys(&:to_s) field_uuids = effective_grouping == :per_field ? vbn.keys.index_with { SecureRandom.uuid } : nil execute_pairs( host_class, records.map { |r| [r, vbn] }, effective_grouping, field_uuids, transaction, chunk_size ) end |
.execute_per_record(host_class:, values_by_record:, version_grouping: :default, transaction: :all, chunk_size: nil) ⇒ Object
Per-record-varying sibling to execute. Accepts a Hash<host_record, Hash<field_name, value>> and routes each record's value-set through
the same shared execute_pairs envelope.
Empty values_by_record short-circuits to the empty result without
opening a transaction (matches execute's empty-records contract).
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/typed_eav/bulk_write.rb', line 55 def execute_per_record( host_class:, values_by_record:, version_grouping: :default, transaction: :all, chunk_size: nil ) validate_per_record_inputs!(values_by_record, version_grouping, transaction, chunk_size) return { successes: [], errors_by_record: {} } if values_by_record.empty? validate_record_classes!(host_class, values_by_record.keys, method: :bulk_set_typed_eav_values_per_record) ensure_connection_owner!(host_class) effective_grouping = resolve_grouping(version_grouping) pairs = values_by_record.map { |record, vbn| [record, vbn.transform_keys(&:to_s)] } field_uuids = if effective_grouping == :per_field pairs.flat_map { |(_record, vbn)| vbn.keys }.uniq.index_with { SecureRandom.uuid } end execute_pairs(host_class, pairs, effective_grouping, field_uuids, transaction, chunk_size) end |