use style;
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Controller.pm
index b1c4c94..fb6df0d 100644 (file)
@@ -13,28 +13,36 @@ use Catalyst::Utils;
 
 with 'Catalyst::Component::ApplicationAttribute';
 
-has path_prefix =>
-    (
-     is => 'rw',
-     isa => 'Str',
-     init_arg => 'path',
-     predicate => 'has_path_prefix',
-    );
-
-has action_namespace =>
-    (
-     is => 'rw',
-     isa => 'Str',
-     init_arg => 'namespace',
-     predicate => 'has_action_namespace',
-    );
-
-has actions =>
-    (
-     accessor => '_controller_actions',
-     isa => 'HashRef',
-     init_arg => undef,
-    );
+has path_prefix => (
+    is        => 'rw',
+    isa       => 'Str',
+    init_arg  => 'path',
+    predicate => 'has_path_prefix',
+);
+
+has action_namespace => (
+    is        => 'rw',
+    isa       => 'Str',
+    init_arg  => 'namespace',
+    predicate => 'has_action_namespace',
+);
+
+has actions => (
+    accessor => '_controller_actions',
+    isa      => 'HashRef',
+    init_arg => undef,
+);
+
+has action_args => (is => 'ro');
+
+# ->config(actions => { '*' => ...
+has _all_actions_attributes => (
+    is       => 'ro',
+    isa      => 'HashRef',
+    init_arg => undef,
+    lazy     => 1,
+    builder  => '_build__all_actions_attributes',
+);
 
 sub BUILD {
     my ($self, $args) = @_;
@@ -42,9 +50,15 @@ sub BUILD {
     my $actions = delete $args->{actions} || {};
     my $attr_value = $self->merge_config_hashes($actions, $action);
     $self->_controller_actions($attr_value);
-}
 
+    # trigger lazy builder
+    $self->_all_actions_attributes;
+}
 
+sub _build__all_actions_attributes {
+    my ($self) = @_;
+    delete $self->_controller_actions->{'*'} || {};
+}
 
 =head1 NAME
 
@@ -182,11 +196,10 @@ around path_prefix => sub {
 sub get_action_methods {
     my $self = shift;
     my $meta = find_meta($self) || confess("No metaclass setup for $self");
-    confess("Metaclass "
-          . ref($meta) . " for "
-          . $meta->name
-          . " cannot support register_actions." )
-      unless $meta->can('get_nearest_methods_with_attributes');
+    confess(
+        sprintf "Metaclass %s for %s cannot support register_actions.",
+            ref $meta, $meta->name,
+    ) unless $meta->can('get_nearest_methods_with_attributes');
     my @methods = $meta->get_nearest_methods_with_attributes;
 
     # actions specified via config are also action_methods
@@ -194,11 +207,9 @@ sub get_action_methods {
         @methods,
         map {
             $meta->find_method_by_name($_)
-              || confess( 'Action "'
-                  . $_
-                  . '" is not available from controller '
-                  . ( ref $self ) )
-          } keys %{ $self->_controller_actions }
+                || confess( sprintf 'Action "%s" is not available from controller %s',
+                            $_, ref $self )
+        } keys %{ $self->_controller_actions }
     ) if ( ref $self );
     return uniq @methods;
 }
@@ -267,7 +278,11 @@ sub create_action {
     my %args = @_;
 
     my $class = $self->action_class(%args);
-    my $action_args = $self->config->{action_args};
+    my $action_args = (
+        ref($self)
+            ? $self->action_args
+            : $self->config->{action_args}
+    );
 
     my %extra_args = (
         %{ $action_args->{'*'}           || {} },
@@ -296,23 +311,32 @@ sub _parse_attrs {
         }
     }
 
-    #I know that the original behavior was to ignore action if actions was set
-    # but i actually think this may be a little more sane? we can always remove
-    # the merge behavior quite easily and go back to having actions have
-    # presedence over action by modifying the keys. i honestly think this is
-    # superior while mantaining really high degree of compat
-    my $actions;
+    my ($actions_config, $all_actions_config);
     if( ref($self) ) {
-        $actions = $self->_controller_actions;
+        $actions_config = $self->_controller_actions;
+        # No, you're not getting actions => { '*' => ... } with actions in MyApp.
+        $all_actions_config = $self->_all_actions_attributes;
     } else {
         my $cfg = $self->config;
-        $actions = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
+        $actions_config = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
+        $all_actions_config = {};
     }
 
-    %raw_attributes = ((exists $actions->{'*'} ? %{$actions->{'*'}} : ()),
-                       %raw_attributes,
-                       (exists $actions->{$name} ? %{$actions->{$name}} : ()));
+    %raw_attributes = (
+        %raw_attributes,
+        # Note we deep copy array refs here to stop crapping on config
+        # when attributes are parsed. RT#65463
+        exists $actions_config->{$name} ? map { ref($_) eq 'ARRAY' ? [ @$_ ] : $_ } %{ $actions_config->{$name } } : (),
+    );
 
+    # Private actions with additional attributes will raise a warning and then
+    # be ignored. Adding '*' arguments to the default _DISPATCH / etc. methods,
+    # which are Private, will prevent those from being registered. They should
+    # probably be turned into :Actions instead, or we might want to otherwise
+    # disambiguate between those built-in internal actions and user-level
+    # Private ones.
+    %raw_attributes = (%{ $all_actions_config }, %raw_attributes)
+        unless $raw_attributes{Private};
 
     my %final_attributes;
 
@@ -461,7 +485,7 @@ Sets 'path_prefix', as described below.
 
 Allows you to set the attributes that the dispatcher creates actions out of.
 This allows you to do 'rails style routes', or override some of the
-attribute defintions of actions composed from Roles.
+attribute definitions of actions composed from Roles.
 You can set arguments globally (for all actions of the controller) and
 specifically (for a single action).