inventory_refresh 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.codeclimate.yml +47 -0
- data/.gitignore +13 -0
- data/.rspec +4 -0
- data/.rspec_ci +4 -0
- data/.rubocop.yml +4 -0
- data/.rubocop_cc.yml +5 -0
- data/.rubocop_local.yml +2 -0
- data/.travis.yml +12 -0
- data/.yamllint +12 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +6 -0
- data/LICENSE +202 -0
- data/README.md +35 -0
- data/Rakefile +47 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/inventory_refresh.gemspec +34 -0
- data/lib/inventory_refresh.rb +11 -0
- data/lib/inventory_refresh/application_record_iterator.rb +56 -0
- data/lib/inventory_refresh/application_record_reference.rb +15 -0
- data/lib/inventory_refresh/graph.rb +157 -0
- data/lib/inventory_refresh/graph/topological_sort.rb +66 -0
- data/lib/inventory_refresh/inventory_collection.rb +1175 -0
- data/lib/inventory_refresh/inventory_collection/data_storage.rb +178 -0
- data/lib/inventory_refresh/inventory_collection/graph.rb +170 -0
- data/lib/inventory_refresh/inventory_collection/index/proxy.rb +230 -0
- data/lib/inventory_refresh/inventory_collection/index/type/base.rb +80 -0
- data/lib/inventory_refresh/inventory_collection/index/type/data.rb +26 -0
- data/lib/inventory_refresh/inventory_collection/index/type/local_db.rb +286 -0
- data/lib/inventory_refresh/inventory_collection/index/type/skeletal.rb +116 -0
- data/lib/inventory_refresh/inventory_collection/reference.rb +96 -0
- data/lib/inventory_refresh/inventory_collection/references_storage.rb +106 -0
- data/lib/inventory_refresh/inventory_collection/scanner.rb +117 -0
- data/lib/inventory_refresh/inventory_collection/serialization.rb +140 -0
- data/lib/inventory_refresh/inventory_object.rb +303 -0
- data/lib/inventory_refresh/inventory_object_lazy.rb +151 -0
- data/lib/inventory_refresh/save_collection/base.rb +38 -0
- data/lib/inventory_refresh/save_collection/recursive.rb +52 -0
- data/lib/inventory_refresh/save_collection/saver/base.rb +390 -0
- data/lib/inventory_refresh/save_collection/saver/batch.rb +17 -0
- data/lib/inventory_refresh/save_collection/saver/concurrent_safe.rb +71 -0
- data/lib/inventory_refresh/save_collection/saver/concurrent_safe_batch.rb +632 -0
- data/lib/inventory_refresh/save_collection/saver/default.rb +57 -0
- data/lib/inventory_refresh/save_collection/saver/sql_helper.rb +85 -0
- data/lib/inventory_refresh/save_collection/saver/sql_helper_update.rb +120 -0
- data/lib/inventory_refresh/save_collection/saver/sql_helper_upsert.rb +196 -0
- data/lib/inventory_refresh/save_collection/topological_sort.rb +38 -0
- data/lib/inventory_refresh/save_inventory.rb +38 -0
- data/lib/inventory_refresh/target.rb +73 -0
- data/lib/inventory_refresh/target_collection.rb +80 -0
- data/lib/inventory_refresh/version.rb +3 -0
- data/tools/ci/create_db_user.sh +3 -0
- metadata +207 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
require "active_support/core_ext/module/delegation"
|
2
|
+
|
3
|
+
module InventoryRefresh
|
4
|
+
class InventoryCollection
|
5
|
+
class Reference
|
6
|
+
attr_reader :full_reference, :keys, :ref, :stringified_reference
|
7
|
+
|
8
|
+
delegate :[], :to => :full_reference
|
9
|
+
|
10
|
+
# @param data [Hash, String] Data needed for creating the reference
|
11
|
+
# @param ref [String] Name of the reference (and of the index associated)
|
12
|
+
# @param keys [Array<Symbol>] Attribute/column names of the reference, that are used as indexes of the passed
|
13
|
+
# data hash
|
14
|
+
def initialize(data, ref, keys)
|
15
|
+
@full_reference = build_full_reference(data, keys)
|
16
|
+
@ref = ref
|
17
|
+
@keys = keys
|
18
|
+
|
19
|
+
@nested_secondary_index = keys.select { |key| full_reference[key].kind_of?(InventoryRefresh::InventoryObjectLazy) }.any? do |key|
|
20
|
+
!full_reference[key].primary?
|
21
|
+
end
|
22
|
+
|
23
|
+
@stringified_reference = self.class.build_stringified_reference(full_reference, keys)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return true if reference is to primary index, false otherwise. Reference is primary, only if all the nested
|
27
|
+
# references are primary.
|
28
|
+
#
|
29
|
+
# @return [Boolean] true if reference is to primary index, false otherwise
|
30
|
+
def primary?
|
31
|
+
ref == :manager_ref && !nested_secondary_index
|
32
|
+
end
|
33
|
+
|
34
|
+
def nested_secondary_index?
|
35
|
+
nested_secondary_index
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
# Builds string uuid from passed Hash and keys
|
40
|
+
#
|
41
|
+
# @param hash [Hash] Hash data
|
42
|
+
# @param keys [Array<Symbol>] Indexes into the Hash data
|
43
|
+
# @return [String] Concatenated values on keys from data
|
44
|
+
def build_stringified_reference(hash, keys)
|
45
|
+
stringify_reference(keys.map { |attribute| hash[attribute].to_s })
|
46
|
+
end
|
47
|
+
|
48
|
+
# Builds string uuid from passed Object and keys
|
49
|
+
#
|
50
|
+
# @param record [ApplicationRecord] ActiveRecord record
|
51
|
+
# @param keys [Array<Symbol>] Indexes into the Hash data
|
52
|
+
# @return [String] Concatenated values on keys from data
|
53
|
+
def build_stringified_reference_for_record(record, keys)
|
54
|
+
stringify_reference(keys.map { |attribute| record.public_send(attribute).to_s })
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns passed array joined by stringify_joiner
|
58
|
+
#
|
59
|
+
# @param reference [Array<String>]
|
60
|
+
# @return [String] Passed array joined by stringify_joiner
|
61
|
+
def stringify_reference(reference)
|
62
|
+
reference.join(stringify_joiner)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Returns joiner for string UIID
|
68
|
+
#
|
69
|
+
# @return [String] Joiner for string UIID
|
70
|
+
def stringify_joiner
|
71
|
+
"__"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# @return Returns true if reference has nested references that are not pointing to primary index, but to
|
78
|
+
# secondary indexes.
|
79
|
+
attr_reader :nested_secondary_index
|
80
|
+
|
81
|
+
# Returns original Hash, or build hash out of passed string
|
82
|
+
#
|
83
|
+
# @param data [Hash, String] Passed data
|
84
|
+
# @param keys [Array<Symbol>] Keys for the reference
|
85
|
+
# @return [Hash] Original Hash, or build hash out of passed string
|
86
|
+
def build_full_reference(data, keys)
|
87
|
+
if data.kind_of?(Hash)
|
88
|
+
data
|
89
|
+
else
|
90
|
+
raise "Please provide Hash as a reference, :manager_ref count includes more than 1 attribute. keys: #{keys}, data: #{data}" if keys.size > 1
|
91
|
+
{keys.first => data}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module InventoryRefresh
|
2
|
+
class InventoryCollection
|
3
|
+
class ReferencesStorage
|
4
|
+
# @return [Hash] A set of InventoryObjects manager_uuids, which tells us which InventoryObjects were
|
5
|
+
# referenced by other InventoryObjects using a lazy_find.
|
6
|
+
attr_reader :references
|
7
|
+
|
8
|
+
# @return [Set] A set of InventoryObject attributes names, which tells us InventoryObject attributes
|
9
|
+
# were referenced by other InventoryObject objects using a lazy_find with :key.
|
10
|
+
attr_reader :attribute_references
|
11
|
+
|
12
|
+
def initialize(index_proxy)
|
13
|
+
@index_proxy = index_proxy
|
14
|
+
@references = {}
|
15
|
+
@references[primary_index_ref] = {}
|
16
|
+
@attribute_references = Set.new
|
17
|
+
end
|
18
|
+
|
19
|
+
# Adds reference to the storage. The reference can be already existing, otherwise we attempt to build it.
|
20
|
+
#
|
21
|
+
# @param reference_data [InventoryRefresh::InventoryCollection::References, Hash, Object] Either existing Reference
|
22
|
+
# object, or data we will build the reference object from. For InventoryCollection with :manager_ref size
|
23
|
+
# bigger than 1, it's required to pass a Hash.
|
24
|
+
# @param key [String] If the reference comes from a InventoryObjectLazy, pointing to specific attribute using :key
|
25
|
+
# we want to record what attribute was referenced.
|
26
|
+
# @param ref [Symbol] A key to specific reference, if it's a reference pointing to something else than primary
|
27
|
+
# index.
|
28
|
+
def add_reference(reference_data, key: nil, ref: nil)
|
29
|
+
reference = build_reference(reference_data, ref)
|
30
|
+
specific_references = references[reference.ref] ||= {}
|
31
|
+
|
32
|
+
specific_references[reference.stringified_reference] = reference
|
33
|
+
|
34
|
+
# If we access an attribute of the value, using a :key, we want to keep a track of that
|
35
|
+
attribute_references << key if key
|
36
|
+
end
|
37
|
+
|
38
|
+
# Adds reference to the storage. The reference can be already existing, otherwise we attempt to build it. This is
|
39
|
+
# simplified version of add_reference, not allowing to define :key or :ref.
|
40
|
+
#
|
41
|
+
# @param reference_data [InventoryRefresh::InventoryCollection::References, Hash, Object] Either existing Reference
|
42
|
+
# object, or data we will build the reference object from. For InventoryCollection with :manager_ref size
|
43
|
+
# bigger than 1, it's required to pass a Hash.
|
44
|
+
def <<(reference_data)
|
45
|
+
add_reference(reference_data)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Adds array of references to the storage. The reference can be already existing, otherwise we attempt to build
|
49
|
+
# it.
|
50
|
+
#
|
51
|
+
# @param references_array [Array] Array of reference objects acceptable by add_reference method.
|
52
|
+
# @param ref [Symbol] A key to specific reference, if it's a reference pointing to something else than primary
|
53
|
+
# index.
|
54
|
+
# @return [InventoryRefresh::InventoryCollection::ReferencesStorage] Returns self
|
55
|
+
def merge!(references_array, ref: nil)
|
56
|
+
references_array.each { |reference_data| add_reference(reference_data, :ref => ref) }
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [Hash{String => InventoryRefresh::InventoryCollection::Reference}] Hash of indexed Reference objects
|
61
|
+
def primary_references
|
62
|
+
references[primary_index_ref]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Builds a Reference object
|
66
|
+
#
|
67
|
+
# @param reference_data [InventoryRefresh::InventoryCollection::References, Hash, Object] Either existing Reference
|
68
|
+
# object, or data we will build the reference object from. For InventoryCollection with :manager_ref size
|
69
|
+
# bigger than 1, it's required to pass a Hash.
|
70
|
+
def build_reference(reference_data, ref = nil)
|
71
|
+
ref ||= primary_index_ref
|
72
|
+
return reference_data if reference_data.kind_of?(::InventoryRefresh::InventoryCollection::Reference)
|
73
|
+
|
74
|
+
::InventoryRefresh::InventoryCollection::Reference.new(reference_data, ref, named_ref(ref))
|
75
|
+
end
|
76
|
+
|
77
|
+
# Builds string uuid from passed Hash and keys
|
78
|
+
#
|
79
|
+
# @param hash [Hash] Hash data
|
80
|
+
# @param keys [Array<Symbol>] Indexes into the Hash data
|
81
|
+
# @return [String] Concatenated values on keys from data
|
82
|
+
def build_stringified_reference(hash, keys)
|
83
|
+
::InventoryRefresh::InventoryCollection::Reference.build_stringified_reference(hash, keys)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Builds string uuid from passed Object and keys
|
87
|
+
#
|
88
|
+
# @param record [ApplicationRecord] ActiveRecord record
|
89
|
+
# @param keys [Array<Symbol>] Indexes into the Hash data
|
90
|
+
# @return [String] Concatenated values on keys from data
|
91
|
+
def build_stringified_reference_for_record(record, keys)
|
92
|
+
::InventoryRefresh::InventoryCollection::Reference.build_stringified_reference_for_record(record, keys)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# @return [InventoryRefresh::InventoryCollection::Index::Proxy] Index::Proxy object associated to this reference
|
98
|
+
# storage
|
99
|
+
attr_reader :index_proxy
|
100
|
+
|
101
|
+
delegate :named_ref,
|
102
|
+
:primary_index_ref,
|
103
|
+
:to => :index_proxy
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require "active_support/core_ext/module/delegation"
|
2
|
+
|
3
|
+
module InventoryRefresh
|
4
|
+
class InventoryCollection
|
5
|
+
class Scanner
|
6
|
+
class << self
|
7
|
+
# Scanning inventory_collections for dependencies and references, storing the results in the inventory_collections
|
8
|
+
# themselves. Dependencies are needed for building a graph, references are needed for effective DB querying, where
|
9
|
+
# we can load all referenced objects of some InventoryCollection by one DB query.
|
10
|
+
#
|
11
|
+
# @param inventory_collections [Array<InventoryRefresh::InventoryCollection>] Array of InventoryCollection objects
|
12
|
+
def scan!(inventory_collections)
|
13
|
+
indexed_inventory_collections = inventory_collections.index_by(&:name)
|
14
|
+
|
15
|
+
inventory_collections.each do |inventory_collection|
|
16
|
+
new(inventory_collection, indexed_inventory_collections).scan!
|
17
|
+
end
|
18
|
+
|
19
|
+
inventory_collections.each do |inventory_collection|
|
20
|
+
inventory_collection.dependencies.each do |dependency|
|
21
|
+
dependency.dependees << inventory_collection
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :inventory_collection, :indexed_inventory_collections
|
28
|
+
|
29
|
+
# Boolean helpers the scanner uses from the :inventory_collection
|
30
|
+
delegate :inventory_object_lazy?,
|
31
|
+
:inventory_object?,
|
32
|
+
:targeted?,
|
33
|
+
:to => :inventory_collection
|
34
|
+
|
35
|
+
# Methods the scanner uses from the :inventory_collection
|
36
|
+
delegate :data,
|
37
|
+
:find_or_build,
|
38
|
+
:manager_ref,
|
39
|
+
:saver_strategy,
|
40
|
+
:to => :inventory_collection
|
41
|
+
|
42
|
+
# The data scanner modifies inside of the :inventory_collection
|
43
|
+
delegate :attribute_references,
|
44
|
+
:data_collection_finalized=,
|
45
|
+
:dependency_attributes,
|
46
|
+
:targeted_scope,
|
47
|
+
:parent_inventory_collections,
|
48
|
+
:parent_inventory_collections=,
|
49
|
+
:references,
|
50
|
+
:transitive_dependency_attributes,
|
51
|
+
:to => :inventory_collection
|
52
|
+
|
53
|
+
def initialize(inventory_collection, indexed_inventory_collections)
|
54
|
+
@inventory_collection = inventory_collection
|
55
|
+
@indexed_inventory_collections = indexed_inventory_collections
|
56
|
+
end
|
57
|
+
|
58
|
+
def scan!
|
59
|
+
# Scan InventoryCollection InventoryObjects and store the results inside of the InventoryCollection
|
60
|
+
data.each do |inventory_object|
|
61
|
+
scan_inventory_object!(inventory_object)
|
62
|
+
|
63
|
+
if targeted? && parent_inventory_collections.blank?
|
64
|
+
# We want to track what manager_uuids we should query from a db, for the targeted refresh
|
65
|
+
targeted_scope << inventory_object.reference
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Transform :parent_inventory_collections symbols to InventoryCollection objects
|
70
|
+
if parent_inventory_collections.present?
|
71
|
+
self.parent_inventory_collections = parent_inventory_collections.map do |inventory_collection_index|
|
72
|
+
ic = indexed_inventory_collections[inventory_collection_index]
|
73
|
+
if ic.nil?
|
74
|
+
raise "Can't find InventoryCollection #{inventory_collection_index} from #{inventory_collection}" if targeted?
|
75
|
+
else
|
76
|
+
# Add parent_inventory_collection as a dependency, so e.g. disconnect is done in a right order
|
77
|
+
(dependency_attributes[:__parent_inventory_collections] ||= Set.new) << ic
|
78
|
+
ic
|
79
|
+
end
|
80
|
+
end.compact
|
81
|
+
end
|
82
|
+
|
83
|
+
# Mark InventoryCollection as finalized aka. scanned
|
84
|
+
self.data_collection_finalized = true
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def scan_inventory_object!(inventory_object)
|
90
|
+
inventory_object.data.each do |key, value|
|
91
|
+
if value.kind_of?(Array)
|
92
|
+
value.each { |val| scan_inventory_object_attribute!(key, val) }
|
93
|
+
else
|
94
|
+
scan_inventory_object_attribute!(key, value)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def scan_inventory_object_attribute!(key, value)
|
100
|
+
return if !inventory_object_lazy?(value) && !inventory_object?(value)
|
101
|
+
value_inventory_collection = value.inventory_collection
|
102
|
+
|
103
|
+
# Storing attributes and their dependencies
|
104
|
+
(dependency_attributes[key] ||= Set.new) << value_inventory_collection if value.dependency?
|
105
|
+
|
106
|
+
# Storing a reference in the target inventory_collection, then each IC knows about all the references and can
|
107
|
+
# e.g. load all the referenced uuids from a DB
|
108
|
+
value_inventory_collection.add_reference(value.reference, :key => value.key)
|
109
|
+
|
110
|
+
if inventory_object_lazy?(value)
|
111
|
+
# Storing if attribute is a transitive dependency, so a lazy_find :key results in dependency
|
112
|
+
transitive_dependency_attributes << key if value.transitive_dependency?
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require "active_support/core_ext/module/delegation"
|
2
|
+
|
3
|
+
module InventoryRefresh
|
4
|
+
class InventoryCollection
|
5
|
+
class Serialization
|
6
|
+
delegate :all_manager_uuids,
|
7
|
+
:build,
|
8
|
+
:targeted_scope,
|
9
|
+
:data,
|
10
|
+
:inventory_object_lazy?,
|
11
|
+
:inventory_object?,
|
12
|
+
:name,
|
13
|
+
:skeletal_primary_index,
|
14
|
+
:to => :inventory_collection
|
15
|
+
|
16
|
+
attr_reader :inventory_collection
|
17
|
+
|
18
|
+
# @param inventory_collection [InventoryRefresh::InventoryCollection] InventoryCollection object we want the storage
|
19
|
+
# for
|
20
|
+
def initialize(inventory_collection)
|
21
|
+
@inventory_collection = inventory_collection
|
22
|
+
end
|
23
|
+
|
24
|
+
# Loads InventoryCollection data from it's serialized form into existing InventoryCollection object
|
25
|
+
#
|
26
|
+
# @param inventory_objects_data [Hash] Serialized InventoryCollection as Hash
|
27
|
+
# @param available_inventory_collections [Array<InventoryRefresh::InventoryCollection>] List of available
|
28
|
+
# InventoryCollection objects
|
29
|
+
def from_hash(inventory_objects_data, available_inventory_collections)
|
30
|
+
targeted_scope.merge!(inventory_objects_data["manager_uuids"].map(&:symbolize_keys!))
|
31
|
+
|
32
|
+
inventory_objects_data['data'].each do |inventory_object_data|
|
33
|
+
build(hash_to_data(inventory_object_data, available_inventory_collections).symbolize_keys!)
|
34
|
+
end
|
35
|
+
|
36
|
+
inventory_objects_data['partial_data'].each do |inventory_object_data|
|
37
|
+
skeletal_primary_index.build(hash_to_data(inventory_object_data, available_inventory_collections).symbolize_keys!)
|
38
|
+
end
|
39
|
+
|
40
|
+
# TODO(lsmola) add support for all_manager_uuids serialization
|
41
|
+
# self.all_manager_uuids = inventory_objects_data['all_manager_uuids']
|
42
|
+
end
|
43
|
+
|
44
|
+
# Serializes InventoryCollection's data storage into Array of Hashes
|
45
|
+
#
|
46
|
+
# @return [Hash] Serialized InventoryCollection object into Hash
|
47
|
+
def to_hash
|
48
|
+
{
|
49
|
+
:name => name,
|
50
|
+
# TODO(lsmola) we do not support nested references here, should we?
|
51
|
+
:manager_uuids => targeted_scope.primary_references.values.map(&:full_reference),
|
52
|
+
:all_manager_uuids => all_manager_uuids,
|
53
|
+
:data => data.map { |x| data_to_hash(x.data) },
|
54
|
+
:partial_data => skeletal_primary_index.index_data.map { |x| data_to_hash(x.data) },
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Converts InventoryRefresh::InventoryObject or InventoryRefresh::InventoryObjectLazy into Hash
|
61
|
+
#
|
62
|
+
# @param value [InventoryRefresh::InventoryObject, InventoryRefresh::InventoryObjectLazy] InventoryObject or a lazy link
|
63
|
+
# @param depth [Integer] Depth of nesting for nested lazy link
|
64
|
+
# @return [Hash] Serialized InventoryRefresh::InventoryObjectLazy
|
65
|
+
def lazy_relation_to_hash(value, depth = 0)
|
66
|
+
{
|
67
|
+
:type => "InventoryRefresh::InventoryObjectLazy",
|
68
|
+
:inventory_collection_name => value.inventory_collection.name,
|
69
|
+
:reference => data_to_hash(value.reference.full_reference, depth + 1),
|
70
|
+
:ref => value.reference.ref,
|
71
|
+
:key => value.try(:key),
|
72
|
+
:default => value.try(:default),
|
73
|
+
:transform_nested_lazy_finds => value.try(:transform_nested_lazy_finds)
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
# Converts Hash to InventoryRefresh::InventoryObjectLazy
|
78
|
+
#
|
79
|
+
# @param value [Hash] Serialized InventoryObject or a lazy link
|
80
|
+
# @param available_inventory_collections [Array<InventoryRefresh::InventoryCollection>] List of available
|
81
|
+
# InventoryCollection objects
|
82
|
+
# @param depth [Integer] Depth of nesting for nested lazy link
|
83
|
+
# @return [InventoryRefresh::InventoryObjectLazy] Lazy link created from hash
|
84
|
+
def hash_to_lazy_relation(value, available_inventory_collections, depth = 0)
|
85
|
+
inventory_collection = available_inventory_collections[value['inventory_collection_name'].try(:to_sym)]
|
86
|
+
raise "Couldn't build lazy_link #{value} the inventory_collection_name was not found" if inventory_collection.blank?
|
87
|
+
|
88
|
+
inventory_collection.lazy_find(
|
89
|
+
hash_to_data(value['reference'], available_inventory_collections, depth + 1).symbolize_keys!,
|
90
|
+
:ref => value['ref'].try(:to_sym),
|
91
|
+
:key => value['key'].try(:to_sym),
|
92
|
+
:default => value['default'],
|
93
|
+
:transform_nested_lazy_finds => value['transform_nested_lazy_finds']
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Converts Hash to attributes usable for building InventoryObject
|
98
|
+
#
|
99
|
+
# @param hash [Hash] Serialized InventoryObject data
|
100
|
+
# @param available_inventory_collections [Array<InventoryRefresh::InventoryCollection>] List of available
|
101
|
+
# InventoryCollection objects
|
102
|
+
# @param depth [Integer] Depth of nesting for nested lazy link
|
103
|
+
# @return [Hash] Hash with data usable for building InventoryObject
|
104
|
+
def hash_to_data(hash, available_inventory_collections, depth = 0)
|
105
|
+
raise "Nested lazy_relation of #{inventory_collection} is too deep, left processing: #{hash}" if depth > 20
|
106
|
+
|
107
|
+
hash.transform_values do |value|
|
108
|
+
if value.kind_of?(Hash) && value['type'] == "InventoryRefresh::InventoryObjectLazy"
|
109
|
+
hash_to_lazy_relation(value, available_inventory_collections, depth)
|
110
|
+
elsif value.kind_of?(Array) && value.first.kind_of?(Hash) && value.first['type'] == "InventoryRefresh::InventoryObjectLazy"
|
111
|
+
# TODO(lsmola) do we need to compact it sooner? What if first element is nil? On the other hand, we want to
|
112
|
+
# deprecate this Vm HABTM assignment because it's not effective
|
113
|
+
value.compact.map { |x| hash_to_lazy_relation(x, available_inventory_collections, depth) }
|
114
|
+
else
|
115
|
+
value
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Transforms data of the InventoryObject or Reference to InventoryObject into Hash
|
121
|
+
#
|
122
|
+
# @param data [Hash] Data of the InventoryObject or Reference to InventoryObject
|
123
|
+
# @param depth [Integer] Depth of nesting for nested lazy link
|
124
|
+
# @return [Hash] Serialized InventoryObject or Reference data into Hash
|
125
|
+
def data_to_hash(data, depth = 0)
|
126
|
+
raise "Nested lazy_relation of #{inventory_collection} is too deep, left processing: #{data}" if depth > 20
|
127
|
+
|
128
|
+
data.transform_values do |value|
|
129
|
+
if inventory_object_lazy?(value) || inventory_object?(value)
|
130
|
+
lazy_relation_to_hash(value, depth)
|
131
|
+
elsif value.kind_of?(Array) && (inventory_object_lazy?(value.compact.first) || inventory_object?(value.compact.first))
|
132
|
+
value.compact.map { |x| lazy_relation_to_hash(x, depth) }
|
133
|
+
else
|
134
|
+
value
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|