Class: InventoryRefresh::InventoryCollection

Inherits:
Object
  • Object
show all
Includes:
Helpers::AssociationsHelper, Helpers::InitializeHelper, Helpers::QuestionsHelper
Defined in:
lib/inventory_refresh/inventory_collection.rb,
lib/inventory_refresh/inventory_collection/graph.rb,
lib/inventory_refresh/inventory_collection/builder.rb,
lib/inventory_refresh/inventory_collection/helpers.rb,
lib/inventory_refresh/inventory_collection/scanner.rb,
lib/inventory_refresh/inventory_collection/reference.rb,
lib/inventory_refresh/inventory_collection/index/proxy.rb,
lib/inventory_refresh/inventory_collection/data_storage.rb,
lib/inventory_refresh/inventory_collection/serialization.rb,
lib/inventory_refresh/inventory_collection/index/type/base.rb,
lib/inventory_refresh/inventory_collection/index/type/data.rb,
lib/inventory_refresh/inventory_collection/unconnected_edge.rb,
lib/inventory_refresh/inventory_collection/references_storage.rb,
lib/inventory_refresh/inventory_collection/index/type/local_db.rb,
lib/inventory_refresh/inventory_collection/index/type/skeletal.rb,
lib/inventory_refresh/inventory_collection/helpers/questions_helper.rb,
lib/inventory_refresh/inventory_collection/helpers/initialize_helper.rb,
lib/inventory_refresh/inventory_collection/helpers/associations_helper.rb

Overview

For more usage examples please follow spec examples in

  • spec/models/inventory_refresh/save_inventory/single_inventory_collection_spec.rb

  • spec/models/inventory_refresh/save_inventory/acyclic_graph_of_inventory_collections_spec.rb

  • spec/models/inventory_refresh/save_inventory/graph_of_inventory_collections_spec.rb

  • spec/models/inventory_refresh/save_inventory/graph_of_inventory_collections_targeted_refresh_spec.rb

  • spec/models/inventory_refresh/save_inventory/strategies_and_references_spec.rb

Examples:

storing Vm model data into the DB


@ems = ManageIQ::Providers::BaseManager.first
puts @ems.vms.collect(&:ems_ref) # => []

# Init InventoryCollection
vms_inventory_collection = ::InventoryRefresh::InventoryCollection.new(
  :model_class => ManageIQ::Providers::CloudManager::Vm, :parent => @ems, :association => :vms
)

# Fill InventoryCollection with data
# Starting with no vms, lets add vm1 and vm2
vms_inventory_collection.build(:ems_ref => "vm1", :name => "vm1")
vms_inventory_collection.build(:ems_ref => "vm2", :name => "vm2")

# Save InventoryCollection to the db
InventoryRefresh::SaveInventory.save_inventory(@ems, [vms_inventory_collection])

# The result in the DB is that vm1 and vm2 were created
puts @ems.vms.collect(&:ems_ref) # => ["vm1", "vm2"]

In another refresh, vm1 does not exist anymore and vm3 was added

# Init InventoryCollection
vms_inventory_collection = ::InventoryRefresh::InventoryCollection.new(
  :model_class => ManageIQ::Providers::CloudManager::Vm, :parent => @ems, :association => :vms
)

# Fill InventoryCollection with data
vms_inventory_collection.build(:ems_ref => "vm2", :name => "vm2")
vms_inventory_collection.build(:ems_ref => "vm3", :name => "vm3")

# Save InventoryCollection to the db
InventoryRefresh::SaveInventory.save_inventory(@ems, [vms_inventory_collection])

# The result in the DB is that vm1 was deleted, vm2 was updated and vm3 was created
puts @ems.vms.collect(&:ems_ref) # => ["vm2", "vm3"]

Defined Under Namespace

Modules: Helpers, Index Classes: Builder, DataStorage, Graph, Reference, ReferencesStorage, Scanner, Serialization, UnconnectedEdge

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Helpers::QuestionsHelper

#check_changed?, #complete?, #create_allowed?, #create_only?, #data_collection_finalized?, #delete_allowed?, #delete_complement_noop?, #noop?, #parallel_safe?, #saveable?, #saved?, #saving_noop?, #supports_column?, #supports_sti?, #targeted?, #update_only?, #use_ar_object?

Methods included from Helpers::InitializeHelper

#init_all_manager_uuids, #init_arels, #init_basic_properties, #init_changed_records_stats, #init_custom_procs, #init_data, #init_flags, #init_ic_relations, #init_model_attributes, #init_references, #init_storages, #init_strategies, #process_retention_strategy, #process_saver_strategy, #process_strategy

