{
$Prophet::Conflict::VERSION
=
'0.751'
;
}
has
prophet_handle
=> (
is
=>
'rw'
,
isa
=>
'Prophet::Replica'
,
);
has
resolvers
=> (
is
=>
'rw'
,
isa
=>
'ArrayRef'
,
default
=>
sub
{ [] },
auto_deref
=> 1,
);
has
changeset
=> (
is
=>
'rw'
,
isa
=>
'Prophet::ChangeSet'
,
);
has
nullification_changeset
=> (
is
=>
'rw'
,
isa
=>
'Prophet::ChangeSet'
,
);
has
resolution_changeset
=> (
is
=>
'rw'
,
isa
=>
'Prophet::ChangeSet'
,
);
has
autoresolved
=> (
is
=>
'rw'
,
isa
=>
'Bool'
,
);
has
conflicting_changes
=> (
is
=>
'ro'
,
isa
=>
'ArrayRef'
,
default
=>
sub
{ [] },
);
sub
has_conflicting_changes {
scalar
@{
$_
[0]->conflicting_changes } }
sub
add_conflicting_change {
my
$self
=
shift
;
push
@{
$self
->conflicting_changes },
@_
;
}
sub
analyze_changeset {
my
$self
=
shift
;
$self
->generate_changeset_conflicts();
return
unless
$self
->has_conflicting_changes;
$self
->generate_nullification_changeset;
return
1;
}
sub
generate_resolution {
my
$self
=
shift
;
my
$resdb
=
shift
;
my
@resolvers
= (
sub
{ Prophet::Resolver::IdenticalChanges->new->run(
@_
); },
$resdb
?
sub
{ Prophet::Resolver::FromResolutionDB->new->run(
@_
) }
: (),
$self
->resolvers,
sub
{ Prophet::Resolver::Fixup::MissingSourceOldValues->new->run(
@_
) },
( -t STDIN && -t STDOUT )
?
sub
{ Prophet::Resolver::Prompt->new->run(
@_
) }
: (),
sub
{ Prophet::Resolver::Failed->new->run(
@_
) },
);
my
$resolutions
= Prophet::ChangeSet->new(
{
creator
=>
$self
->prophet_handle->changeset_creator,
is_resolution
=> 1,
}
);
for
my
$conflicting_change
( @{
$self
->conflicting_changes } ) {
for
(
@resolvers
) {
if
(
my
$resolution
=
$_
->(
$conflicting_change
,
$self
,
$resdb
) )
{
$resolutions
->add_change(
change
=>
$resolution
)
if
$resolution
->has_prop_changes;
last
;
}
}
}
$self
->resolution_changeset(
$resolutions
);
return
1;
}
sub
generate_changeset_conflicts {
my
$self
=
shift
;
for
my
$change
(
$self
->changeset->changes ) {
if
(
my
$change_conflicts
=
$self
->_generate_change_conflicts(
$change
) )
{
$self
->add_conflicting_change(
$change_conflicts
);
}
}
}
sub
_generate_change_conflicts {
my
$self
=
shift
;
my
(
$change
) = validate_pos(
@_
, {
isa
=>
"Prophet::Change"
} );
my
$file_op_conflict
;
my
$file_exists
=
$self
->prophet_handle->record_exists(
uuid
=>
$change
->record_uuid,
type
=>
$change
->record_type
);
if
(
$change
->change_type eq
'delete'
&& !
$file_exists
) {
$file_op_conflict
=
"delete_missing_file"
;
}
elsif
(
$change
->change_type eq
'update_file'
&& !
$file_exists
) {
$file_op_conflict
=
"update_missing_file"
;
}
elsif
(
$change
->change_type eq
'add_file'
&&
$file_exists
) {
$change
->change_type(
'update_file'
);
}
elsif
(
$change
->change_type eq
'add_dir'
&&
$file_exists
) {
$file_op_conflict
=
"create_existing_dir"
;
}
my
$change_conflict
= Prophet::ConflictingChange->new(
{
record_type
=>
$change
->record_type,
record_uuid
=>
$change
->record_uuid,
target_record_exists
=> (
$file_exists
? 1 : 0 ),
change_type
=>
$change
->change_type,
$file_op_conflict
? (
file_op_conflict
=>
$file_op_conflict
) : (),
}
);
if
(
$file_exists
) {
my
$current_state
=
$self
->prophet_handle->get_record_props(
uuid
=>
$change
->record_uuid,
type
=>
$change
->record_type
);
$change_conflict
->add_prop_conflict(
$self
->_generate_prop_change_conflicts(
$change
,
$current_state
)
);
}
return
(
$change_conflict
->has_prop_conflicts ||
$file_op_conflict
)
?
$change_conflict
:
undef
;
}
sub
_generate_prop_change_conflicts {
my
$self
=
shift
;
my
$change
=
shift
;
my
$current_state
=
shift
;
my
@prop_conflicts
;
for
my
$prop_change
(
$change
->prop_changes ) {
next
if
( !
defined
$current_state
->{
$prop_change
->name }
&& !
defined
$prop_change
->old_value );
my
$s
= {
name
=>
$prop_change
->name,
source_old_value
=>
$prop_change
->old_value,
target_value
=>
$current_state
->{
$prop_change
->name },
source_new_value
=>
$prop_change
->new_value
};
my
$old_exists
=
(
defined
$prop_change
->old_value &&
$prop_change
->old_value ne
''
)
? 1
: 0;
my
$current_exists
=
exists
$current_state
->{
$prop_change
->name }
? 1
: 0;
no
warnings
'uninitialized'
;
if
(
(
$current_exists
!=
$old_exists
)
|| (
$current_state
->{
$prop_change
->name } ne
$prop_change
->old_value )
)
{
push
@prop_conflicts
, Prophet::ConflictingPropChange->new(
$s
);
}
}
return
@prop_conflicts
;
}
sub
generate_nullification_changeset {
my
$self
=
shift
;
my
$nullification
= Prophet::ChangeSet->new(
{
is_nullification
=> 1,
creator
=>
undef
,
created
=>
undef
,
}
);
for
my
$conflict
( @{
$self
->conflicting_changes } ) {
my
$nullify_conflict
= Prophet::Change->new(
{
record_type
=>
$conflict
->record_type,
record_uuid
=>
$conflict
->record_uuid
}
);
my
$file_op_conflict
=
$conflict
->file_op_conflict ||
''
;
if
(
$file_op_conflict
eq
"delete_missing_file"
) {
$nullify_conflict
->change_type(
'add_file'
);
}
elsif
(
$file_op_conflict
eq
"update_missing_file"
) {
$nullify_conflict
->change_type(
'add_file'
);
}
elsif
(
$file_op_conflict
eq
"create_existing_file"
) {
$nullify_conflict
->change_type(
'delete'
);
}
elsif
(
$file_op_conflict
) {
die
"We don't know how to deal with a conflict of type "
.
$conflict
->file_op_conflict;
}
else
{
$nullify_conflict
->change_type(
'update_file'
);
}
for
my
$prop_conflict
( @{
$conflict
->prop_conflicts } ) {
$nullify_conflict
->add_prop_change(
name
=>
$prop_conflict
->name,
old
=>
$prop_conflict
->target_value,
new
=>
$prop_conflict
->source_old_value
);
}
$nullification
->add_change(
change
=>
$nullify_conflict
);
}
$self
->nullification_changeset(
$nullification
);
}
__PACKAGE__->meta->make_immutable;
no
Any::Moose;
1;