EntityModel

 view release on metacpan or  search on metacpan

lib/EntityModel/Model.pm  view on Meta::CPAN

=cut

sub rollback {
	my $self = shift;
	undef $self->{pending};
	return $self;
}

=head2 apply_fields

Apply the field definitions for an entity.

=cut

sub apply_fields {
	my $self = shift;
	my $entity = shift;
	local $SIG{__DIE__} = sub {
		logStack("FAILED HERE ===== $_[0]");
		die @_;
	};
	my @fieldList = $self->read_fields($entity);
	foreach my $details (@fieldList) {
		my $field = $entity->new_field($details->{name});
		foreach (sort keys %$details) {
			$field->$_($details->{$_});
		}
		$entity->field->push($field);
	}
	return $self;
}

=head2 load_model

Populate the entity model from any information we can get from
the data source.

=cut

sub load_model {
	my $self = shift;
	$self->entity->clear;

	logDebug("Reading tables");
	my @tableList = $self->read_tables;
	logDebug("Import " . scalar(@tableList) . " tables");
	foreach (@tableList) {
		my $tbl = $self->add_table($_);
		$self->apply_fields($tbl);
	}
	logDebug("Import complete");
	return $self;
}

=head2 update_from

Update this entity model so that it matches the given model.

=cut

sub update_from {
	my ($self, $src) = @_;
	my %srcNames = map { $_->name => $_ } $src->entity->list;
	foreach my $e ($self->entity->list) {
		if(exists $srcNames{$e->name}) {
			my $es = delete $srcNames{$e->name};
# Mark this for update unless it's the same as the one we have already
			if($es->matches($e)) {
				logDebug("Should keep [%s]", $e->name);
			} else {
				logDebug("Should update [%s]", $e->name);
				$self->{pending}->{update}->{$es->name} = $es;
			}
		} else {
			logDebug("Should remove [%s]", $e->name);
			$self->{pending}->{remove}->{$e->name} = $e;
		}
	}
	foreach my $name (sort keys %srcNames) {
		logDebug("Should add [%s]", $name);
		$self->{pending}->{add}->{$name} = $srcNames{$name};
	}
	return $self;
}

=head2 matches

Returns true if this entity model has identical content to another given model.

=cut

sub matches {
	my ($self, $dst) = @_;
	my @srcList = sort { $a->name cmp $b->name } $self->entity->list;
	my @dstList = sort { $a->name cmp $b->name } $dst->entity->list;
	logDebug("Check match: src " . scalar(@srcList) . ", dest " . scalar(@dstList));
	return \@srcList ~~ \@dstList;
}

=head2 read_tables

Virtual method for reading table definitions.

=cut

sub read_tables { }

sub dump  {
	my $self = shift;
	my $out = shift || sub {
		print join(' ', @_) . "\n";
	};

	$out->('Entity list for ' . $self->name);
	foreach (sort { $a->name cmp $b->name } $self->entity) {
		$out->($_->name);
		$_->dump($out);
	}
	$self;
}

lib/EntityModel/Model.pm  view on Meta::CPAN


sub commit_pending_remove {
	my $self = shift;
	logInfo("Remove " . join(',', map { $_->name } $self->pending_entities('remove')));
	$self->remove_table($_) foreach $self->pending_entities('remove');
	return $self;
}

=head2 commit_pending_add

Add all pending items, ordering to resolve dependencies as required.

=cut

sub commit_pending_add {
	my $self = shift;
	logInfo("Create " . join(',', map { $_->name } $self->pending_entities('add')));
	my @pending = $self->pending_entities('add');
	ITEM:
	while(@pending) {
		my $e = shift(@pending);
		# TODO Not hugely efficient, perhaps could do with a profile run here?
		my @deps = map { $_->name } $e->dependencies;
		my @pendingNames = map { $_->name } @pending;
		my @unsatisfied = grep { $_ ~~ @deps } @pendingNames;
		my @existing = map { $_->name } $self->entity->list;
		# Include current entity in list of available entries, so that we can allow self-reference
		my @unresolved = grep { !($_ ~~ [@pendingNames, @existing, $e->name]) } @deps;
		if(@unresolved) {
			logError("%s unresolved (pending %s, deps %s for %s)", join(',', @unresolved), join(',', @pendingNames), join(',', @deps), $e->name);
			die "Dependency error";
		}
		if(@unsatisfied) {
			logInfo("%s has %d unsatisfied deps, postponing: %s", $e->name, scalar @unsatisfied, join(',',@unsatisfied));
			push @pending, $e;
			next ITEM;
		}
		$self->create_table($e);
	}
	return $self;
}

sub remove_entity { shift->remove_table(@_) }

sub remove_table {
	my $self = shift;
	my $tbl = shift;
	logDebug("Remove table " . $tbl->name);
	$self->entity->remove(sub { $_[0]->name ne $tbl->name });
	return $self;
}

sub create_table {
	my $self = shift;
	my $tbl = shift;
	logDebug("Create table " . $tbl->name);
	$self->entity->push($tbl);
	return $self;
}

sub update_table {
	my $self = shift;
	my $src = shift;
	my ($e) = grep { $_->name eq $src->name } $self->entity->list;
	logDebug("Found table [%s] for [%s]", $e->name, $src->name);
	my $dst = $self->entity_map->get($src->name);
	logDebug("Update table [%s], dest has fields: [%s]", $src->name, join(',', map { $_->name // "undef" } $dst->field->list));
	my @add = grep { !$dst->field_map->get($_->name) } $src->field->list;
	logDebug("Want to add [%s]", join(',', map { $_->name } @add));
	$self->add_field_to_table($dst, $_) foreach @add;
	return $self;
}

=head2 add_field_to_table

=cut

sub add_field_to_table {
	my $self = shift;
	my $entity = shift;
	my $field = shift;
	$entity->field->push($field->clone);
	return $self;
}

sub handler_for {
	my $self = shift;
	my $name = shift;
	logDebug("Check for handlers for [%s] node", $name);
	return;
}

sub provide_handler_for {
	my $self = shift;
	my @args = @_;
	while(@args) {
		my $k = shift(@args);
		my $v = shift(@args);
		$self->handler->set($k, $v);
	}
	return $self;
}

sub handle_item {
	my $self = shift;
	my %args = @_;
	if(my $code = $self->handler->get($args{item})) {
		logDebug("Handling [%s] with plugin", $args{item});
		$code->($self, item => $args{item}, data => $args{data});
	} else {
		logError("No handler for [%s]", $args{item});
	}
	return $self;
}

sub flush {
	my $self = shift;
	$self->commit;
}

=head2 DESTROY



( run in 0.737 second using v1.01-cache-2.11-cpan-d8267643d1d )