Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add InventoryObject interface automatically #17010

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions app/models/manager_refresh/inventory_object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ class InventoryObject
delegate :manager_ref, :base_class_name, :model_class, :to => :inventory_collection
delegate :[], :[]=, :to => :data

# @param inventory_collection [ManagerRefresh::InventoryCollection] InventoryCollection object owning the
# InventoryObject
# @param data [Hash] Data of the InventoryObject object
def initialize(inventory_collection, data)
@inventory_collection = inventory_collection
@data = data
Expand All @@ -14,10 +17,12 @@ def initialize(inventory_collection, data)
@reference = inventory_collection.build_reference(data)
end

# @return [String] stringified reference
def manager_uuid
reference.stringified_reference
end

# @return [Hash] serialized InventoryObject into Lazy format
def to_raw_lazy_relation
{
:type => "ManagerRefresh::InventoryObjectLazy",
Expand All @@ -26,10 +31,16 @@ def to_raw_lazy_relation
}
end

# @return [ManagerRefresh::InventoryObject] returns self
def load
self
end

# Transforms InventoryObject object data into hash format with keys that are column names and resolves correct
# values of the foreign keys (even the polymorphic ones)
#
# @param inventory_collection_scope [ManagerRefresh::InventoryCollection] parent InventoryCollection object
# @return [Hash] Data in DB format
def attributes(inventory_collection_scope = nil)
# We should explicitly pass a scope, since the inventory_object can be mapped to more InventoryCollections with
# different blacklist and whitelist. The generic code always passes a scope.
Expand Down Expand Up @@ -78,6 +89,12 @@ def attributes(inventory_collection_scope = nil)
attributes_for_saving
end

# Transforms InventoryObject object data into hash format with keys that are column names and resolves correct
# values of the foreign keys (even the polymorphic ones)
#
# @param inventory_collection_scope [ManagerRefresh::InventoryCollection] parent InventoryCollection object
# @param all_attribute_keys [Array<Symbol>] Attribute keys we will modify based on object's data
# @return [Hash] Data in DB format
def attributes_with_keys(inventory_collection_scope = nil, all_attribute_keys = [])
# We should explicitly pass a scope, since the inventory_object can be mapped to more InventoryCollections with
# different blacklist and whitelist. The generic code always passes a scope.
Expand Down Expand Up @@ -120,23 +137,33 @@ def attributes_with_keys(inventory_collection_scope = nil, all_attribute_keys =
attributes_for_saving
end

# Given hash of attributes, we assign them to InventoryObject object using its public writers
#
# @param attributes [Hash] attributes we want to assign
# @return [ManagerRefresh::InventoryObject] self
def assign_attributes(attributes)
attributes.each { |k, v| public_send("#{k}=", v) }
self
end

# @return [String] stringified UUID
def to_s
manager_uuid
end

# @return [String] string format for nice logging
def inspect
"InventoryObject:('#{manager_uuid}', #{inventory_collection})"
end

# @return [TrueClass] InventoryObject object is always a dependency
def dependency?
true
end

# Adds setters and getters based on :inventory_object_attributes kwarg passed into InventoryCollection
#
# @param inventory_object_attributes [Array<Symbol>]
def self.add_attributes(inventory_object_attributes)
inventory_object_attributes.each do |attr|
define_method("#{attr}=") do |value|
Expand All @@ -151,11 +178,68 @@ def self.add_attributes(inventory_object_attributes)

private

# @return [Set] all model's writer names
def allowed_writers
return [] unless model_class

# Get all writers of a model
@allowed_writers ||= (model_class.new.methods - Object.methods).grep(/^[\w]+?\=$/).to_set
end

# @return [Set] all model's reader names
def allowed_readers
return [] unless model_class

# Get all readers inferred from writers of a model
@allowed_readers ||= allowed_writers.map { |x| x.to_s.delete("=").to_sym }.to_set
end

def method_missing(method_name, *arguments, &block)
if allowed_writers.include?(method_name)
self.class.define_data_writer(method_name)
public_send(method_name, arguments[0])
elsif allowed_readers.include?(method_name)
self.class.define_data_reader(method_name)
public_send(method_name)
else
super
end
end

def respond_to_missing?(method_name, _include_private = false)
allowed_writers.include?(method_name) || allowed_readers.include?(method_name) || super
end

def self.define_data_writer(data_key)
define_method(data_key) do |value|
public_send(:[]=, data_key.to_s.delete("=").to_sym, value)
end
end
private_class_method :define_data_writer

def self.define_data_reader(data_key)
define_method(data_key) do
public_send(:[], data_key)
end
end
private_class_method :define_data_reader

# Return true passed key representing a getter is an association
#
# @param inventory_collection_scope [ManagerRefresh::InventoryCollection]
# @param key [Symbol] key representing getter
# @return [Boolean] true if the passed key points to association
def association?(inventory_collection_scope, key)
# Is the key an association on inventory_collection_scope model class?
!inventory_collection_scope.association_to_foreign_key_mapping[key].nil?
end

# Return true if the attribute is allowed to be saved into the DB
#
# @param inventory_collection_scope [ManagerRefresh::InventoryCollection] InventoryCollection object owning the
# attribute
# @param key [Symbol] attribute name
# @return true if the attribute is allowed to be saved into the DB
def allowed?(inventory_collection_scope, key)
foreign_to_association = inventory_collection_scope.foreign_key_to_association_mapping[key] ||
inventory_collection_scope.foreign_type_to_association_mapping[key]
Expand All @@ -171,6 +255,10 @@ def allowed?(inventory_collection_scope, key)
true
end

# Return true if the object is loadable, which we determine by a list of loadable classes.
#
# @param value [Object] object we test
# @return true if the object is loadable
def loadable?(value)
value.kind_of?(::ManagerRefresh::InventoryObjectLazy) || value.kind_of?(::ManagerRefresh::InventoryObject) ||
value.kind_of?(::ManagerRefresh::ApplicationRecordReference)
Expand Down