5 use DX::Observer::FromCode;
9 package My::PathStatus;
13 has path => (is => 'ro', required => 1);
14 has info => (is => 'ro', required => 1);
16 package My::PathStatusInfo;
20 has is_directory => (is => 'ro', default => 0);
21 has is_file => (is => 'ro', default => 0);
22 has mode => (is => 'ro', required => 1);
25 @INC{qw(My/PathStatus.pm My/PathStatusInfo.pm)} = (__FILE__,__FILE__);
28 '.ssh' => My::PathStatus->new(
30 info => My::PathStatusInfo->new(is_directory => 1, mode => '0755')
32 '.ssh/authorized_keys' => My::PathStatus->new(
33 path => '.ssh/authorized_keys',
34 info => My::PathStatusInfo->new(is_file => 1, mode => '0644')
40 my $solver = DX::Solver->new(
41 facts => { path_status => DX::SetOver->new(
43 values => \%path_status,
47 $solver->add_rule(@$_) for (
48 [ path_status => [ qw(PS) ],
49 [ member_of => 'PS', [ value => 'path_status' ] ] ],
50 [ path => [ qw(PS P) ],
51 [ constrain => [ qw(PS P) ], sub { $_[0]->path eq $_[1] } ] ],
52 [ mode => [ qw(PS M) ],
53 [ constrain => [ qw(PS M) ],
54 sub { $_[0]->info and $_[0]->info->mode eq $_[1] } ] ],
55 [ is_directory => [ qw(PS) ],
56 [ constrain => [ qw(PS) ],
57 sub { $_[0]->info and $_[0]->info->is_directory } ] ],
58 [ is_file => [ qw(PS) ],
59 [ constrain => [ qw(PS) ],
60 sub { $_[0]->info and $_[0]->info->is_file } ] ],
63 %path_status = %protos;
65 sub paths_for_simple {
66 join ' ', map $_->{PS}->path, $solver->query(
67 [ qw(PS) ], [ path_status => 'PS' ], @_
71 is(paths_for_simple(), '.ssh .ssh/authorized_keys');
73 is(paths_for_simple([ is_directory => 'PS' ]), '.ssh');
75 is(paths_for_simple([ is_file => 'PS' ]), '.ssh/authorized_keys');
77 is(paths_for_simple([ mode => 'PS', [ value => '0755' ] ]), '.ssh');
80 path_status_at => [ 'PS', 'P' ],
81 [ path_status => 'PS' ],
85 path_status_at => [ 'PS', 'P' ],
86 [ constrain => [] => sub { die "ARGH" } ]
92 [ path_status_at => 'PS', [ value => '.ssh' ] ]
96 delete $solver->rule_set->rules->{'path_status_at/2'};
99 path_status_at => [ 'PS', 'P' ],
100 [ path_status => 'PS' ],
101 [ path => qw(PS P) ],
105 path_status_at => [ 'PS', 'P' ],
106 [ constrain => [] => sub { die "ARGH" } ]
112 @res = $solver->query(
114 [ path_status_at => 'PS', [ value => '.ssh' ] ]
118 is(join(' ', map $_->{PS}->path, @res), '.ssh');
120 delete $solver->rule_set->rules->{'path_status_at/2'};
123 path_status_at => [ 'PS', 'P' ],
124 [ path_status => 'PS' ],
125 [ path => qw(PS P) ],
132 path_status_at => [ 'PS', 'P' ],
133 [ observe => [ 'P' ],
136 DX::Observer::FromCode->new(
137 code => sub { (path_status => $ob_res{$path}) }
141 [ path_status => 'PS' ],
142 [ path => qw(PS P) ],
145 %path_status = ('.ssh/authorized_keys' => $protos{'.ssh/authorized_keys'});
147 $ob_res{'.ssh'} = $protos{'.ssh'};
150 join ' ', map $_->{PS}->path, $solver->query([ 'PS' ], @_)->results;
154 paths_for([ path_status => 'PS' ], [ path => 'PS', [ value => '.ssh' ] ]),
159 throws_ok { paths_for([ path_status_at => 'PS', [ value => '.ssh' ] ]) }
162 $solver->{observation_policy} = sub { 1 };
165 paths_for([ path_status_at => 'PS', [ value => '.ssh' ] ]),
170 is($path_status{'.ssh'}, $ob_res{'.ssh'});
172 delete $solver->{observation_policy};
174 lives_ok { paths_for([ path_status_at => 'PS', [ value => '.ssh' ] ]) }
175 'No observation required anymore';