Methods included from Helpers::AssociationsHelper

#association_to_base_class_mapping, #association_to_foreign_key_mapping, #association_to_foreign_type_mapping, #belongs_to_associations, #fixed_foreign_keys, #foreign_key_to_association_mapping, #foreign_keys, #foreign_type_to_association_mapping

Constructor Details

#initialize(properties = {}) ⇒ InventoryCollection

Returns a new instance of InventoryCollection.

Parameters:

  • properties (Hash) (defaults to: {})
    • see init methods for params description



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/inventory_refresh/inventory_collection.rb', line 140

def initialize(properties = {})
  init_basic_properties(properties[:association],
                        properties[:model_class],
                        properties[:name],
                        properties[:parent])

  init_flags(properties[:complete],
             properties[:create_only],
             properties[:check_changed],
             properties[:update_only],
             properties[:use_ar_object],
             properties[:targeted],
             properties[:assert_graph_integrity])

  init_strategies(properties[:strategy],
                  properties[:saver_strategy],
                  properties[:retention_strategy],
                  properties[:delete_method])

  init_references(properties[:manager_ref],
                  properties[:manager_ref_allowed_nil],
                  properties[:secondary_refs],
                  properties[:manager_uuids])

  init_all_manager_uuids(properties[:all_manager_uuids],
                         properties[:all_manager_uuids_scope],
                         properties[:all_manager_uuids_timestamp])

  init_ic_relations(properties[:dependency_attributes],
                    properties[:parent_inventory_collections])

  init_arels(properties[:arel],
             properties[:targeted_arel])

  init_custom_procs(properties[:custom_save_block],
                    properties[:custom_reconnect_block])

  init_model_attributes(properties[:attributes_blacklist],
                        properties[:attributes_whitelist],
                        properties[:inventory_object_attributes],
                        properties[:batch_extra_attributes])

  init_data(properties[:default_values])

  init_storages

  init_changed_records_stats
end

Instance Attribute Details

#all_manager_uuidsArray?

If present, InventoryCollection switches into delete_complement mode, where it will delete every record from the DB, that is not present in this list. This is used for the batch processing, where we don’t know which InventoryObject should be deleted, but we know all manager_uuids of all InventoryObject objects that exists in the provider.

Returns:

  • (Array, nil)

    nil or a list of all :manager_uuids that are present in the Provider’s InventoryCollection.



80
81
82
# File 'lib/inventory_refresh/inventory_collection.rb', line 80

def all_manager_uuids
  @all_manager_uuids
end

#all_manager_uuids_scopeArray?

Returns Scope for applying :all_manager_uuids.

Returns:

  • (Array, nil)

    Scope for applying :all_manager_uuids



83
84
85
# File 'lib/inventory_refresh/inventory_collection.rb', line 83

def all_manager_uuids_scope
  @all_manager_uuids_scope
end

#all_manager_uuids_timestampString

Returns Timestamp in UTC before fetching :all_manager_uuids.

Returns:

  • (String)

    Timestamp in UTC before fetching :all_manager_uuids



86
87
88
# File 'lib/inventory_refresh/inventory_collection.rb', line 86

def all_manager_uuids_timestamp
  @all_manager_uuids_timestamp
end

#arelObject (readonly)

Returns the value of attribute arel.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def arel
  @arel
end

#assert_graph_integrityObject (readonly)

Returns the value of attribute assert_graph_integrity.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def assert_graph_integrity
  @assert_graph_integrity
end

#associationObject (readonly)

Returns the value of attribute association.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def association
  @association
end

#attributes_blacklistObject

Returns the value of attribute attributes_blacklist.



95
96
97
# File 'lib/inventory_refresh/inventory_collection.rb', line 95

def attributes_blacklist
  @attributes_blacklist
end

#attributes_whitelistObject

Returns the value of attribute attributes_whitelist.



95
96
97
# File 'lib/inventory_refresh/inventory_collection.rb', line 95

def attributes_whitelist
  @attributes_whitelist
end

#batch_extra_attributesObject (readonly)

Returns the value of attribute batch_extra_attributes.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def batch_extra_attributes
  @batch_extra_attributes
end

#check_changedObject (readonly)

Returns the value of attribute check_changed.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def check_changed
  @check_changed
end

#completeObject (readonly)

Returns the value of attribute complete.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def complete
  @complete
end

#create_onlyObject (readonly)

Returns the value of attribute create_only.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def create_only
  @create_only
end

#created_recordsObject (readonly)

Returns the value of attribute created_records.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def created_records
  @created_records
end

#custom_reconnect_blockObject (readonly)

