--- /dev/null
+package System::Introspector::Config;
+use Moo;
+use Config::General;
+
+has config => (is => 'lazy');
+
+has config_file => (is => 'ro', required => 1);
+
+sub _build_config {
+ my ($self) = @_;
+ my $reader = Config::General->new($self->config_file);
+ my %config = $reader->getall;
+ return \%config;
+}
+
+sub groups { keys %{ $_[0]->config->{group} || {} } }
+
+sub has_group { exists $_[0]->config->{group}{ $_[1] } }
+
+sub config_for_group { $_[0]->config->{group}{ $_[1] } }
+
+1;
--- /dev/null
+package System::Introspector::State;
+use Moo;
+use Data::YAML::Writer;
+use Object::Remote;
+use Object::Remote::Future;
+use System::Introspector::Gatherer;
+
+has config => (is => 'ro', required => 1);
+
+has introspectors => (is => 'lazy');
+
+has host => (is => 'ro');
+
+has storage => (is => 'ro', required => 1);
+
+has node_path => (is => 'lazy');
+
+sub fetch {
+ my ($self) = @_;
+ my $gatherer = $self->_create_gatherer;
+ my $spec = $self->introspectors;
+ my %report;
+ for my $class_base (sort keys %$spec) {
+ $report{ $class_base } = $gatherer
+ ->gather($class_base, $spec->{ $class_base });
+ }
+ return \%report;
+}
+
+sub fetch_and_store {
+ my ($self) = @_;
+ return $self->_store($self->fetch);
+}
+
+sub _build_node_path {
+ my ($self) = @_;
+ return defined($self->host)
+ ? sprintf('host/%s', $self->host)
+ : 'local';
+}
+
+sub _build_introspectors {
+ my ($self) = @_;
+ return $self->config->{introspect};
+}
+
+sub _store {
+ my ($self, $data) = @_;
+ my $yaml = Data::YAML::Writer->new;
+ my $storage = $self->storage;
+ my @files;
+ for my $class (sort keys %$data) {
+ my $file = sprintf '%s.yml', join '/',
+ node => $self->node_path,
+ map lc, map {
+ s{([a-z0-9])([A-Z])}{${1}_${2}}g;
+ $_;
+ } split m{::}, $class;
+ my $fh = $storage->open('>:utf8', $file, mkpath => 1);
+ print "Writing $file\n";
+ $yaml->write($data->{$class}, $fh);
+ push @files, $storage->file($file);
+ }
+ $self->_cleanup(\@files);
+ return 1;
+}
+
+sub _cleanup {
+ my ($self, $known_files) = @_;
+ my %known = map { ($_ => 1) } @$known_files;
+ my $data_dir = $self->storage->file(node => $self->node_path);
+ my @files = $self->storage->find_files('yml', node => $self->node_path);
+ for my $file (@files) {
+ next if $known{$file};
+ print "Removing $file\n";
+ unlink($file)
+ or die "Unable to remove '$file': $!\n";
+ }
+ return 1;
+}
+
+sub _create_gatherer {
+ my ($self) = @_;
+ if (defined( my $host = $self->host )) {
+ return System::Introspector::Gatherer->new::on($host);
+ }
+ return System::Introspector::Gatherer->new;
+}
+
+1;