source: perl/lib/BarnOwl/ModuleLoader.pm @ 78b9503

release-1.9
Last change on this file since 78b9503 was f34728b, checked in by Alex Dehnert <adehnert@mit.edu>, 10 years ago
Add completion for reload-module
  • Property mode set to 100644
File size: 4.8 KB
Line 
1use strict;
2use warnings;
3
4package BarnOwl::ModuleLoader;
5
6use PAR;
7
8our %modules;
9
10sub load_all {
11    my $class = shift;
12    $class->rescan_modules;
13    PAR::reload_libs();
14
15    for my $mod (keys %modules) {
16        if(!defined eval "use BarnOwl::Module::$mod") {
17            BarnOwl::error("Unable to load module $mod: \n$@\n") if $@;
18        }
19    }
20
21    $BarnOwl::Hooks::startup->add(\&register_keybindings);
22}
23
24=h2 rescan_modules
25
26Re-compute the list of available modules, and add the necessary items to @INC
27and @PAR_INC.
28
29We load modules from two directories, the system module dir, and the user module
30directory. Modules can be in either of two forms: ${modname}.par, or else a
31${modname}/ directory containing lib/${modname}.
32
33We prefer to load modules from the user's directory, and if a module exists in
34both packed and unpacked form in the same directory, we prefer the unpacked
35module.
36
37We walk the module directories in order of ascending priority -- user directory,
38and then system directory.
39
40When we walk the directories, we first check all things that are not named
41Foo.par, add them to @INC, and add them to the module list. We then walk the
42.par files and add them to @PAR_INC and update the module list.
43
44It is important that we never add a module to @INC (or @PAR_INC) if we already
45have a source for it, in order to get priorities right. The reason is that @INC
46is processed before @PAR_INC, so if we had an unpacked system module and a
47packed local module, if we added both, the system module would take priority.
48
49=cut
50
51sub rescan_modules {
52    @PAR::PAR_INC = ();
53
54    %modules = ();
55
56    my @moddirs = ();
57    push @moddirs, BarnOwl::get_config_dir() . "/modules";
58    push @moddirs, BarnOwl::get_data_dir() . "/modules";
59
60    for my $dir (@moddirs) {
61        # Strip defunct entries from @INC
62        @INC = grep {!/^\Q$dir/} @INC;
63
64        opendir(my $dh, $dir) or next;
65        my @ents = grep {!/^\./} readdir($dh);
66
67        # Walk unpacked modules
68        for my $f (grep {!/\.par$/} @ents) {
69            if(-d "$dir/$f" && -d "$dir/$f/lib") {
70                next if $modules{$f};
71
72                unshift @INC, "$dir/$f/lib" unless grep m{^$dir/$f/lib$}, @INC;
73                $modules{$f} = 1;
74            }
75        }
76
77        # Walk parfiles
78        for my $f (grep /\.par$/, @ents) {
79            if(-f "$dir/$f" && $f =~ /^(.+)\.par$/) {
80                next if $modules{$1};
81
82                PAR->import("$dir/$f");
83                $modules{$1} = 1;
84            }
85        }
86
87        closedir($dh);
88    }
89}
90
91sub reload_module {
92    my $class = shift;
93    my $module = shift;
94
95    $class->rescan_modules();
96
97    for my $m (keys %INC) {
98        delete $INC{$m} if $m =~ m{^BarnOwl/Module/$module};
99    }
100
101    my $parfile;
102    for my $p (@PAR::PAR_INC) {
103        if($p =~ m/\Q$module\E[.]par$/) {
104            $parfile = $p;
105            last;
106        }
107    }
108
109    local $SIG{__WARN__} = \&squelch_redefine;
110
111    if(defined $parfile) {
112        PAR::reload_libs($parfile);
113        $class->run_startup_hooks();
114    } elsif(!defined eval "use BarnOwl::Module::$module") {
115        BarnOwl::error("Unable to load module $module: \n$@\n") if $@;
116    }
117}
118
119sub reload_module_cmd {
120    my $class = shift;
121    shift; # Command
122    my $module = shift;
123    unless(defined($module)) {
124        die("Usage: reload-module [MODULE]");
125    };
126    $class->reload_module($module);
127}
128
129sub complete_module_name {
130    return sort(keys %modules);
131}
132
133sub register_keybindings {
134    BarnOwl::new_command('reload-modules', sub {BarnOwl::ModuleLoader->reload}, {
135                           summary => 'Reload all modules',
136                           usage   => 'reload-modules',
137                           description => q{Reloads all modules located in ~/.owl/modules and the system modules directory}
138                          });
139    BarnOwl::new_command('reload-module', sub {BarnOwl::ModuleLoader->reload_module_cmd(@_)}, {
140                           summary => 'Reload one module',
141                           usage   => 'reload-module [MODULE]',
142                           description => q{Reloads a single module located in ~/.owl/modules or the system modules directory}
143                          });
144
145    BarnOwl::Completion::register_completer('reload-module', \&complete_module_name);
146}
147
148sub reload {
149    my $class = shift;
150    for my $m (keys %INC) {
151        delete $INC{$m} if $m =~ m{^BarnOwl/};
152    }
153    # Restore core modules from perlwrap.pm
154    $INC{$_} = 1 for (qw(BarnOwl.pm BarnOwl/Hooks.pm
155                         BarnOwl/Message.pm BarnOwl/Style.pm));
156    $class->run_startup_hooks();
157}
158
159sub run_startup_hooks {
160    my $class = shift;
161    local $SIG{__WARN__} = \&squelch_redefine;
162    $class->load_all;
163    $BarnOwl::Hooks::startup->run(1);
164    BarnOwl::startup() if *BarnOwl::startup{CODE};
165}
166
167sub squelch_redefine {
168    my $warning = shift;
169    warn $warning unless $warning =~ /^Subroutine .+ redefined at/;
170}
171
1721;
Note: See TracBrowser for help on using the repository browser.