Returns the value of attribute custom_reconnect_block.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def custom_reconnect_block
  @custom_reconnect_block
end

#custom_save_blockObject (readonly)

Returns the value of attribute custom_save_block.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def custom_save_block
  @custom_save_block
end

#data_collection_finalizedBoolean

Returns A true value marks that we collected all the data of the InventoryCollection, meaning we also collected all the references.

Returns:

  • (Boolean)

    A true value marks that we collected all the data of the InventoryCollection, meaning we also collected all the references.



63
64
65
# File 'lib/inventory_refresh/inventory_collection.rb', line 63

def data_collection_finalized
  @data_collection_finalized
end

#data_storageInventoryRefresh::InventoryCollection::DataStorage

Returns An InventoryCollection encapsulating all data with indexes.

Returns:



67
68
69
# File 'lib/inventory_refresh/inventory_collection.rb', line 67

def data_storage
  @data_storage
end

#default_valuesObject (readonly)

Returns the value of attribute default_values.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def default_values
  @default_values
end

#delete_methodObject (readonly)

Returns the value of attribute delete_method.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def delete_method
  @delete_method
end

#deleted_recordsObject (readonly)

Returns the value of attribute deleted_records.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def deleted_records
  @deleted_records
end

#dependeesSet

Returns A set of InventoryCollection objects that depends on this InventoryCollection object.

Returns:

  • (Set)

    A set of InventoryCollection objects that depends on this InventoryCollection object.



89
90
91
# File 'lib/inventory_refresh/inventory_collection.rb', line 89

def dependees
  @dependees
end

#dependency_attributesObject (readonly)

Returns the value of attribute dependency_attributes.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def dependency_attributes
  @dependency_attributes
end

#internal_attributesObject (readonly)

Returns the value of attribute internal_attributes.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def internal_attributes
  @internal_attributes
end

#inventory_object_attributesObject (readonly)

Returns the value of attribute inventory_object_attributes.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def inventory_object_attributes
  @inventory_object_attributes
end

#manager_refObject (readonly)

Returns the value of attribute manager_ref.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def manager_ref
  @manager_ref
end

#manager_ref_allowed_nilObject (readonly)

Returns the value of attribute manager_ref_allowed_nil.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def manager_ref_allowed_nil
  @manager_ref_allowed_nil
end

#model_classObject (readonly)

Returns the value of attribute model_class.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def model_class
  @model_class
end

#nameObject (readonly)

Returns the value of attribute name.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def name
  @name
end

#parentObject (readonly)

Returns the value of attribute parent.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def parent
  @parent
end

#parent_inventory_collectionsArray<Symbol>

Returns @see #parent_inventory_collections documentation of InventoryCollection.new’s initialize_ic_relations() parameters.

Returns:

  • (Array<Symbol>)

    @see #parent_inventory_collections documentation of InventoryCollection.new’s initialize_ic_relations() parameters



93
94
95
# File 'lib/inventory_refresh/inventory_collection.rb', line 93

def parent_inventory_collections
  @parent_inventory_collections
end

#references_storageObject (readonly)

Returns the value of attribute references_storage.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def references_storage
  @references_storage
end

#retention_strategyObject (readonly)

Returns the value of attribute retention_strategy.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def retention_strategy
  @retention_strategy
end

#savedBoolean

Returns true if this collection is already saved into the DB. E.g. InventoryCollections with DB only strategy are marked as saved. This causes InventoryCollection not being a dependency for any other InventoryCollection, since it is already persisted into the DB.

Returns:

  • (Boolean)

    true if this collection is already saved into the DB. E.g. InventoryCollections with DB only strategy are marked as saved. This causes InventoryCollection not being a dependency for any other InventoryCollection, since it is already persisted into the DB.



72
73
74
# File 'lib/inventory_refresh/inventory_collection.rb', line 72

def saved
  @saved
end

#saver_strategyObject (readonly)

Returns the value of attribute saver_strategy.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def saver_strategy
  @saver_strategy
end

#strategyObject (readonly)

Returns the value of attribute strategy.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def strategy
  @strategy
end

#targetedObject (readonly)

Returns the value of attribute targeted.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def targeted
  @targeted
end

#targeted_arelObject (readonly)

Returns the value of attribute targeted_arel.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def targeted_arel
  @targeted_arel
end

#targeted_scopeObject (readonly)

Returns the value of attribute targeted_scope.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def targeted_scope
  @targeted_scope
end

#transitive_dependency_attributesObject (readonly)

Returns the value of attribute transitive_dependency_attributes.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def transitive_dependency_attributes
  @transitive_dependency_attributes
end

#unconnected_edgesObject (readonly)

