instantiate nested entities if they are provided as hash

also add debug logging to entity constructor
This commit is contained in:
Benjamin Neff 2016-05-30 01:33:17 +02:00
parent 4ded6da139
commit 653b0fe276
2 changed files with 42 additions and 12 deletions

View file

@ -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

View file

@ -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