Newer Older
147 lines | 3.408kb
add files
Yuki Kimoto authored on 2014-03-26
1
package Mojo::Loader;
2
use Mojo::Base -base;
3

            
4
use File::Basename 'fileparse';
5
use File::Spec::Functions qw(catdir catfile splitdir);
6
use Mojo::Exception;
7
use Mojo::Util qw(b64_decode class_to_path);
8

            
9
my (%BIN, %CACHE);
10

            
11
sub data { $_[1] ? $_[2] ? _all($_[1])->{$_[2]} : _all($_[1]) : undef }
12

            
13
sub is_binary { keys %{_all($_[1])} ? !!$BIN{$_[1]}{$_[2]} : undef }
14

            
15
sub load {
16
  my ($self, $module) = @_;
17

            
18
  # Check module name
19
  return 1 if !$module || $module !~ /^\w(?:[\w:']*\w)?$/;
20

            
21
  # Load
22
  return undef if $module->can('new') || eval "require $module; 1";
23

            
24
  # Exists
25
  return 1 if $@ =~ /^Can't locate \Q@{[class_to_path $module]}\E in \@INC/;
26

            
27
  # Real error
28
  return Mojo::Exception->new($@);
29
}
30

            
31
sub search {
32
  my ($self, $namespace) = @_;
33

            
34
  my (@modules, %found);
35
  for my $directory (@INC) {
36
    next unless -d (my $path = catdir $directory, split(/::|'/, $namespace));
37

            
38
    # List "*.pm" files in directory
39
    opendir(my $dir, $path);
40
    for my $file (grep /\.pm$/, readdir $dir) {
41
      next if -d catfile splitdir($path), $file;
42
      my $class = "${namespace}::" . fileparse $file, qr/\.pm/;
43
      push @modules, $class unless $found{$class}++;
44
    }
45
  }
46

            
47
  return \@modules;
48
}
49

            
50
sub _all {
51
  my $class = shift;
52

            
53
  my $handle = do { no strict 'refs'; \*{"${class}::DATA"} };
54
  return $CACHE{$class} || {} if $CACHE{$class} || !fileno $handle;
55
  seek $handle, 0, 0;
56
  my $data = join '', <$handle>;
57

            
58
  # Ignore everything before __DATA__ (Windows will seek to start of file)
59
  $data =~ s/^.*\n__DATA__\r?\n/\n/s;
60

            
61
  # Ignore everything after __END__
62
  $data =~ s/\n__END__\r?\n.*$/\n/s;
63

            
64
  # Split files
65
  (undef, my @files) = split /^@@\s*(.+?)\s*\r?\n/m, $data;
66

            
67
  # Find data
68
  my $all = $CACHE{$class} = {};
69
  while (@files) {
70
    my ($name, $data) = splice @files, 0, 2;
71
    $all->{$name} = $name =~ s/\s*\(\s*base64\s*\)$//
72
      && ++$BIN{$class}{$name} ? b64_decode($data) : $data;
73
  }
74

            
75
  return $all;
76
}
77

            
78
1;
79

            
80
=encoding utf8
81

            
82
=head1 NAME
83

            
84
Mojo::Loader - Loader
85

            
86
=head1 SYNOPSIS
87

            
88
  use Mojo::Loader;
89

            
90
  # Find modules in a namespace
91
  my $loader = Mojo::Loader->new;
92
  for my $module (@{$loader->search('Some::Namespace')}) {
93

            
94
    # Load them safely
95
    my $e = $loader->load($module);
96
    warn qq{Loading "$module" failed: $e} and next if ref $e;
97

            
98
    # And extract files from the DATA section
99
    say $loader->data($module, 'some_file.txt');
100
  }
101

            
102
=head1 DESCRIPTION
103

            
104
L<Mojo::Loader> is a class loader and plugin framework.
105

            
106
=head1 METHODS
107

            
108
L<Mojo::Loader> inherits all methods from L<Mojo::Base> and implements the
109
following new ones.
110

            
111
=head2 data
112

            
113
  my $all   = $loader->data('Foo::Bar');
114
  my $index = $loader->data('Foo::Bar', 'index.html');
115

            
116
Extract embedded file from the C<DATA> section of a class.
117

            
118
  say for keys %{$loader->data('Foo::Bar')};
119

            
120
=head2 is_binary
121

            
122
  my $bool = $loader->is_binary('Foo::Bar', 'test.png');
123

            
124
Check if embedded file from the C<DATA> section of a class was Base64 encoded.
125

            
126
=head2 load
127

            
128
  my $e = $loader->load('Foo::Bar');
129

            
130
Load a class and catch exceptions. Note that classes are checked for a C<new>
131
method to see if they are already loaded.
132

            
133
  if (my $e = $loader->load('Foo::Bar')) {
134
    die ref $e ? "Exception: $e" : 'Not found!';
135
  }
136

            
137
=head2 search
138

            
139
  my $modules = $loader->search('MyApp::Namespace');
140

            
141
Search for modules in a namespace non-recursively.
142

            
143
=head1 SEE ALSO
144

            
145
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
146

            
147
=cut