Returns the value of attribute unconnected_edges.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def unconnected_edges
  @unconnected_edges
end

#update_onlyObject (readonly)

Returns the value of attribute update_only.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def update_only
  @update_only
end

#updated_recordsObject (readonly)

Returns the value of attribute updated_records.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def updated_records
  @updated_records
end

#use_ar_objectObject (readonly)

Returns the value of attribute use_ar_object.



97
98
99
# File 'lib/inventory_refresh/inventory_collection.rb', line 97

def use_ar_object
  @use_ar_object
end

Instance Method Details

#base_class_nameString

Returns Base class name of the model_class of this InventoryCollection.

Returns:

  • (String)

    Base class name of the model_class of this InventoryCollection



445
446
447
448
449
# File 'lib/inventory_refresh/inventory_collection.rb', line 445

def base_class_name
  return "" unless model_class

  @base_class_name ||= model_class.base_class.name
end

#base_columnsObject



305
306
307
# File 'lib/inventory_refresh/inventory_collection.rb', line 305

def base_columns
  @base_columns ||= (unique_index_columns + internal_columns + not_null_columns).uniq
end

#batch_sizeInteger

Returns default batch size for talking to the DB.

Returns:

  • (Integer)

    default batch size for talking to the DB



469
470
471
472
# File 'lib/inventory_refresh/inventory_collection.rb', line 469

def batch_size
  # TODO(lsmola) mode to the settings
  1000
end

#batch_size_pure_sqlInteger

Returns default batch size for talking to the DB if not using ApplicationRecord objects.

Returns:

  • (Integer)

    default batch size for talking to the DB if not using ApplicationRecord objects



475
476
477
478
# File 'lib/inventory_refresh/inventory_collection.rb', line 475

def batch_size_pure_sql
  # TODO(lsmola) mode to the settings
  10_000
end

#blacklist_attributes!(attributes) ⇒ Array<Symbol>

Add passed attributes to blacklist. The manager_ref attributes cannot be blacklisted, otherwise we will not be able to identify the inventory_object. We do not automatically remove attributes causing fixed dependencies, so beware that without them, you won’t be able to create the record.

Parameters:

  • attributes (Array<Symbol>)

    Attributes we want to blacklist

Returns:

  • (Array<Symbol>)

    All blacklisted attributes



409
410
411
# File 'lib/inventory_refresh/inventory_collection.rb', line 409

def blacklist_attributes!(attributes)
  self.attributes_blacklist += attributes - (fixed_attributes + internal_attributes)
end

#build_multi_selection_condition(hashes, keys = manager_ref) ⇒ String

Builds a multiselection conditions like (table1.a = a1 AND table2.b = b1) OR (table1.a = a2 AND table2.b = b2)

Parameters:

  • hashes (Array<Hash>)

    data we want to use for the query

  • keys (Array<Symbol>) (defaults to: manager_ref)

    keys of attributes involved

Returns:

  • (String)

    A condition usable in .where of an ActiveRecord relation



496
497
498
499
500
501
# File 'lib/inventory_refresh/inventory_collection.rb', line 496

def build_multi_selection_condition(hashes, keys = manager_ref)
  arel_table = model_class.arel_table
  # We do pure SQL OR, since Arel is nesting every .or into another parentheses, otherwise this would be just
  # inject(:or) instead of to_sql with .join(" OR ")
  hashes.map { |hash| "(#{keys.map { |key| arel_table[key].eq(hash[key]) }.inject(:and).to_sql})" }.join(" OR ")
end

#cloneInventoryCollection

Returns a shallow copy of InventoryCollection, the copy will share data_storage of the original collection, otherwise we would be copying a lot of records in memory.

Returns:

  • (InventoryCollection)

    a shallow copy of InventoryCollection, the copy will share data_storage of the original collection, otherwise we would be copying a lot of records in memory.



425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/inventory_refresh/inventory_collection.rb', line 425

def clone
  cloned = self.class.new(:model_class           => model_class,
                          :manager_ref           => manager_ref,
                          :association           => association,
                          :parent                => parent,
                          :arel                  => arel,
                          :strategy              => strategy,
                          :saver_strategy        => saver_strategy,
                          :custom_save_block     => custom_save_block,
                          # We want cloned IC to be update only, since this is used for cycle resolution
                          :update_only           => true,
                          # Dependency attributes need to be a hard copy, since those will differ for each
                          # InventoryCollection
                          :dependency_attributes => dependency_attributes.clone)

  cloned.data_storage = data_storage
  cloned
end

#db_collection_for_comparisonActiveRecord::Relation

Returns A relation that can fetch all data of this InventoryCollection from the DB.

