Skip to content

Commit

Permalink
Merge pull request #17010 from Ladas/add_inventory_object_interface_a…
Browse files Browse the repository at this point in the history
…utomatically

Add InventoryObject interface automatically
  • Loading branch information
agrare authored Apr 18, 2018
2 parents 51f97d4 + 68d996c commit 14b83a3
Showing 1 changed file with 88 additions and 0 deletions.
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

0 comments on commit 14b83a3

Please sign in to comment.