File Coverage

blib/lib/Zoidberg/PluginHash.pm
Criterion Covered Total %
statement 86 130 66.1
branch 26 56 46.4
condition 1 2 50.0
subroutine 9 14 64.2
pod 0 3 0.0
total 122 205 59.5


line stmt bran cond sub pod time code
1             package Zoidberg::PluginHash;
2              
3             our $VERSION = '0.981';
4              
5 19     19   113 use strict;
  19         39  
  19         778  
6 19     19   902 use Zoidberg::Utils qw/:default read_file merge_hash list_dir/;
  19         40  
  19         200  
7              
8             # $self->[0] = plugin objects hash
9             # $self->[1] = plugin meta data hash
10             # $self->[2] = parent zoid
11              
12             sub TIEHASH {
13 16     16   48 my ($class, $zoid) = @_;
14 16         48 my $self = [{}, {}, $zoid];
15 16         65 bless $self, $class;
16 16         80 $self->hash;
17 16         99 return $self;
18             }
19              
20             sub FETCH {
21 46     46   160 my ($self, $key) = @_;
22              
23 46 100       796 return $self->[0]{$key} if exists $self->[0]{$key};
24              
25 16 50       95 unless ($self->[1]{$key}) {
26 0         0 my @caller = caller;
27 0         0 error "No such object \'$key\' as requested by $caller[1] line $caller[2]";
28             }
29              
30 16 50   0   81 $self->load($key) or return sub { undef };
  0         0  
31 16         198 return $self->[0]{$key};
32             }
33              
34             sub STORE {
35 80     80   164 my ($self, $name, $ding) = @_;
36 80 100       200 my $data = ref($ding) ? $ding : { config_file => $ding, %{read_file($ding)} } ;
  64         228  
37              
38 80 50       339 if (exists $$data{object}) {
39             $$data{object}{zoidname} = $name
40 0 0       0 if eval{ $$data{object}->isa( 'Zoidberg::Fish' ) };
  0         0  
41 0         0 $self->[0]{$name} = $$data{object}
42             }
43              
44             # settings && aliases
45 80         148 for my $t (qw/settings aliases/) {
46 160         185 $$self[2]{$t}{$_} = $$data{$t}{$_} for keys %{$$data{$t}};
  160         607  
47 160         500 delete $$data{$t};
48             }
49              
50             # config
51 80   50     602 $self->[2]{settings}{$name} = merge_hash(
52             $$data{config},
53             $self->[2]{settings}{$name}
54             ) || {};
55 80         243 delete $$data{config};
56            
57             # commands
58 80         114 for (keys %{$$data{commands}}) {
  80         309  
59 32 50       320 $$data{commands}{$_} =~ s/^(\w)/->$name->$1/
60             unless ref $$data{commands}{$_};
61             }
62 80 100       304 if (exists $$data{export}) {
63 64         1750 $$data{commands}{$_} = "->$name->$_"
64 64         113 for @{$$data{export}};
65 64         206 delete $$data{export};
66             }
67 80         115 my ($c, $s);
68 80         113 while( ($c, $s) = each %{$$data{commands}} ) {
  704         2571  
69 624         3381 $self->[2]{commands}{$c} = [$s, $name];
70             }
71 80         262 delete $$data{commands};
72              
73             # events
74 80         98 for (keys %{$$data{events}}) {
  80         277  
75 16 50       229 $$data{events}{$_} =~ s/^(\w)/->$name->$1/
76             unless ref $$data{events}{$_};
77             }
78 80 50       228 if (exists $$data{import}) {
79 0         0 $$data{events}{$_} = "->$name->$_"
80 0         0 for @{$$data{import}};
81 0         0 delete $$data{import};
82             }
83 80         113 while( ($c, $s) = each %{$$data{events}} ) {
  96         370  
84 16         98 $self->[2]{events}{$c} = [$s, $name];
85             }
86 80         163 delete $$data{events};
87              
88             # parser
89 80 50       240 if (exists $$data{parser}) {
90 0         0 require Zoidberg::Fish;
91 0 0       0 my @c = (ref($$data{parser}) eq 'ARRAY') ? (@{$$data{parser}}) : ($$data{parser});
  0         0  
92 0         0 Zoidberg::Fish::add_context({zoidname => $name, shell => $$self[2]}, $_) for @c;
93 0         0 delete $$data{parser};
94             }
95              
96 80         294 $self->[1]{$name} = $data;
97             }
98              
99             our @_keys;
100              
101 0     0   0 sub FIRSTKEY { @_keys = keys %{$_[0][1]}; shift @_keys }
  0         0  
  0         0  