Returns:

  • (ActiveRecord::Relation)

    A relation that can fetch all data of this InventoryCollection from the DB



504
505
506
507
508
509
510
511
512
513
514
515
516
# File 'lib/inventory_refresh/inventory_collection.rb', line 504

def db_collection_for_comparison
  if targeted?
    if targeted_arel.respond_to?(:call)
      targeted_arel.call(self)
    elsif parent_inventory_collections.present?
      targeted_arel_default
    else
      targeted_iterator_for(targeted_scope.primary_references)
    end
  else
    full_collection_for_comparison
  end
end

#db_collection_for_comparison_for(references) ⇒ ActiveRecord::Relation

Builds an ActiveRecord::Relation that can fetch all the references from the DB

Parameters:

Returns:

  • (ActiveRecord::Relation)

    relation that can fetch all the references from the DB



571
572
573
# File 'lib/inventory_refresh/inventory_collection.rb', line 571

def db_collection_for_comparison_for(references)
  full_collection_for_comparison.where(targeted_selection_for(references))
end

#dependenciesArray<InventoryRefresh::InventoryCollection>

Returns all unique non saved dependencies.

Returns:



386
387
388
# File 'lib/inventory_refresh/inventory_collection.rb', line 386

def dependencies
  filtered_dependency_attributes.values.map(&:to_a).flatten.uniq.reject(&:saved?)
end

#dependency_attributes_for(inventory_collections) ⇒ Array<InventoryRefresh::InventoryCollection>

Returns what attributes are causing a dependencies to certain InventoryCollection objects.

Parameters:

Returns:



395
396
397
398
399
400
401
# File 'lib/inventory_refresh/inventory_collection.rb', line 395

def dependency_attributes_for(inventory_collections)
  attributes = Set.new
  inventory_collections.each do |inventory_collection|
    attributes += filtered_dependency_attributes.select { |_key, value| value.include?(inventory_collection) }.keys
  end
  attributes
end

#filtered_dependency_attributesHash{Symbol => Set}

List attributes causing a dependency and filters them by attributes_blacklist and attributes_whitelist

Returns:

  • (Hash{Symbol => Set})

    attributes causing a dependency and filtered by blacklist and whitelist



345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/inventory_refresh/inventory_collection.rb', line 345

def filtered_dependency_attributes
  filtered_attributes = dependency_attributes

  if attributes_blacklist.present?
    filtered_attributes = filtered_attributes.reject { |key, _value| attributes_blacklist.include?(key) }
  end

  if attributes_whitelist.present?
    filtered_attributes = filtered_attributes.select { |key, _value| attributes_whitelist.include?(key) }
  end

  filtered_attributes
end

#fixed_attributesArray<Symbol>

Attributes that are needed to be able to save the record, i.e. attributes that are part of the unique index and attributes with presence validation or NOT NULL constraint

Returns:

  • (Array<Symbol>)

    attributes that are needed for saving of the record



363
364
365
366
367
368
369
370
371
372
# File 'lib/inventory_refresh/inventory_collection.rb', line 363

def fixed_attributes
  if model_class
    presence_validators = model_class.validators.detect { |x| x.kind_of?(ActiveRecord::Validations::PresenceValidator) }
  end
  # Attributes that has to be always on the entity, so attributes making unique index of the record + attributes
  # that have presence validation
  fixed_attributes = manager_ref
  fixed_attributes += presence_validators.attributes if presence_validators.present?
  fixed_attributes
end

#fixed_dependenciesObject

Returns fixed dependencies, which are the ones we can’t move, because we wouldn’t be able to save the data



377
378
379
380
381
382
383
# File 'lib/inventory_refresh/inventory_collection.rb', line 377

def fixed_dependencies
  fixed_attrs = fixed_attributes

  filtered_dependency_attributes.each_with_object(Set.new) do |(key, value), fixed_deps|
    fixed_deps.merge(value) if fixed_attrs.include?(key)
  end.reject(&:saved?)
end

#full_collection_for_comparisonActiveRecord::Relation

Returns relation that can fetch all the references from the DB.

Returns:

  • (ActiveRecord::Relation)

    relation that can fetch all the references from the DB



576
577
578
579
580
581
582
# File 'lib/inventory_refresh/inventory_collection.rb', line 576

def full_collection_for_comparison
  return arel unless arel.nil?

  rel = parent.send(association)
  rel = rel.active if rel && supports_column?(:archived_at) && retention_strategy == :archive
  rel
end

#inspectString

Returns a concise form of the InventoryCollection for easy logging.

Returns:

  • (String)

    a concise form of the InventoryCollection for easy logging



