inventory_refresh 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|