102              
103 0     0   0 sub NEXTKEY { shift @_keys }
104              
105 41     41   1385 sub EXISTS { exists $_[0][1]->{$_[1]} }
106              
107             sub DELETE { # leaves config intact
108 0     0   0 my ($self, $key) = @_;
109 0 0       0 $$self[0]{$key}->round_up() if eval { $self->[0]{$key}->isa( 'Zoidberg::Fish' ) };
  0         0  
110 0         0 delete $$self[0]{$key};
111 0         0 $$self[2]{$_}->wipe($key) for qw/events commands/; # wipe DispatchTable stacks
112 0         0 $$self[2]->broadcast('unplug_'.$key);
113 0         0 return $$self[1]{$key};
114             }
115              
116 0     0   0 sub CLEAR { $_[0]->DELETE($_) for keys %{$_[0][1]} }
  0         0  
117              
118             sub hash {
119 16     16 0 32 my $self = shift;
120              
121             # TODO how about an ignore list for users who disagree with there admin ?
122              
123 16         112 $self->[1] = {};
124 16         48 for my $dir (map "$_/plugins", @{$self->[2]{settings}{data_dirs}}) {
  16         96  
125 16 50       623 next unless -d $dir;
126 16         163 for (list_dir($dir)) {
127 80 50       391 /^(\w+)/ || next;
128 80         270 my ($name, $ding) = ($1, "$dir/$_");
129 80 50       290 next if exists $$self[1]{$name};
130 80 50       3154 if (-d "$dir/$_") {
    100          
131 0         0 my ($conf) = grep /^PluginConf/, list_dir("$dir/$_");
132 0 0       0 next unless $conf;
133 0         0 unshift @INC, "$dir/$_";
134 0 0       0 unshift @{$self->[2]{settings}{data_dirs}}, "$dir/$_/data"
  0         0  
135             if -d "$dir/$_/data";
136 0         0 $ding = "$dir/$_/$conf";
137             }
138             elsif (/.pm$/) {
139 16         50 my $class = $_;
140 16         115 $class =~ s/.pm$//;
141 16         100 $ding = {module => $class, pmfile => "$dir/$_"};
142             }
143 80         148 eval { $self->STORE($name, $ding) };
  80         277  
144 80 50       379 complain if $@;
145             }
146             }
147             }
148              
149             sub load {
150 16     16 0 49 my ($self, $zoidname, @args) = @_;
151 16         65 my $class = $$self[1]{$zoidname}{module};
152 16 50       80 unless ($class) { # FIXME is this allright and does it belong in this package ?
153 0         0 $self->[0]{$zoidname} = {
154             shell => $self->[2],
155             zoidname => $zoidname,
156             settings => $self->[2]->{settings},
157             config => $self->[2]->{settings}{$zoidname},
158             };
159 0         0 debug "Loaded stub plugin $zoidname";
160 0         0 $$self[2]->broadcast('plug_'.$zoidname);
161 0         0 return $self->[0]{$zoidname};
162             }
163              
164 16         32 my $req = $class;
165 16 50       96 $req = '\''.$$self[1]{$zoidname}{pmfile}.'\'' if exists $$self[1]{$zoidname}{pmfile};
166 16         143 debug "Going to load plugin $zoidname of class $class, requiring $req";
167 16         1340 eval "require $req";
168 16 50       117 eval {
169 16 50       123 if (eval{ $class->isa( 'Zoidberg::Fish' ) }) {
  16 0       268  
170 16         149 $self->[0]{$zoidname} = $class->new($self->[2], $zoidname);
171 16         113 $self->[0]{$zoidname}->init(@args);
172             }
173 0         0 elsif ($class->can('new')) { $self->[0]{$zoidname} = $class->new(@args) }
174 0         0 else { error "Module $class doesn't seem to be Object Oriented" }
175             } unless $@;
176 16 50       81 if ($@) {
177 0         0 $@ =~ s/\n$/ /;
178 0         0 complain "Failed to load class: $class ($@)\nDisabling plugin: $zoidname";
179 0         0 $self->DELETE($zoidname);
180 0         0 delete $$self[1]{$zoidname};
181 0         0 return undef;
182             }
183             else {
184 16         148 debug "Loaded plugin $zoidname";
185 16         180 $$self[2]->broadcast('plug_'.$zoidname);
186 16         147 return $self->[0]{$zoidname};
187             }
188             }
189              
190             sub round_up {
191 2     2 0 11 my $self = shift;
192 2         12 for (keys %{$$self[0]}) {
  2         33  
193             $$self[0]{$_}->round_up(@_)
194 2 50       184 if eval{ $$self[0]{$_}->isa( 'Zoidberg::Fish' ) };
  2         136  
195             }
196             }
197              
198             1;
199              
200             __END__