464
465
466
# File 'lib/inventory_refresh/inventory_collection.rb', line 464

def inspect
  to_s
end

#internal_columnsObject



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/inventory_refresh/inventory_collection.rb', line 276

def internal_columns
  return @internal_columns if @internal_columns

  @internal_columns = [] + internal_timestamp_columns
  @internal_columns << :type if supports_sti?
  @internal_columns += [resource_version_column,
                        :resource_timestamps_max,
                        :resource_timestamps,
                        :resource_timestamp,
                        :resource_counters_max,
                        :resource_counters,
                        :resource_counter].collect do |col|
    col if supports_column?(col)
  end.compact
end

#internal_timestamp_columnsObject



292
293
294
295
296
297
298
# File 'lib/inventory_refresh/inventory_collection.rb', line 292

def internal_timestamp_columns
  return @internal_timestamp_columns if @internal_timestamp_columns

  @internal_timestamp_columns = %i[created_at created_on updated_at updated_on].collect do |timestamp_col|
    timestamp_col if supports_column?(timestamp_col)
  end.compact
end

#inventory_object?(value) ⇒ Boolean

Returns true is value is kind of InventoryRefresh::InventoryObject.

Parameters:

  • value (Object)

    Object we want to test

Returns:

  • (Boolean)

    true is value is kind of InventoryRefresh::InventoryObject



311
312
313
# File 'lib/inventory_refresh/inventory_collection.rb', line 311

def inventory_object?(value)
  value.kind_of?(::InventoryRefresh::InventoryObject)
end

#inventory_object_lazy?(value) ⇒ Boolean

Returns true is value is kind of InventoryRefresh::InventoryObjectLazy.

Parameters:

  • value (Object)

    Object we want to test

Returns:

  • (Boolean)

    true is value is kind of InventoryRefresh::InventoryObjectLazy



317
318
319
# File 'lib/inventory_refresh/inventory_collection.rb', line 317

def inventory_object_lazy?(value)
  value.kind_of?(::InventoryRefresh::InventoryObjectLazy)
end

#manager_ref_to_colsArray<String>

Convert manager_ref list of attributes to list of DB columns

Returns:

  • (Array<String>)

    true is processing of this InventoryCollection will be in targeted mode



334
335
336
337
338
339
340
# File 'lib/inventory_refresh/inventory_collection.rb', line 334

def manager_ref_to_cols
  # TODO(lsmola) this should contain the polymorphic _type, otherwise the IC with polymorphic unique key will get
  # conflicts
  manager_ref.map do |ref|
    association_to_foreign_key_mapping[ref] || ref
  end
end

#manager_uuidsArray<String>

Returns a list of stringified uuids of all scoped InventoryObjects, which is used for scoping in targeted mode

Returns:

  • (Array<String>)

    list of stringified uuids of all scoped InventoryObjects



483
484
485
486
487
488
489
# File 'lib/inventory_refresh/inventory_collection.rb', line 483

def manager_uuids
  # TODO(lsmola) LEGACY: this is still being used by :targetel_arel definitions and it expects array of strings
  raise "This works only for :manager_ref size 1" if manager_ref.size > 1

  key = manager_ref.first
  transform_references_to_hashes(targeted_scope.primary_references).map { |x| x[key] }
end

#new_inventory_object(hash) ⇒ InventoryRefresh::InventoryObject

Creates InventoryRefresh::InventoryObject object from passed hash data

Parameters:

  • hash (Hash)

    Object data

Returns:



588
589
590
591
592
593
594
595
# File 'lib/inventory_refresh/inventory_collection.rb', line 588

def new_inventory_object(hash)
  manager_ref.each do |x|
    # TODO(lsmola) with some effort, we can do this, but it's complex
    raise "A lazy_find with a :key can't be a part of the manager_uuid" if inventory_object_lazy?(hash[x]) && hash[x].key
  end

  inventory_object_class.new(self, hash)
end

#not_null_columnsArray

Returns Array of column names that have not null constraint.

Returns:

  • (Array)

    Array of column names that have not null constraint



301
302
303
# File 'lib/inventory_refresh/inventory_collection.rb', line 301

def not_null_columns
  @not_null_constraint_columns ||= model_class.columns.reject(&:null).map { |x| x.name.to_sym } - [model_class.primary_key.to_sym]
end

#object_index_with_keys(keys, record) ⇒ String

Builds string uuid from passed Object and keys

Parameters:

  • keys (Array<Symbol>)

    Indexes into the Hash data

  • record (ApplicationRecord)

    ActiveRecord record

Returns:

  • (String)

    Concatenated values on keys from data



