diff --git a/lib/diaspora_federation/entity.rb b/lib/diaspora_federation/entity.rb index 7c34d2a..07e9acc 100644 --- a/lib/diaspora_federation/entity.rb +++ b/lib/diaspora_federation/entity.rb @@ -34,6 +34,7 @@ module DiasporaFederation # are intended to be immutable data containers, only. class Entity extend PropertiesDSL + include Logging # Initializes the Entity with the given attribute hash and freezes the created # instance it returns. @@ -50,15 +51,14 @@ module DiasporaFederation # @param [Hash] data entity data # @return [Entity] new instance def initialize(data) + logger.debug "create entity #{self.class} with data: #{data}" raise ArgumentError, "expected a Hash" unless data.is_a?(Hash) + entity_data = self.class.resolv_aliases(data) - missing_props = self.class.missing_props(entity_data) - unless missing_props.empty? - raise ArgumentError, "missing required properties: #{missing_props.join(', ')}" - end + validate_missing_props(entity_data) self.class.default_values.merge(entity_data).each do |name, value| - instance_variable_set("@#{name}", nilify(value)) if setable?(name, value) + instance_variable_set("@#{name}", instantiate_nested(name, nilify(value))) if setable?(name, value) end freeze @@ -147,6 +147,11 @@ module DiasporaFederation private + def validate_missing_props(entity_data) + missing_props = self.class.missing_props(entity_data) + raise ArgumentError, "missing required properties: #{missing_props.join(', ')}" unless missing_props.empty? + end + def setable?(name, val) type = self.class.class_props[name] return false if type.nil? # property undefined @@ -159,11 +164,12 @@ module DiasporaFederation end def setable_nested?(type, val) - type.instance_of?(Class) && type.ancestors.include?(Entity) && val.is_a?(Entity) + type.instance_of?(Class) && type.ancestors.include?(Entity) && (val.is_a?(Entity) || val.is_a?(Hash)) end def setable_multi?(type, val) - type.instance_of?(Array) && val.instance_of?(Array) && val.all? {|v| v.instance_of?(type.first) } + type.instance_of?(Array) && val.instance_of?(Array) && + (val.all? {|v| v.instance_of?(type.first) } || val.all? {|v| v.instance_of?(Hash) }) end def nilify(value) @@ -171,6 +177,17 @@ module DiasporaFederation value end + def instantiate_nested(name, value) + if value.instance_of?(Array) + return value unless value.first.instance_of?(Hash) + value.map {|hash| self.class.class_props[name].first.new(hash) } + elsif value.instance_of?(Hash) + self.class.class_props[name].new(value) + else + value + end + end + def validate validator_name = "#{self.class.name.split('::').last}Validator" return unless Validators.const_defined? validator_name diff --git a/spec/lib/diaspora_federation/entity_spec.rb b/spec/lib/diaspora_federation/entity_spec.rb index c63457a..f770815 100644 --- a/spec/lib/diaspora_federation/entity_spec.rb +++ b/spec/lib/diaspora_federation/entity_spec.rb @@ -250,15 +250,16 @@ XML multi: [Entities::OtherEntity.new(asdf: "asdf"), Entities::OtherEntity.new(asdf: "asdf")] } } - - it "gets returned as Hash by #to_h" do - entity = Entities::TestNestedEntity.new(nested_data) - - nested_hash = { + let(:nested_hash) { + { asdf: nested_data[:asdf], test: nested_data[:test].to_h, multi: nested_data[:multi].map(&:to_h) } + } + + it "gets returned as Hash by #to_h" do + entity = Entities::TestNestedEntity.new(nested_data) expect(entity.to_h).to eq(nested_hash) end @@ -273,6 +274,18 @@ XML expect(xml.xpath("test_entity")).to have_exactly(1).items expect(xml.xpath("other_entity")).to have_exactly(2).items end + + it "instantiates nested entities if provided as hash" do + entity = Entities::TestNestedEntity.new(nested_hash) + + expect(entity.test).to be_instance_of(Entities::TestEntity) + expect(entity.test.test).to eq("test") + + expect(entity.multi).to be_instance_of(Array) + expect(entity.multi).to have_exactly(2).items + expect(entity.multi.first).to be_instance_of(Entities::OtherEntity) + expect(entity.multi.first.asdf).to eq("asdf") + end end context "xml_name" do