Class: HasHelpers::Cards::Importer
- Inherits:
-
Object
- Object
- HasHelpers::Cards::Importer
- Defined in:
- app/importers/has_helpers/cards/importer.rb
Defined Under Namespace
Classes: AuditError, Diff, Result
Class Method Summary collapse
-
.run(*args, **opts) ⇒ Object
Public API entry point.
Instance Method Summary collapse
-
#hover_only_definition?(raw_def) ⇒ Boolean
Definitions where
hover_only: trueare skipped at import — they live in memory only and are assembled from the registry at request time. -
#initialize(application:, resources:, mode:, audit:) ⇒ Importer
constructor
Constructor is internal; external callers use .run.
-
#purge_persisted_hover_only_cards!(card_pairs) ⇒ Object
Removes any persisted CardDefinition rows that match a hover-only DSL definition (by identifier).
-
#run ⇒ Object
Main execution entry.
Constructor Details
#initialize(application:, resources:, mode:, audit:) ⇒ Importer
Constructor is internal; external callers use .run.
resources: Class|Symbol|Array or nil (no filter)
mode: :apply or :dry_run
audit: true/false (whether to run drift detection)
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'app/importers/has_helpers/cards/importer.rb', line 134 def initialize(application:, resources:, mode:, audit:) # Normalize resource filter into a compact set of symbols representing resource names (e.g. [:advisor, :carrier]). @application_name = application.to_s @application = ::HasHelpers::Application.find_by!("LOWER(name) = ?", @application_name.downcase) @resource_filter = normalize_resource_filter(resources) @mode = (mode || :apply).to_sym @audit = audit # This struct accumulates counts while importing. @result = Result.new( created_cards: 0, updated_cards: 0, created_fields: 0, updated_fields: 0, created_resources: 0, updated_resources: 0, skipped_cards: 0 ) end |
Class Method Details
.run(*args, **opts) ⇒ Object
Public API entry point.
Import all resources declared in the DSL
HasHelpers::Cards::Importer.run
# Import only selected resource types
HasHelpers::Cards::Importer.run([Advisor, Carrier]) # or symbols: [:advisor, :carrier]
# Options (keyword args)
HasHelpers::Cards::Importer.run(resources: [:advisor], mode: :apply, audit: true)
Supported modes (ClickUp):
- mode: :dry_run → compute and print diffs, no DB writes,
raise error if audit fails.
- mode: :apply → default. Always audit first. If drift is breaking,
raise and do not persist.
87 88 89 90 91 92 93 94 95 |
# File 'app/importers/has_helpers/cards/importer.rb', line 87 def run(*args, **opts) ::HasHelpers::Cards::Registry.cards resources, mode, audit = normalize_run_arguments(args, opts) application = opts[:application] || opts[:application_name] raise ArgumentError, "application is required (e.g. application: 'AgenciesHQ')" if application.blank? new(application: application, resources: resources, mode: mode, audit: audit).run end |
Instance Method Details
#hover_only_definition?(raw_def) ⇒ Boolean
Definitions where hover_only: true are skipped at import — they live in
memory only and are assembled from the registry at request time.
206 207 208 209 |
# File 'app/importers/has_helpers/cards/importer.rb', line 206 def hover_only_definition?(raw_def) defn = raw_def.is_a?(Hash) ? raw_def.deep_symbolize_keys : raw_def.to_h.deep_symbolize_keys !!defn[:hover_only] end |
#purge_persisted_hover_only_cards!(card_pairs) ⇒ Object
Removes any persisted CardDefinition rows that match a hover-only DSL definition (by identifier). Hover-only cards are in-memory only; this purges any rows left over from prior runs before we made them in-memory. Safe to call repeatedly. Dependent rows cascade via :dependent => :destroy associations.
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'app/importers/has_helpers/cards/importer.rb', line 215 def purge_persisted_hover_only_cards!(card_pairs) return if mode == :dry_run hover_only_ids = card_pairs. select { |_klass, defn| hover_only_definition?(defn) }. map { |_klass, defn| normalize_identifier((defn.deep_symbolize_keys)[:identifier]) }. compact. uniq return if hover_only_ids.empty? identifier_ids = ::HasHelpers::Cards::CardIdentifier.where(name: hover_only_ids).pluck(:id) return if identifier_ids.empty? ::HasHelpers::Cards::CardDefinition.where(identifier_id: identifier_ids).find_each(&:destroy) end |
#run ⇒ Object
Main execution entry.
"Importer Flow"
Steps:
1. (Optionally) run migrations for cards (hook only for now).
2. Discover card definitions from the DSL (CardBuilder::Base descendants).
3. For each card:
- Normalize + compute checksum (code view).
- Build DB view (if card exists).
- Diff + classify changes.
- Audit: if breaking drift, raise AuditError.
- If mode = :apply, upsert card_definitions + card_fields +
card_definition_resources inside a transaction.
- If mode = :dry_run, just log diffs and don't write.
4. Optional global audit: detect cards existing only in DB but
not in code, when there is no resource filter (breaking).
5. Log summary of created/updated/skipped records.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'app/importers/has_helpers/cards/importer.rb', line 173 def run # "We may add a migrations section just like we have for reports and webhooks" run_migrations_if_any! # Discover all card definitions exposed by the DSL (HasHelpers::Cards::CardBuilder::Base subclasses). card_pairs = discover_card_definitions # Each element is: [CardClass, raw_hash_definition] validate_hover_cards!(card_pairs) # Hover-only cards live entirely in-memory (DSL registry). They are NOT persisted. # Any pre-existing rows for hover-only identifiers (whether currently flagged as # hover_only in DB or not) are dropped here, then we skip the import for them below. purge_persisted_hover_only_cards!(card_pairs) # Import each card definition (or audit, depending on mode), skipping hover-only definitions. card_pairs.each do |klass, raw_def| next if hover_only_definition?(raw_def) import_card!(klass, raw_def) end # Global audit for "cards present in DB but missing in code". # This is only meaningful when we are importing "all resources" (i.e. no resource filter is applied). audit_removed_cards!(card_pairs) if audit && resource_filter.nil? # Log structured summary for debugging / future dashboards. log_summary # Return Result struct (created/updated counts, etc.) result end |