326
327
328
329
# File 'lib/inventory_refresh/inventory_collection.rb', line 326

def object_index_with_keys(keys, record)
  # TODO(lsmola) remove, last usage is in k8s reconnect logic
  build_stringified_reference_for_record(record, keys)
end

#resource_version_columnObject



272
273
274
# File 'lib/inventory_refresh/inventory_collection.rb', line 272

def resource_version_column
  :resource_version
end

#store_created_records(records) ⇒ Object

Caches what records were created, for later use, e.g. post provision behavior

Parameters:

  • records (Array<ApplicationRecord, Hash>)

    list of stored records



199
200
201
# File 'lib/inventory_refresh/inventory_collection.rb', line 199

def store_created_records(records)
  @created_records.concat(records_identities(records))
end

#store_deleted_records(records) ⇒ Object

Caches what records were deleted/soft-deleted, for later use, e.g. post provision behavior

Parameters:

  • records (Array<ApplicationRecord, Hash>)

    list of stored records



213
214
215
# File 'lib/inventory_refresh/inventory_collection.rb', line 213

def store_deleted_records(records)
  @deleted_records.concat(records_identities(records))
end

#store_unconnected_edges(inventory_object, inventory_object_key, inventory_object_lazy) ⇒ Object



189
190
191
192
193
194
# File 'lib/inventory_refresh/inventory_collection.rb', line 189

def store_unconnected_edges(inventory_object, inventory_object_key, inventory_object_lazy)
  (@unconnected_edges ||= []) <<
    InventoryRefresh::InventoryCollection::UnconnectedEdge.new(
      inventory_object, inventory_object_key, inventory_object_lazy
    )
end

#store_updated_records(records) ⇒ Object

Caches what records were updated, for later use, e.g. post provision behavior

Parameters:

  • records (Array<ApplicationRecord, Hash>)

    list of stored records



206
207
208
# File 'lib/inventory_refresh/inventory_collection.rb', line 206

def store_updated_records(records)
  @updated_records.concat(records_identities(records))
end

#targeted_arel_defaultInventoryRefresh::ApplicationRecordIterator

Builds targeted query limiting the results by the :references defined in parent_inventory_collections

Returns:



521
522
523
524
525
526
527
528
529
530
# File 'lib/inventory_refresh/inventory_collection.rb', line 521

def targeted_arel_default
  if parent_inventory_collections.collect { |x| x.model_class.base_class }.uniq.count > 1
    raise "Multiple :parent_inventory_collections with different base class are not supported by default. Write "\
          ":targeted_arel manually, or separate [#{self}] into 2 InventoryCollection objects."
  end
  parent_collection = parent_inventory_collections.first
  references        = parent_inventory_collections.map { |x| x.targeted_scope.primary_references }.reduce({}, :merge!)

  parent_collection.targeted_iterator_for(references, full_collection_for_comparison)
end

#targeted_iterator_for(references, query = nil) ⇒ InventoryRefresh::ApplicationRecordIterator

Returns iterator for the passed references and a query

Parameters:

Returns:



559
560
561
562
563
564
565
# File 'lib/inventory_refresh/inventory_collection.rb', line 559

def targeted_iterator_for(references, query = nil)
  InventoryRefresh::ApplicationRecordIterator.new(
    :inventory_collection => self,
    :manager_uuids_set    => references,
    :query                => query
  )
end

#targeted_selection_for(references) ⇒ String

Builds a multiselection conditions like (table1.a = a1 AND table2.b = b1) OR (table1.a = a2 AND table2.b = b2) for passed references

Parameters:

Returns:

  • (String)

    A condition usable in .where of an ActiveRecord relation



550
551
552
# File 'lib/inventory_refresh/inventory_collection.rb', line 550

def targeted_selection_for(references)
  build_multi_selection_condition(transform_references_to_hashes(references))
end

#to_sString

Returns a concise form of the inventoryCollection for easy logging.

Returns:

  • (String)

    a concise form of the inventoryCollection for easy logging



452
453
454
455
456
457
458
459
460
461
# File 'lib/inventory_refresh/inventory_collection.rb', line 452

def to_s
  whitelist = ", whitelist: [#{attributes_whitelist.to_a.join(", ")}]" if attributes_whitelist.present?
  blacklist = ", blacklist: [#{attributes_blacklist.to_a.join(", ")}]" if attributes_blacklist.present?

  strategy_name = ", strategy: #{strategy}" if strategy

  name = model_class || association

  "InventoryCollection:<#{name}>#{whitelist}#{blacklist}#{strategy_name}"
end

#transform_references_to_hashes(references) ⇒ Array<Hash>

