use
5.014;
my
$logger
= Log::Log4perl::get_logger(
"Backend::Itself"
);
subtype
'ModelPathTiny'
=> as
'Object'
=> where {
$_
->isa(
'Path::Tiny'
) };
coerce
'ModelPathTiny'
=> from
'Str'
=> via {path(
$_
)} ;
around
BUILDARGS
=>
sub
{
my
$orig
=
shift
;
my
$class
=
shift
;
my
%args
=
@_
;
my
$legacy
=
delete
$args
{model_object};
if
(
$legacy
) {
$args
{config_model} =
$legacy
->instance->config_model;
$args
{meta_instance} =
$legacy
->instance;
$args
{meta_root} =
$legacy
;
}
return
$class
->
$orig
(
%args
);
};
has
'config_model'
=> (
is
=>
'ro'
,
isa
=>
'Config::Model'
,
lazy_build
=> 1,
) ;
sub
_build_config_model {
my
$self
=
shift
;
if
(
$self
->{meta_root}) {
return
$self
->meta_root->instance->config_model;
}
elsif
(
$self
->{meta_instance}) {
return
$self
->meta_instance->config_model;
}
else
{
return
Config::Model -> new ( ) ;
}
}
has
check
=> (
is
=>
'ro'
,
isa
=>
'Bool'
,
default
=> 1) ;
has
'meta_instance'
=> (
is
=>
'ro'
,
isa
=>
'Config::Model::Instance'
,
lazy_build
=> 1,
) ;
sub
_build_meta_instance {
my
$self
=
shift
;
if
(
$self
->{meta_root}) {
return
$self
->meta_root->instance;
}
else
{
return
$self
->config_model->instance (
root_class_name
=>
'Itself::Model'
,
instance_name
=>
'meta_model'
,
check
=>
$self
->check,
);
}
}
has
meta_root
=> (
is
=>
'ro'
,
isa
=>
'Config::Model::Node'
,
lazy_build
=> 1,
) ;
sub
_build_meta_root {
my
$self
=
shift
;
return
$self
->meta_instance -> config_root ;
}
has
cm_lib_dir
=> (
is
=>
'ro'
,
isa
=>
'ModelPathTiny'
,
lazy_build
=> 1,
coerce
=> 1
) ;
sub
_build_cm_lib_dir {
my
$self
=
shift
;
my
$p
= path(
'lib/Config/Model'
);
if
(!
$p
->is_dir) {
$p
->
mkdir
();
}
return
$p
;
}
has
force_write
=> (
is
=>
'ro'
,
isa
=>
'Bool'
,
default
=> 0) ;
has
root_model
=> (
is
=>
'ro'
,
isa
=>
'str'
);
has
modified_classes
=> (
is
=>
'rw'
,
isa
=>
'HashRef[Bool]'
,
traits
=> [
'Hash'
],
default
=>
sub
{ {} } ,
handles
=> {
clear_classes
=>
'clear'
,
set_class
=>
'set'
,
class_was_changed
=>
'get'
,
class_known
=>
'exists'
,
}
) ;
has
model_dir
=> (
is
=>
'ro'
,
isa
=>
'ModelPathTiny'
,
lazy_build
=> 1,
);
sub
_build_model_dir {
my
$self
=
shift
;
my
$md
=
$self
->cm_lib_dir->child(
'models'
);
$md
->
mkdir
;
return
$md
;
}
sub
BUILD {
my
$self
=
shift
;
weaken(
$self
);
my
$cb
=
sub
{
my
%args
=
@_
;
my
$p
=
$args
{path} ||
''
;
return
unless
$p
=~ /^class/ ;
return
unless
$args
{
index
};
return
if
$self
->class_was_changed(
$args
{
index
}) ;
$logger
->info(
"class $args{index} was modified"
);
$self
->add_modified_class(
$args
{
index
}) ;
} ;
$self
->meta_instance -> on_change_cb(
$cb
) ;
}
sub
add_tracked_class {
my
$self
=
shift
;
$self
->set_class(
shift
,0) ;
}
sub
add_modified_class {
my
$self
=
shift
;
$self
->set_class(
shift
,1) ;
}
sub
class_needs_write {
my
$self
=
shift
;
my
$name
=
shift
;
return
(
$self
->force_write or not
$self
->class_known(
$name
) or
$self
->class_was_changed(
$name
)) ;
}
sub
read_app_files {
my
$self
=
shift
;
my
$force_load
=
shift
|| 0;
my
$read_from
=
shift
;
my
$application
=
shift
;
my
$app_dir
=
$read_from
||
$self
->model_dir->parent;
my
%apps
;
my
%map
;
$logger
->info(
"reading app files from "
.
$app_dir
);
foreach
my
$dir
(
$app_dir
->children(
qr/\.d$/
) ) {
$logger
->info(
"reading app dir "
.
$dir
);
foreach
my
$file
(
$dir
->children() ) {
next
if
$file
=~ m!/README!;
next
if
$file
=~ /(~|\.bak|\.orig)$/;
next
if
$application
and
$file
->basename ne
$application
;
my
%data
= (
category
=>
$dir
->basename(
'.d'
) );
$logger
->info(
"reading app file "
.
$file
);
foreach
(
$file
->lines({
chomp
=> 1})) {
s/^\s+//;
s/\s+$//;
s/
my
(
$k
,
$v
) =
split
/\s*=\s*/;
next
unless
$v
;
$data
{
$k
} =
$v
;
}
my
$appli
=
$file
->basename;
$apps
{
$appli
} =
$data
{model} ;
$map
{
$appli
} =
$file
;
$self
->meta_root->load_data(
data
=> {
application
=> {
$appli
=> \
%data
} },
check
=>
$force_load
?
'no'
:
'yes'
) ;
}
}
$self
->{app_map} = \
%map
;
return
\
%apps
;
}
sub
read_all {
my
$self
=
shift
;
my
%args
=
@_
;
my
$force_load
=
delete
$args
{force_load} || 0 ;
my
$read_from
;
my
$model_dir
;
if
(
$args
{read_from}) {
$read_from
= path (
delete
$args
{read_from});
die
"Cannot read from unknown dir "
.
$read_from
unless
$read_from
->is_dir;
$model_dir
=
$read_from
->child(
'models'
);
die
"Cannot read from unknown dir "
.
$model_dir
unless
$model_dir
->is_dir;
}
my
$apps
=
$self
-> read_app_files(
$force_load
,
$read_from
,
delete
$args
{application});
my
$root_model_arg
=
delete
$args
{root_model} ||
''
;
my
$model
=
$apps
->{
$root_model_arg
} ||
$root_model_arg
;
my
$legacy
=
delete
$args
{legacy} ;
croak
"read_all: unexpected parameters "
,
join
(
' '
,
keys
%args
)
if
%args
;
my
$dir
=
$self
->model_dir;
$dir
->
mkdir
;
my
$root_model_file
=
$model
;
$root_model_file
=~ s!::!/!g ;
my
$read_dir
=
$model_dir
||
$dir
;
$logger
->info(
"searching model files in "
.
$read_dir
);
my
@files
;
my
$wanted
=
sub
{
push
@files
,
$_
if
(
$_
->is_file and /\.pl$/
and m!
$read_dir
/
$root_model_file
\b!
and not m!\.d/!
) ;
} ;
$read_dir
->visit(
$wanted
, {
recurse
=> 1} ) ;
my
%read_models
;
my
%class_file_map
;
my
@all_models
=
$self
->load_model_files(
$read_dir
, \
@files
,
$legacy
, \
%class_file_map
, \
%read_models
);
$self
->{root_model} =
$model
|| (
sort
@all_models
)[0];
my
$root_obj
=
$self
->meta_root ;
my
$class_element
=
$root_obj
->fetch_element(
'class'
) ;
foreach
my
$class
(
sort
keys
%read_models
) {
$class_element
->fetch_with_id(
$class
);
}
$logger
->info(
"loading all extracted data in Config::Model::Itself"
);
$root_obj
->load_data(
data
=> {
class
=> [
%read_models
] },
check
=>
$force_load
?
'no'
:
'yes'
) ;
$self
->read_model_annotations(
$dir
,
$root_obj
, \
@files
);
return
$self
->{
map
} = \
%class_file_map
;
}
sub
load_model_files {
my
(
$self
,
$read_dir
,
$files
,
$legacy
,
$class_file_map
,
$read_models
) =
@_
;
my
@all_models
;
for
my
$file
(
@$files
) {
$logger
->info(
"loading config file $file"
);
my
@legacy
=
$legacy
? (
legacy
=>
$legacy
) : () ;
my
$tmp_model
= Config::Model -> new(
skip_include
=> 1,
@legacy
) ;
my
@models
=
$tmp_model
-> load (
'Tmp'
,
$file
->absolute ) ;
push
@all_models
,
@models
;
my
$rel_file
=
$file
;
$rel_file
=~ s/^
$read_dir
\/?//;
die
"wrong reg_exp"
if
$file
eq
$rel_file
;
$class_file_map
->{
$rel_file
} = \
@models
;
foreach
my
$model_name
(
@models
) {
$read_models
->{
$model_name
} =
$self
->normalize_model(
$model_name
,
$tmp_model
);
}
}
return
@all_models
;
}
sub
normalize_model {
my
(
$self
,
$model_name
,
$tmp_model
) =
@_
;
my
$raw_model
=
$tmp_model
-> get_raw_model(
$model_name
) ;
my
$new_model
=
$tmp_model
-> get_model_clone(
$model_name
) ;
$self
->upgrade_model(
$model_name
,
$new_model
);
$self
->add_tracked_class(
$model_name
);
$self
->add_modified_class(
$model_name
)
unless
Compare(
$raw_model
,
$new_model
) ;
foreach
my
$item
(
qw/description summary level experience status/
) {
foreach
my
$elt_name
(
keys
%{
$new_model
->{element}}) {
my
$moved_data
=
delete
$new_model
->{
$item
}{
$elt_name
} ;
next
unless
defined
$moved_data
;
$new_model
->{element}{
$elt_name
}{
$item
} =
$moved_data
;
}
delete
$new_model
->{
$item
} ;
}
foreach
my
$what
(
qw/element accept/
) {
my
$list
=
delete
$new_model
-> {
$what
.
'_list'
} ;
my
$h
=
delete
$new_model
-> {
$what
} ;
$new_model
-> {
$what
} = [] ;
foreach
my
$name
(
@$list
) {
push
@{
$new_model
->{
$what
}},
$name
,
$h
->{
$name
}
}
;
}
foreach
my
$name
(
keys
%$new_model
) {
if
(not
defined
$new_model
->{
$name
} or
$new_model
->{
$name
} eq
''
) {
delete
$new_model
->{
$name
};
}
}
return
$new_model
;
}
sub
read_model_annotations {
my
(
$self
,
$dir
,
$root_obj
,
$files
) =
@_
;
for
my
$file
(
@$files
) {
$logger
->info(
"loading annotations from file $file"
);
my
$fh
= IO::File->new(
$file
) ||
die
"Can't open $file: $!"
;
my
@lines
=
$fh
->getlines ;
$fh
->
close
;
$root_obj
->load_pod_annotation(
join
(
''
,
@lines
)) ;
my
@headers
;
foreach
my
$l
(
@lines
) {
if
(
$l
=~ /^\s*
push
@headers
,
$l
}
else
{
last
;
}
}
my
$rel_file
=
$file
;
$rel_file
=~ s/^
$dir
\/?//;
$self
->{header}{
$rel_file
} = \
@headers
;
}
}
sub
upgrade_model {
my
(
$self
,
$config_class_name
,
$model
) =
@_
;
my
$multi_backend
= 0;
foreach
my
$config
(
qw/read_config write_config/
) {
my
$ref
=
$model
->{
$config
};
if
(
$ref
and
ref
(
$ref
) eq
'ARRAY'
) {
if
(
@$ref
== 1) {
$model
->{
$config
} =
$ref
->[0];
}
elsif
(
@$ref
> 1){
$logger
->
warn
(
"$config_class_name $config: cannot migrate multiple backends to rw_config"
);
$multi_backend
++;
}
}
}
if
(
$model
->{read_config} and not
$multi_backend
) {
say
(
"Model $config_class_name: moving read_config specification to rw_config"
);
$model
->{rw_config} =
delete
$model
->{read_config};
}
if
(
$model
->{write_config} and not
$multi_backend
) {
say
"Model $config_class_name: merging write_config specification in rw_config"
;
if
(not
$multi_backend
) {
foreach
my
$spec
(
keys
%{
$model
->{write_config}} ) {
$model
->{rw_config}{
$spec
} =
$model
->{write_config}{
$spec
}
} ;
delete
$model
->{write_config};
}
}
}
sub
get_perl_data_model{
my
$self
=
shift
;
my
%args
=
@_
;
my
$root_obj
=
$self
->{meta_root};
my
$class_name
=
$args
{class_name}
|| croak __PACKAGE__,
" read: undefined class name"
;
my
$class_element
=
$root_obj
->fetch_element(
'class'
) ;
return
unless
$class_element
->
defined
(
$class_name
) ;
my
$class_elt
=
$class_element
-> fetch_with_id(
$class_name
) ;
my
$model
=
$class_elt
->dump_as_data ;
$model
->{name} =
$class_name
if
keys
%$model
;
return
$model
;
}
sub
write_app_files {
my
$self
=
shift
;
my
$app_dir
=
$self
->cm_lib_dir;
my
$app_obj
=
$self
->meta_root->fetch_element(
'application'
);
foreach
my
$app_name
(
$app_obj
->fetch_all_indexes ) {
$logger
->debug(
"writing $app_name..."
);
my
$app
=
$app_obj
->fetch_with_id(
$app_name
);
my
$cat_dir_name
=
$app
->fetch_element_value(
name
=>
'category'
).
'.d'
;
$app_dir
->child(
$cat_dir_name
)->
mkdir
();
my
$app_file
=
$app_dir
->child(
$cat_dir_name
)->child(
$app
->index_value) ;
my
@lines
;
foreach
my
$name
(
$app
->children ) {
next
if
$name
eq
'category'
;
my
$v
=
$app
->fetch_element_value(
$name
);
next
unless
defined
$v
;
push
@lines
,
"$name = $v\n"
;
}
$logger
->info(
"writing file "
.
$app_file
);
$app_file
->spew(
@lines
);
delete
$self
->{app_map}{
$app_name
};
}
foreach
my
$old_file
(
values
%{
$self
->{app_map}}) {
$logger
->debug(
"Removing $old_file."
);
$old_file
->remove;
}
}
sub
write_all {
my
$self
=
shift
;
my
%args
=
@_
;
my
$root_obj
=
$self
->meta_root ;
my
$dir
=
$self
->model_dir ;
croak
"write_all: unexpected parameters "
,
join
(
' '
,
keys
%args
)
if
%args
;
$self
->write_app_files;
my
$map
=
$self
->{
map
} ;
$dir
->
mkdir
;
my
%loaded_classes
=
map
{ (
$_
=> 1); }
$root_obj
->fetch_element(
'class'
)->fetch_all_indexes ;
foreach
my
$file
(
keys
%$map
) {
foreach
my
$class_name
(@{
$map
->{
$file
}}) {
delete
$loaded_classes
{
$class_name
} ;
}
}
my
%new_map
;
foreach
my
$class
(
keys
%loaded_classes
) {
my
$f
=
$class
=~ s!::!/!gr;
$new_map
{
"$f.pl"
} = [
$class
] ;
}
my
%map_to_write
= (
%$map
,
%new_map
) ;
foreach
my
$file
(
keys
%map_to_write
) {
my
(
$data
,
$notes
) =
$self
->check_model_to_write(
$file
, \
%map_to_write
, \
%loaded_classes
);
next
unless
@$data
;
write_model_file (
$dir
->child(
$file
),
$self
->{header}{
$file
},
$notes
,
$data
);
delete
$map_to_write
{
$file
};
}
foreach
my
$goner
(
%map_to_write
) {
$logger
->debug(
"Removing model file $goner."
);
$dir
->child(
$goner
)->remove;
}
$self
->meta_instance->clear_changes ;
}
sub
check_model_to_write {
my
(
$self
,
$file
,
$map_to_write
,
$loaded_classes
) =
@_
;
$logger
->info(
"checking model file $file"
);
my
@data
;
my
@notes
;
my
$file_needs_write
= 0;
foreach
my
$class_name
(@{
$map_to_write
->{
$file
}}) {
$file_needs_write
++
if
$self
->class_needs_write(
$class_name
);
$logger
->info(
"file $file class $class_name needs write "
,
$file_needs_write
);
}
if
(
$file_needs_write
) {
foreach
my
$class_name
(@{
$map_to_write
->{
$file
}}) {
$logger
->info(
"writing class $class_name"
);
my
$model
=
$self
-> get_perl_data_model(
class_name
=>
$class_name
) ;
push
@data
,
$model
if
defined
$model
and
keys
%$model
;
my
$node
=
$self
->{meta_root}->grab(
"class:"
.
$class_name
) ;
push
@notes
,
$node
->dump_annotations_as_pod ;
delete
$loaded_classes
->{
$class_name
} ;
}
}
return
(\
@data
, \
@notes
);
}
sub
write_model_plugin {
my
$self
=
shift
;
my
%args
=
@_
;
my
$plugin_dir
=
delete
$args
{plugin_dir}
|| croak __PACKAGE__,
" write_model_plugin: undefined plugin_dir"
;
my
$plugin_name
=
delete
$args
{plugin_name}
|| croak __PACKAGE__,
" write_model_plugin: undefined plugin_name"
;
croak
"write_model_plugin: unexpected parameters "
,
join
(
' '
,
keys
%args
)
if
%args
;
my
$model
=
$self
->meta_root->dump_as_data(
mode
=>
'custom'
) ;
my
@raw_data
= @{
$model
->{class} || []} ;
while
(
@raw_data
) {
my
(
$class
,
$data
) =
splice
@raw_data
,0,2 ;
$data
->{name} =
$class
;
my
@notes
=
$self
->meta_root->grab(
"class:$class"
)->dump_annotations_as_pod ;
my
$plugin_file
=
$class
.
'.pl'
;
$plugin_file
=~ s!::!/!g;
write_model_file (
"$plugin_dir/$plugin_name/$plugin_file"
, [], \
@notes
, [
$data
]);
}
$self
->meta_instance->clear_changes ;
}
sub
read_model_plugin {
my
$self
=
shift
;
my
%args
=
@_
;
my
$plugin_dir
=
delete
$args
{plugin_dir}
|| croak __PACKAGE__,
" write_model_plugin: undefined plugin_dir"
;
my
$plugin_name
=
delete
$args
{plugin_name}
|| croak __PACKAGE__,
" read_model_plugin: undefined plugin_name"
;
croak
"read_model_plugin: unexpected parameters "
,
join
(
' '
,
keys
%args
)
if
%args
;
my
@files
;
my
$wanted
=
sub
{
my
$n
=
$File::Find::name
;
push
@files
,
$n
if
(-f
$_
and not /~$/
and
$n
!~ /CVS/
and
$n
!~ m!.(svn|orig|pod)$!
and
$n
=~ m!\.d/
$plugin_name
!
) ;
} ;
find (
$wanted
,
$plugin_dir
) ;
foreach
my
$load_file
(
@files
) {
$self
->read_plugin_file(
$load_file
);
}
}
sub
read_plugin_file {
my
(
$self
,
$load_file
) =
@_
;
$logger
->info(
"trying to read plugin $load_file"
);
my
$class_element
=
$self
->meta_root->fetch_element(
'class'
) ;
$load_file
=
"./$load_file"
if
$load_file
!~ m!^/! and -e
$load_file
;
my
$plugin
=
do
$load_file
;
unless
(
$plugin
) {
if
($@) {
die
"couldn't parse $load_file: $@"
; }
elsif
(not
defined
$plugin
) {
die
"couldn't do $load_file: $!"
}
else
{
die
"couldn't run $load_file"
;}
}
foreach
my
$model
(
@$plugin
) {
my
$class_name
=
delete
$model
->{name} ;
$class_element
->fetch_with_id(
$class_name
)->load_data(
$model
) ;
}
$logger
->info(
"loading annotations from plugin file $load_file"
);
my
$fh
= IO::File->new(
$load_file
) ||
die
"Can't open $load_file: $!"
;
my
@lines
=
$fh
->getlines ;
$fh
->
close
;
$self
->meta_root->load_pod_annotation(
join
(
''
,
@lines
)) ;
}
sub
write_model_file {
my
$wr_file
=
shift
;
my
$comments
=
shift
;
my
$notes
=
shift
;
my
$data
=
shift
;
my
$wr_dir
= dirname(
$wr_file
);
unless
( -d
$wr_dir
) {
mkpath(
$wr_dir
, 0,
oct
(755) ) ||
die
"Can't mkpath $wr_dir:$!"
;
}
my
$wr
= IO::File->new(
$wr_file
,
'>'
)
|| croak
"Cannot open file $wr_file:$!"
;
$logger
->info(
"in $wr_file"
);
my
$dumper
= Data::Dumper->new( [ \
@$data
] );
$dumper
->Indent(1);
$dumper
->Terse(1);
$dumper
->Sortkeys(1);
my
$dump
=
$dumper
->Dump;
$dump
=~ s/\n=/\n
'.'
=/g;
$wr
->
print
(
@$comments
) ;
$wr
->
print
(
"use strict;\nuse warnings;\n\n"
);
$wr
->
print
(
"return $dump;\n\n"
);
$wr
->
print
(
join
(
"\n"
,
@$notes
) );
$wr
->
close
;
}
sub
list_class_element {
my
$self
=
shift
;
my
$pad
=
shift
||
''
;
my
$res
=
''
;
my
$meta_class
=
$self
->{meta_root}->fetch_element(
'class'
) ;
foreach
my
$class_name
(
$meta_class
->fetch_all_indexes ) {
$res
.=
$self
->list_one_class_element(
$class_name
) ;
}
return
$res
;
}
sub
list_one_class_element {
my
$self
=
shift
;
my
$class_name
=
shift
||
return
''
;
my
$pad
=
shift
||
''
;
my
$res
=
$pad
.
"Class: $class_name\n"
;
my
$meta_class
=
$self
->{meta_root}->fetch_element(
'class'
)
-> fetch_with_id(
$class_name
) ;
my
@elts
=
$meta_class
->fetch_element(
'element'
)->fetch_all_indexes ;
my
@include
=
$meta_class
->fetch_element(
'include'
)->fetch_all_values ;
my
$inc_after
=
$meta_class
->grab_value(
'include_after'
) ;
if
(
@include
and not
defined
$inc_after
) {
foreach
my
$inc
(
@include
) {
$res
.=
$self
->list_one_class_element(
$inc
,
$pad
.
' '
) ;
}
}
return
$res
unless
@elts
;
foreach
my
$elt_name
(
@elts
) {
my
$type
=
$meta_class
->grab_value(
"element:$elt_name type"
) ;
$res
.=
$pad
.
" - $elt_name ($type)\n"
;
if
(
@include
and
defined
$inc_after
and
$inc_after
eq
$elt_name
) {
foreach
my
$inc
(
@include
) {
$res
.=
$self
->list_one_class_element(
$inc
,
$pad
.
' '
) ;
}
}
}
return
$res
;
}
sub
get_dot_diagram {
my
$self
=
shift
;
my
$dot
=
"digraph model {\n"
;
my
$meta_class
=
$self
->{meta_root}->fetch_element(
'class'
) ;
foreach
my
$class_name
(
$meta_class
->fetch_all_indexes ) {
my
$d_class
=
$class_name
;
$d_class
=~ s/::/__/g;
my
$elt_list
=
''
;
my
$use
=
''
;
my
$class_obj
=
$self
->{meta_root}->grab(
qq!class:"$class_name"!
);
my
@elts
=
$class_obj
->grab(
qq!element!
) ->fetch_all_indexes ;
foreach
my
$elt_name
(
@elts
) {
my
$of
=
''
;
my
$elt_obj
=
$class_obj
->grab(
qq!element:"$elt_name"!
) ;
my
$type
=
$elt_obj
->grab_value(
"type"
) ;
if
(
$type
=~ /^list|hash$/) {
my
$cargo
=
$elt_obj
->grab(
"cargo"
);
my
$ct
=
$cargo
->grab_value(
"type"
) ;
$of
=
" of $ct"
;
$use
.=
$self
->scan_used_class(
$d_class
,
$elt_name
,
$cargo
);
}
else
{
$use
.=
$self
->scan_used_class(
$d_class
,
$elt_name
,
$elt_obj
);
}
$elt_list
.=
"- $elt_name ($type$of)\\n"
;
}
$dot
.=
$d_class
.
qq! [shape=box label="$class_name\\n$elt_list"];\n!
.
$use
.
"\n"
;
$dot
.=
$self
->scan_includes(
$class_name
,
$class_obj
) ;
}
$dot
.=
"}\n"
;
return
$dot
;
}
sub
scan_includes {
my
(
$self
,
$class_name
,
$class_obj
) =
@_
;
my
$d_class
=
$class_name
;
$d_class
=~ s/::/__/g;
my
@includes
=
$class_obj
->grab(
'include'
)->fetch_all_values ;
my
$dot
=
''
;
foreach
my
$c
(
@includes
) {
say
"$class_name includes $c"
;
my
$t
=
$c
;
$t
=~ s/::/__/g;
$dot
.=
qq!$d_class -> $t ;\n!
;
}
return
$dot
;
}
sub
scan_used_class {
my
(
$self
,
$d_class
,
$elt_name
,
$elt_obj
) =
@_
;
my
$disp_leaf
=
sub
{
my
(
$scanner
,
$data_ref
,
$node
,
$element_name
,
$index
,
$leaf_object
) =
@_
;
return
unless
$element_name
eq
'config_class_name'
;
my
$v
=
$leaf_object
->fetch;
return
unless
$v
;
$v
=~ s/::/__/g;
$$data_ref
.=
qq!$d_class -> $v !
.
qq![ style=dashed, label="$elt_name" ];\n!
;
} ;
my
$scan
= Config::Model::ObjTreeScanner-> new (
leaf_cb
=>
$disp_leaf
,
) ;
my
$result
=
''
;
$scan
->scan_node(\
$result
,
$elt_obj
) ;
return
$result
;
}
__PACKAGE__->meta->make_immutable;
1;