From 20096636a9ae332f6b7c7573155a8862bd722917 Mon Sep 17 00:00:00 2001
From: Lucy Fu <lufu@redhat.com>
Date: Fri, 25 Aug 2017 10:58:06 -0400
Subject: [PATCH] Add GenericObject#property_associations to return the objects
 associations.

---
 app/models/generic_object.rb       | 23 +++++++---
 spec/models/generic_object_spec.rb | 68 ++++++++++++++++++++++++++++++
 2 files changed, 85 insertions(+), 6 deletions(-)

diff --git a/app/models/generic_object.rb b/app/models/generic_object.rb
index 0d48befe73c..7a39d998a9f 100644
--- a/app/models/generic_object.rb
+++ b/app/models/generic_object.rb
@@ -8,7 +8,7 @@ class GenericObject < ApplicationRecord
   delegate :property_attribute_defined?,
            :property_defined?,
            :type_cast,
-           :property_associations, :property_association_defined?,
+           :property_association_defined?,
            :property_methods, :property_method_defined?,
            :to => :generic_object_definition, :allow_nil => true
 
@@ -35,6 +35,12 @@ def property_attributes
     end
   end
 
+  def property_associations
+    properties.select { |k, _| property_association_defined?(k) }.each_with_object({}) do |(k, _), h|
+      h[k] = _property_getter(k)
+    end
+  end
+
   def delete_property(name)
     if !property_attribute_defined?(name) && !property_association_defined?(name)
       valid_property_names = generic_object_definition.property_attributes.keys + generic_object_definition.property_associations.keys
@@ -52,7 +58,7 @@ def add_to_property_association(name, objs)
     name = name.to_s
     properties[name] ||= []
 
-    klass = property_associations[name].constantize
+    klass = generic_object_definition.property_associations[name].constantize
     selected = objs.select { |obj| obj.kind_of?(klass) }
     properties[name] = (properties[name] + selected.pluck(:id)).uniq if selected
     save!
@@ -63,10 +69,14 @@ def delete_from_property_association(name, objs)
     name = name.to_s
     properties[name] ||= []
 
-    klass = property_associations[name].constantize
+    klass = generic_object_definition.property_associations[name].constantize
     selected = objs.select { |obj| obj.kind_of?(klass) }
-    properties[name] = properties[name] - selected.pluck(:id)
+    common_ids = properties[name] & selected.pluck(:id)
+    properties[name] = properties[name] - common_ids
+    return unless properties_changed?
+
     save!
+    klass.where(:id => common_ids).to_a
   end
 
   def inspect
@@ -75,7 +85,7 @@ def inspect
     end
 
     attributes_as_string += ["attributes: #{property_attributes}"]
-    attributes_as_string += ["associations: #{property_associations.keys}"]
+    attributes_as_string += ["associations: #{generic_object_definition.property_associations.keys}"]
     attributes_as_string += ["methods: #{property_methods}"]
 
     prefix = Kernel.instance_method(:inspect).bind(self).call.split(' ', 2).first
@@ -126,13 +136,14 @@ def _property_getter(name)
 
   def _property_setter(name, value)
     name = name.to_s
+
     val =
       if property_attribute_defined?(name)
         # property attribute is of single value, for now
         type_cast(name, value)
       elsif property_association_defined?(name)
         # property association is of multiple values
-        value.select { |v| v.kind_of?(property_associations[name].constantize) }.uniq.map(&:id)
+        value.select { |v| v.kind_of?(generic_object_definition.property_associations[name].constantize) }.uniq.map(&:id)
       end
 
     self.properties = properties.merge(name => val)
diff --git a/spec/models/generic_object_spec.rb b/spec/models/generic_object_spec.rb
index 2998a487bfd..ff28ac25f3e 100644
--- a/spec/models/generic_object_spec.rb
+++ b/spec/models/generic_object_spec.rb
@@ -159,6 +159,15 @@
       go_assoc.save!
       expect(go_assoc.vms.count).to eq(1)
     end
+
+    it 'method returns all associations' do
+      host = FactoryGirl.create(:host)
+      go_assoc.hosts = [host]
+
+      result = go_assoc.property_associations
+      expect(result["vms"]).to match_array([vm1, vm2])
+      expect(result["hosts"]).to match_array([host])
+    end
   end
 
   describe 'property methods' do
@@ -244,4 +253,63 @@
       expect { go.delete_property("some_attribute_not_defined") }.to raise_error(RuntimeError)
     end
   end
+
+  describe '#add_to_property_association' do
+    let(:new_vm) { FactoryGirl.create(:vm_vmware) }
+    subject { go.add_to_property_association("vms", vm1) }
+
+    it 'adds objects into association' do
+      subject
+      expect(go.vms.count).to eq(1)
+
+      go.add_to_property_association(:vms, [new_vm])
+      expect(go.vms.count).to eq(2)
+    end
+
+    it 'does not add duplicate object' do
+      subject
+      expect(go.vms.count).to eq(1)
+
+      subject
+      expect(go.vms.count).to eq(1)
+    end
+
+    it 'does not add object from differnt class' do
+      go.add_to_property_association("vms", FactoryGirl.create(:host))
+      expect(go.vms.count).to eq(0)
+    end
+
+    it 'does not accept object id' do
+      go.add_to_property_association(:vms, new_vm.id)
+      expect(go.vms.count).to eq(0)
+    end
+  end
+
+  describe '#delete_from_property_association' do
+    before { go.add_to_property_association("vms", [vm1]) }
+    let(:new_vm) { FactoryGirl.create(:vm_vmware) }
+
+    it 'deletes objects from association' do
+      result = go.delete_from_property_association(:vms, [vm1])
+      expect(go.vms.count).to eq(0)
+      expect(result).to match_array([vm1])
+    end
+
+    it 'does not delete object that is not in association' do
+      expect(go.vms.count).to eq(1)
+      result = go.delete_from_property_association(:vms, [new_vm])
+      expect(go.vms).to match_array([vm1])
+      expect(result).to be_nil
+    end
+
+    it 'does not delete object from differnt class' do
+      result = go.delete_from_property_association(:vms, [FactoryGirl.create(:host)])
+      expect(go.vms).to match_array([vm1])
+      expect(result).to be_nil
+    end
+
+    it 'does not accept object id' do
+      expect(go.delete_from_property_association(:vms, vm1.id)).to be_nil
+    end
+  end
 end