Gets targeted references and transforms them into list of hashes

Parameters:

  • references (Array, InventoryRefresh::InventoryCollection::TargetedScope)

    passed references

Returns:

  • (Array<Hash>)

    References transformed into the array of hashes



536
537
538
539
540
541
542
543
# File 'lib/inventory_refresh/inventory_collection.rb', line 536

def transform_references_to_hashes(references)
  if references.kind_of?(Array)
    # Sliced InventoryRefresh::InventoryCollection::TargetedScope
    references.map { |x| x.second.full_reference }
  else
    references.values.map(&:full_reference)
  end
end

#uniq_keys_candidates(keys) ⇒ Array<ActiveRecord::ConnectionAdapters::IndexDefinition>

Find candidates for unique key. Candidate must cover all columns we are passing as keys.

Parameters:

  • keys (Array<Symbol>)

Returns:

  • (Array<ActiveRecord::ConnectionAdapters::IndexDefinition>)

    Array of unique indexes fitting the keys

Raises:

  • (Exception)

    if the unique index for the columns was not found



260
261
262
263
264
265
266
267
268
269
270
# File 'lib/inventory_refresh/inventory_collection.rb', line 260

def uniq_keys_candidates(keys)
  # Find all uniq indexes that that are covering our keys
  uniq_key_candidates = unique_indexes.each_with_object([]) { |i, obj| obj << i if (keys - i.columns.map(&:to_sym)).empty? }

  if unique_indexes.blank? || uniq_key_candidates.blank?
    raise "#{self} and its table #{model_class.table_name} must have a unique index defined "\
          "covering columns #{keys} to be able to use saver_strategy :concurrent_safe_batch."
  end

  uniq_key_candidates
end

#unique_index_columnsArray<Symbol>

Returns all columns that are part of the best fit unique index.

Returns:

  • (Array<Symbol>)

    all columns that are part of the best fit unique index



218
219
220
221
222
# File 'lib/inventory_refresh/inventory_collection.rb', line 218

def unique_index_columns
  return @unique_index_columns if @unique_index_columns

  @unique_index_columns = unique_index_for(unique_index_keys).columns.map(&:to_sym)
end

#unique_index_for(keys) ⇒ ActiveRecord::ConnectionAdapters::IndexDefinition

Finds an index that fits the list of columns (keys) the best

Parameters:

  • keys (Array<Symbol>)

Returns:

  • (ActiveRecord::ConnectionAdapters::IndexDefinition)

    unique index fitting the keys

Raises:

  • (Exception)

    if the unique index for the columns was not found



247
248
249
250
251
252
253
# File 'lib/inventory_refresh/inventory_collection.rb', line 247

def unique_index_for(keys)
  @unique_index_for_keys_cache ||= {}
  return @unique_index_for_keys_cache[keys] if @unique_index_for_keys_cache[keys]

  # Take the uniq key having the least number of columns
  @unique_index_for_keys_cache[keys] = uniq_keys_candidates(keys).min_by { |x| x.columns.count }
end

#unique_index_keysObject



224
225
226
# File 'lib/inventory_refresh/inventory_collection.rb', line 224

def unique_index_keys
  @unique_index_keys ||= manager_ref_to_cols.map(&:to_sym)
end

#unique_indexesArray<ActiveRecord::ConnectionAdapters::IndexDefinition>

Returns array of all unique indexes known to model.

Returns:

  • (Array<ActiveRecord::ConnectionAdapters::IndexDefinition>)

    array of all unique indexes known to model



229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/inventory_refresh/inventory_collection.rb', line 229

def unique_indexes
  return @unique_indexes if @unique_indexes

  @unique_indexes = model_class.connection.indexes(model_class.table_name).select(&:unique)

  if @unique_indexes.blank?
    raise "#{self} and its table #{model_class.table_name} must have a unique index defined, to"\
          " be able to use saver_strategy :concurrent_safe_batch."
  end

  @unique_indexes
end

#whitelist_attributes!(attributes) ⇒ Array<Symbol>

Add passed attributes to whitelist. The manager_ref attributes always needs to be in the white list, otherwise we will not be able to identify theinventory_object. We do not automatically add attributes causing fixed dependencies, so beware that without them, you won’t be able to create the record.

Parameters:

  • attributes (Array<Symbol>)

    Attributes we want to whitelist

Returns:

  • (Array<Symbol>)

    All whitelisted attributes



419
420
421
# File 'lib/inventory_refresh/inventory_collection.rb', line 419

def whitelist_attributes!(attributes)
  self.attributes_whitelist += attributes + (fixed_attributes + internal_attributes)
end