split Container's BUILD, moved BB logic from Catalyst.pm to the Container
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
index bb2bad5..1fabc37 100644 (file)
@@ -14,6 +14,7 @@ use Catalyst::Request::Upload;
 use Catalyst::Response;
 use Catalyst::Utils;
 use Catalyst::Controller;
+use Data::OptList;
 use Devel::InnerPackage ();
 use File::stat;
 use Module::Pluggable::Object ();
@@ -66,7 +67,7 @@ our $GO        = Catalyst::Exception::Go->new;
 #I imagine that very few of these really need to be class variables. if any.
 #maybe we should just make them attributes with a default?
 __PACKAGE__->mk_classdata($_)
-  for qw/components arguments dispatcher engine log dispatcher_class
+  for qw/container components arguments dispatcher engine log dispatcher_class
   engine_class context_class request_class response_class stats_class
   setup_finished/;
 
@@ -78,10 +79,7 @@ __PACKAGE__->stats_class('Catalyst::Stats');
 
 # Remember to update this in Catalyst::Runtime as well!
 
-our $VERSION = '5.80021';
-our $PRETTY_VERSION = $VERSION;
-
-$VERSION = eval $VERSION;
+our $VERSION = '5.80032';
 
 sub import {
     my ( $class, @arguments ) = @_;
@@ -102,7 +100,12 @@ sub import {
     $meta->superclasses(grep { $_ ne 'Moose::Object' } $meta->superclasses);
 
     unless( $meta->has_method('meta') ){
-        $meta->add_method(meta => sub { Moose::Meta::Class->initialize("${caller}") } );
+        if ($Moose::VERSION >= 1.15) {
+            $meta->_add_meta_method('meta');
+        }
+        else {
+            $meta->add_method(meta => sub { Moose::Meta::Class->initialize("${caller}") } );
+        }
     }
 
     $caller->arguments( [@arguments] );
@@ -283,14 +286,15 @@ Specifies a comma-delimited list of log levels.
 
 =head2 -Stats
 
-Enables statistics collection and reporting. You can also force this setting
-from the system environment with CATALYST_STATS or <MYAPP>_STATS. The
-environment settings override the application, with <MYAPP>_STATS having the
-highest priority.
+Enables statistics collection and reporting.
+
+   use Catalyst qw/-Stats=1/;
 
-e.g.
+You can also force this setting from the system environment with CATALYST_STATS
+or <MYAPP>_STATS. The environment settings override the application, with
+<MYAPP>_STATS having the highest priority.
 
-   use Catalyst qw/-Stats=1/
+Stats are also enabled if L<< debugging |/"-Debug" >> is enabled.
 
 =head1 METHODS
 
@@ -366,6 +370,8 @@ or stash it like so:
 
 and access it from the stash.
 
+Keep in mind that the C<end> method used is that of the caller action. So a C<$c-E<gt>detach> inside a forwarded action would run the C<end> method from the original action requested.
+
 =cut
 
 sub forward { my $c = shift; no warnings 'recursion'; $c->dispatcher->forward( $c, @_ ) }
@@ -429,6 +435,10 @@ with localized C<< $c->action >> and C<< $c->namespace >>. Like C<detach>,
 C<go> escapes the processing of the current request chain on completion, and
 does not return to its caller.
 
+@arguments are arguments to the final destination of $action. @captures are
+arguments to the intermediate steps, if any, on the way to the final sub of
+$action.
+
 =cut
 
 sub go { my $c = shift; $c->dispatcher->go( $c, @_ ) }
@@ -538,9 +548,11 @@ sub _comp_names_search_prefixes {
     my $filter   = "^${appclass}::(" . join( '|', @prefixes ) . ')::';
     $filter = qr/$filter/; # Compile regex now rather than once per loop
 
+    my @components = map { $c->container->get_sub_container($_)->get_service_list } qw(controller view model);
+
     # map the original component name to the sub part that we will search against
     my %eligible = map { my $n = $_; $n =~ s{^$appclass\::[^:]+::}{}; $_ => $n; }
-        grep { /$filter/ } keys %{ $c->components };
+        grep { /$filter/ } @components;
 
     # undef for a name will return all
     return keys %eligible if !defined $name;
@@ -643,9 +655,10 @@ sub controller {
     my $appclass = ref($c) || $c;
     if( $name ) {
         unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
-            my $comps = $c->components;
             my $check = $appclass."::Controller::".$name;
-            return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check};
+            my $container = $c->container->get_sub_container('controller');
+            return $c->_filter_component( $container->resolve(service => "$check"), @args )
+                if $container->has_service($check);
         }
         my @result = $c->_comp_search_prefixes( $name, qw/Controller C/ );
         return map { $c->_filter_component( $_, @args ) } @result if ref $name;
@@ -681,9 +694,10 @@ sub model {
     my $appclass = ref($c) || $c;
     if( $name ) {
         unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
-            my $comps = $c->components;
             my $check = $appclass."::Model::".$name;
-            return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check};
+            my $container = $c->container->get_sub_container('model');
+            return $c->_filter_component( $container->resolve(service => "$check"), @args )
+                if $container->has_service($check);
         }
         my @result = $c->_comp_search_prefixes( $name, qw/Model M/ );
         return map { $c->_filter_component( $_, @args ) } @result if ref $name;
@@ -740,9 +754,15 @@ sub view {
     my $appclass = ref($c) || $c;
     if( $name ) {
         unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
-            my $comps = $c->components;
             my $check = $appclass."::View::".$name;
-            return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check};
+            my $container = $c->container->get_sub_container('view');
+            if ($container->has_service($check)) {
+
+                return $c->_filter_component( $container->resolve(service => $check), @args );
+            }
+            else {
+                $c->log->warn( "Attempted to use view '$check', but does not exist" );
+            }
         }
         my @result = $c->_comp_search_prefixes( $name, qw/View V/ );
         return map { $c->_filter_component( $_, @args ) } @result if ref $name;
@@ -779,7 +799,7 @@ Returns the available names which can be passed to $c->controller
 
 sub controllers {
     my ( $c ) = @_;
-    return $c->_comp_names(qw/Controller C/);
+    return $c->container->get_sub_container('controller')->get_service_list;
 }
 
 =head2 $c->models
@@ -790,7 +810,7 @@ Returns the available names which can be passed to $c->model
 
 sub models {
     my ( $c ) = @_;
-    return $c->_comp_names(qw/Model M/);
+    return $c->container->get_sub_container('model')->get_service_list;
 }
 
 
@@ -802,7 +822,7 @@ Returns the available names which can be passed to $c->view
 
 sub views {
     my ( $c ) = @_;
-    return $c->_comp_names(qw/View V/);
+    return $c->container->get_sub_container('view')->get_service_list;
 }
 
 =head2 $c->comp($name)
@@ -893,7 +913,7 @@ component is constructed.
 For example:
 
     MyApp->config({ 'Model::Foo' => { bar => 'baz', overrides => 'me' } });
-    MyApp::Model::Foo->config({ quux => 'frob', 'overrides => 'this' });
+    MyApp::Model::Foo->config({ quux => 'frob', overrides => 'this' });
 
 will mean that C<MyApp::Model::Foo> receives the following data when
 constructed:
@@ -904,6 +924,27 @@ constructed:
         overrides => 'me',
     });
 
+It's common practice to use a Moose attribute
+on the receiving component to access the config value.
+
+    package MyApp::Model::Foo;
+
+    use Moose;
+
+    # this attr will receive 'baz' at construction time
+    has 'bar' => (
+        is  => 'rw',
+        isa => 'Str',
+    );
+
+You can then get the value 'baz' by calling $c->model('Foo')->bar
+(or $self->bar inside code in the model).
+
+B<NOTE:> you MUST NOT call C<< $self->config >> or C<< __PACKAGE__->config >>
+as a way of reading config within your code, as this B<will not> give you the
+correctly merged config back. You B<MUST> take the config values supplied to
+the constructor and use those instead.
+
 =cut
 
 around config => sub {
@@ -1073,6 +1114,7 @@ sub setup {
         }
     }
 
+    $class->setup_config();
     $class->setup_home( delete $flags->{home} );
 
     $class->setup_log( delete $flags->{log} );
@@ -1162,7 +1204,7 @@ EOF
 
     if ( $class->debug ) {
         my $name = $class->config->{name} || 'Application';
-        $class->log->info("$name powered by Catalyst $Catalyst::PRETTY_VERSION");
+        $class->log->info("$name powered by Catalyst $Catalyst::VERSION");
     }
 
     # Make sure that the application class becomes immutable at this point,
@@ -1205,7 +1247,7 @@ EOF
 A hook to attach modifiers to. This method does not do anything except set the
 C<setup_finished> accessor.
 
-Applying method modifiers to the C<setup> method doesn't work, because of quirky thingsdone for plugin setup.
+Applying method modifiers to the C<setup> method doesn't work, because of quirky things done for plugin setup.
 
 Example:
 
@@ -1228,7 +1270,9 @@ sub setup_finalize {
 
 Constructs an absolute L<URI> object based on the application root, the
 provided path, and the additional arguments and query parameters provided.
-When used as a string, provides a textual URI.
+When used as a string, provides a textual URI.  If you need more flexibility
+than this (i.e. the option to provide relative URIs etc.) see
+L<Catalyst::Plugin::SmartURI>.
 
 If no arguments are provided, the URI for the current action is returned.
 To return the current action and also provide @args, use
@@ -1282,13 +1326,11 @@ sub uri_for {
     carp "uri_for called with undef argument" if grep { ! defined $_ } @args;
     foreach my $arg (@args) {
         utf8::encode($arg) if utf8::is_utf8($arg);
-    }
-    s/([^$URI::uric])/$URI::Escape::escapes{$1}/go for @args;
-    if (blessed $path) { # Action object only.
-        s|/|%2F|g for @args;
+        $arg =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
     }
 
     if ( blessed($path) ) { # action object
+        s|/|%2F|g for @args;
         my $captures = [ map { s|/|%2F|g; $_; }
                         ( scalar @args && ref $args[0] eq 'ARRAY'
                          ? @{ shift(@args) }
@@ -1309,8 +1351,6 @@ sub uri_for {
         $path = '/' if $path eq '';
     }
 
-    undef($path) if (defined $path && $path eq '');
-
     unshift(@args, $path);
 
     unless (defined $path && $path =~ s!^/!!) { # in-place strip
@@ -1555,6 +1595,35 @@ These methods are not meant to be used by end users.
 
 Returns a hash of components.
 
+=cut
+
+around components => sub {
+    my $orig  = shift;
+    my $class = shift;
+    my $comps = shift;
+
+    return $class->$orig if ( !$comps );
+
+# FIXME: should this ugly kludge exist?
+    $class->setup_config unless defined $class->container;
+
+# FIXME: should there be a warning here, not to use this accessor to create the components?
+    my $components = {};
+
+    my $containers;
+    $containers->{$_} = $class->container->get_sub_container($_) for qw(model view controller);
+
+    for my $component ( keys %$comps ) {
+        $components->{ $component } = $comps->{$component};
+
+        my $type = _get_component_type($component);
+
+        $containers->{$type}->add_service(Bread::Board::BlockInjection->new( name => $component, block => sub { return $class->setup_component($component) } ));
+    }
+
+    return $class->$orig($components);
+};
+
 =head2 $c->context_class
 
 Returns or sets the context class.
@@ -1626,7 +1695,9 @@ sub execute {
     push( @{ $c->stack }, $code );
 
     no warnings 'recursion';
-    eval { $c->state( $code->execute( $class, $c, @{ $c->req->args } ) || 0 ) };
+    # N.B. This used to be combined, but I have seen $c get clobbered if so, and
+    #      I have no idea how, ergo $ret (which appears to fix the issue)
+    eval { my $ret = $code->execute( $class, $c, @{ $c->req->args } ) || 0; $c->state( $ret ) };
 
     $c->_stats_finish_execute( $stats_info ) if $c->use_stats and $stats_info;
 
@@ -1648,8 +1719,8 @@ sub execute {
                 $error = qq/Caught exception in $class->$name "$error"/;
             }
             $c->error($error);
-            $c->state(0);
         }
+        $c->state(0);
     }
     return $c->state;
 }
@@ -1689,7 +1760,7 @@ sub _stats_start_execute {
         my $parent = $c->stack->[-1];
 
         # forward, locate the caller
-        if ( exists $c->counter->{"$parent"} ) {
+        if ( defined $parent && exists $c->counter->{"$parent"} ) {
             $c->stats->profile(
                 begin  => $action,
                 parent => "$parent" . $c->counter->{"$parent"},
@@ -1823,10 +1894,10 @@ sub finalize_headers {
     }
 
     # Content-Length
-    if ( $response->body && !$response->content_length ) {
+    if ( defined $response->body && length $response->body && !$response->content_length ) {
 
         # get the length from a filehandle
-        if ( blessed( $response->body ) && $response->body->can('read') )
+        if ( blessed( $response->body ) && $response->body->can('read') || ref( $response->body ) eq 'GLOB' )
         {
             my $stat = stat $response->body;
             if ( $stat && $stat->size > 0 ) {
@@ -1893,7 +1964,7 @@ namespaces.
 
 sub get_actions { my $c = shift; $c->dispatcher->get_actions( $c, @_ ) }
 
-=head2 $c->handle_request( $class, @arguments )
+=head2 $app->handle_request( @arguments )
 
 Called to handle each HTTP request.
 
@@ -1953,7 +2024,7 @@ sub prepare {
 
     #surely this is not the most efficient way to do things...
     $c->stats($class->stats_class->new)->enable($c->use_stats);
-    if ( $c->debug ) {
+    if ( $c->debug || $c->config->{enable_catalyst_header} ) {
         $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
     }
 
@@ -2138,7 +2209,7 @@ sub log_request {
         $c->log->debug("Query keywords are: $keywords");
     }
 
-    $c->log_request_parameters( query => $request->query_parameters, body => $request->body_parameters );
+    $c->log_request_parameters( query => $request->query_parameters, $request->_has_body ? (body => $request->body_parameters) : () );
 
     $c->log_request_uploads($request);
 }
@@ -2278,7 +2349,8 @@ sub log_headers {
 
     return unless $c->debug;
 
-    my $t = Text::SimpleTable->new( [ 35, 'Header Name' ], [ 40, 'Value' ] );
+    my $column_width = Catalyst::Utils::term_width() - 28;
+    my $t = Text::SimpleTable->new( [ 15, 'Header Name' ], [ $column_width, 'Value' ] );
     $headers->scan(
         sub {
             my ( $name, $value ) = @_;
@@ -2327,11 +2399,11 @@ sub prepare_write { my $c = shift; $c->engine->prepare_write( $c, @_ ) }
 
 =head2 $c->request_class
 
-Returns or sets the request class.
+Returns or sets the request class. Defaults to L<Catalyst::Request>.
 
 =head2 $c->response_class
 
-Returns or sets the response class.
+Returns or sets the response class. Defaults to L<Catalyst::Response>.
 
 =head2 $c->read( [$maxlength] )
 
@@ -2374,6 +2446,34 @@ Sets up actions for a component.
 
 sub setup_actions { my $c = shift; $c->dispatcher->setup_actions( $c, @_ ) }
 
+=head2 $c->setup_config
+
+=cut
+
+sub setup_config {
+    my $class = shift;
+
+    my %args = %{ $class->config || {} };
+
+    my @container_classes = ( "${class}::Container", 'Catalyst::Container');
+    unshift @container_classes, delete $args{container_class} if exists $args{container_class};
+
+    my $container_class = Class::MOP::load_first_existing_class(@container_classes);
+
+    my $container = $container_class->new( %args, name => "$class" );
+    $class->container($container);
+
+    my $config = $container->resolve(service => 'config');
+    $class->config($config);
+    $class->finalize_config; # back-compat
+}
+
+=head $c->finalize_config
+
+=cut
+
+sub finalize_config { }
+
 =head2 $c->setup_components
 
 This method is called internally to set up the application's components.
@@ -2394,8 +2494,7 @@ sub setup_components {
 
     my $config  = $class->config->{ setup_components };
 
-    my @comps = sort { length $a <=> length $b }
-                $class->locate_components($config);
+    my @comps = $class->locate_components($config);
     my %comps = map { $_ => 1 } @comps;
 
     my $deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @comps;
@@ -2410,25 +2509,47 @@ sub setup_components {
         # we know M::P::O found a file on disk so this is safe
 
         Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
-
-        # Needs to be done as soon as the component is loaded, as loading a sub-component
-        # (next time round the loop) can cause us to get the wrong metaclass..
-        $class->_controller_init_base_classes($component);
     }
 
+    my $containers;
+    $containers->{$_} = $class->container->get_sub_container($_) for qw(model view controller);
+
     for my $component (@comps) {
         my $instance = $class->components->{ $component } = $class->setup_component($component);
+        if ( my $type = _get_component_type($component) ) {
+            $containers->{$type}->add_service(Bread::Board::BlockInjection->new( name => $component, block => sub { return $instance } ));
+        }
         my @expanded_components = $instance->can('expand_modules')
             ? $instance->expand_modules( $component, $config )
             : $class->expand_component_module( $component, $config );
         for my $component (@expanded_components) {
             next if $comps{$component};
-            $class->_controller_init_base_classes($component); # Also cover inner packages
+
+            $deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components;
+            $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
+                qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
+            ) if $deprecatedcatalyst_component_names;
+
+            if (my $type = _get_component_type($component)) {
+                $containers->{$type}->add_service(Bread::Board::BlockInjection->new( name => $component, block => sub { return $class->setup_component($component) } ));
+            }
+
             $class->components->{ $component } = $class->setup_component($component);
         }
     }
 }
 
+sub _get_component_type {
+    my $component = shift;
+    my @parts     = split /::/, $component;
+
+    for (@parts) {
+        return 'controller' if /^c|controller$/i;
+        return 'model'      if /^m|model$/i;
+        return 'view'       if /^v|view$/i;
+    }
+}
+
 =head2 $c->locate_components( $setup_component_config )
 
 This method is meant to provide a list of component modules that should be
@@ -2455,7 +2576,8 @@ sub locate_components {
         %$config
     );
 
-    my @comps = $locator->plugins;
+    # XXX think about ditching this sort entirely
+    my @comps = sort { length $a <=> length $b } $locator->plugins;
 
     return @comps;
 }
@@ -2476,19 +2598,6 @@ sub expand_component_module {
 
 =cut
 
-# FIXME - Ugly, ugly hack to ensure the we force initialize non-moose base classes
-#         nearest to Catalyst::Controller first, no matter what order stuff happens
-#         to be loaded. There are TODO tests in Moose for this, see
-#         f2391d17574eff81d911b97be15ea51080500003
-sub _controller_init_base_classes {
-    my ($app_class, $component) = @_;
-    return unless $component->isa('Catalyst::Controller');
-    foreach my $class ( reverse @{ mro::get_linear_isa($component) } ) {
-        Moose::Meta::Class->initialize( $class )
-            unless find_meta($class);
-    }
-}
-
 sub setup_component {
     my( $class, $component ) = @_;
 
@@ -2511,8 +2620,7 @@ sub setup_component {
             message => qq/Couldn't instantiate component "$component", "$error"/
         );
     }
-
-    unless (blessed $instance) {
+    elsif (!blessed $instance) {
         my $metaclass = Moose::Util::find_meta($component);
         my $method_meta = $metaclass->find_method_by_name('COMPONENT');
         my $component_method_from = $method_meta->associated_metaclass->name;
@@ -2522,6 +2630,7 @@ sub setup_component {
             qq/Couldn't instantiate component "$component", COMPONENT() method (from $component_method_from) didn't return an object-like value (value was $value)./
         );
     }
+
     return $instance;
 }
 
@@ -2789,13 +2898,8 @@ the plugin name does not begin with C<Catalyst::Plugin::>.
             if $plugin->isa( 'Catalyst::Component' );
         $proto->_plugins->{$plugin} = 1;
         unless ($instant) {
-            no strict 'refs';
-            if ( my $meta = Class::MOP::get_metaclass_by_name($class) ) {
-              my @superclasses = ($plugin, $meta->superclasses );
-              $meta->superclasses(@superclasses);
-            } else {
-              unshift @{"$class\::ISA"}, $plugin;
-            }
+            my $meta = Class::MOP::get_metaclass_by_name($class);
+            $meta->superclasses($plugin, $meta->superclasses);
         }
         return $class;
     }
@@ -2804,22 +2908,29 @@ the plugin name does not begin with C<Catalyst::Plugin::>.
         my ( $class, $plugins ) = @_;
 
         $class->_plugins( {} ) unless $class->_plugins;
-        $plugins ||= [];
+        $plugins = Data::OptList::mkopt($plugins || []);
 
-        my @plugins = Catalyst::Utils::resolve_namespace($class . '::Plugin', 'Catalyst::Plugin', @$plugins);
+        my @plugins = map {
+            [ Catalyst::Utils::resolve_namespace(
+                  $class . '::Plugin',
+                  'Catalyst::Plugin', $_->[0]
+              ),
+              $_->[1],
+            ]
+         } @{ $plugins };
 
         for my $plugin ( reverse @plugins ) {
-            Class::MOP::load_class($plugin);
-            my $meta = find_meta($plugin);
+            Class::MOP::load_class($plugin->[0], $plugin->[1]);
+            my $meta = find_meta($plugin->[0]);
             next if $meta && $meta->isa('Moose::Meta::Role');
 
-            $class->_register_plugin($plugin);
+            $class->_register_plugin($plugin->[0]);
         }
 
         my @roles =
-            map { $_->name }
-            grep { $_ && blessed($_) && $_->isa('Moose::Meta::Role') }
-            map { find_meta($_) }
+            map  { $_->[0]->name, $_->[1] }
+            grep { blessed($_->[0]) && $_->[0]->isa('Moose::Meta::Role') }
+            map  { [find_meta($_->[0]), $_->[1]] }
             @plugins;
 
         Moose::Util::apply_all_roles(
@@ -2833,15 +2944,24 @@ the plugin name does not begin with C<Catalyst::Plugin::>.
 Returns an arrayref of the internal execution stack (actions that are
 currently executing).
 
+=head2 $c->stats
+
+Returns the current timing statistics object. By default Catalyst uses
+L<Catalyst::Stats|Catalyst::Stats>, but can be set otherwise with
+L<< stats_class|/"$c->stats_class" >>.
+
+Even if L<< -Stats|/"-Stats" >> is not enabled, the stats object is still
+available. By enabling it with C< $c->stats->enabled(1) >, it can be used to
+profile explicitly, although MyApp.pm still won't profile nor output anything
+by itself.
+
 =head2 $c->stats_class
 
-Returns or sets the stats (timing statistics) class.
+Returns or sets the stats (timing statistics) class. L<Catalyst::Stats|Catalyst::Stats> is used by default.
 
 =head2 $c->use_stats
 
-Returns 1 when stats collection is enabled.  Stats collection is enabled
-when the -Stats options is set, debug is on or when the <MYAPP>_STATS
-environment variable is set.
+Returns 1 when L<< stats collection|/"-Stats" >> is enabled.
 
 Note that this is a static method, not an accessor and should be overridden
 by declaring C<sub use_stats { 1 }> in your MyApp.pm, not by calling C<< $c->use_stats(1) >>.
@@ -2939,6 +3059,12 @@ to be shown in hit debug tables in the test server.
 
 =item *
 
+C<use_request_uri_for_path> - Controlls if the C<REQUEST_URI> or C<PATH_INFO> environment
+variable should be used for determining the request path. See L<Catalyst::Engine::CGI/PATH DECODING>
+for more information.
+
+=item *
+
 C<using_frontend_proxy> - See L</PROXY SUPPORT>.
 
 =back
@@ -3168,6 +3294,8 @@ random: Roland Lammel <lammel@cpan.org>
 
 Robert Sedlacek C<< <rs@474.at> >>
 
+SpiceMan: Marcel Montes
+
 sky: Arthur Bergman
 
 szbalint: Balint Szilakszi <szbalint@cpan.org>
@@ -3182,8 +3310,18 @@ Will Hawes C<info@whawes.co.uk>
 
 willert: Sebastian Willert <willert@cpan.org>
 
+wreis: Wallace Reis <wallace@reis.org.br>
+
 Yuval Kogman, C<nothingmuch@woobling.org>
 
+rainboxx: Matthias Dietrich, C<perl@rainboxx.de>
+
+dd070: Dhaval Dhanani <dhaval070@gmail.com>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2005, the above named PROJECT FOUNDER and CONTRIBUTORS.
+
 =head1 LICENSE
 
 This library is free software. You can redistribute it and/or modify it under