3 class CatalystX::Declarative::Keyword::Action
4 with MooseX::Declare::Syntax::KeywordHandling {
8 use Perl6::Junction qw( any );
9 use Data::Dump qw( pp );
11 use aliased 'MooseX::Method::Signatures::Meta::Method';
12 use aliased 'MooseX::MethodAttributes::Role::Meta::Method', 'AttributeRole';
15 method parse (Object $ctx) {
17 # somewhere to put the attributes
20 my $skipped_declarator;
23 until (do { $ctx->skipspace; $ctx->peek_next_char } eq any qw( ; { } )) {
28 if ($ctx->peek_next_char eq ',') {
30 my $linestr = $ctx->get_linestr;
31 substr($linestr, $ctx->offset, 1) = '';
32 $ctx->set_linestr($linestr);
37 # next thing should be an option name
42 $ctx->skip_declarator;
43 $skipped_declarator++;
46 or croak "Expected option token, not " . substr($ctx->get_linestr, $ctx->offset);
48 # we need to be able to handle the rest
49 my $handler = $self->can("_handle_${option}_option")
50 or croak "Unknown action option: $option";
53 push @populators, $self->$handler($ctx, \%attributes);
56 croak "Need an action specification"
57 unless exists $attributes{Signature};
59 my $name = $attributes{Subname};
60 my $method = Method->wrap(
61 signature => qq{($attributes{Signature})},
62 package_name => $ctx->get_curstash_name,
69 $attributes{PathPart} ||= "'$name'";
71 delete $attributes{CaptureArgs}
72 if exists $attributes{Args};
74 $attributes{CaptureArgs} = 0
75 unless exists $attributes{Args}
76 or exists $attributes{CaptureArgs};
78 if ($ctx->peek_next_char eq '{') {
79 $ctx->inject_if_block($ctx->scope_injector_call . $method->injectable_code);
82 $ctx->inject_code_parts_here(
84 $ctx->scope_injector_call,
85 $method->injectable_code,
89 AttributeRole->meta->apply($method);
91 my @attributes = map {
94 sprintf('(%s)', $attributes{ $_ }),
98 return $ctx->shadow(sub (&) {
101 $method->_set_actual_body(shift);
102 $method->{attributes} = \@attributes;
104 $class->meta->add_method($name, $method);
105 $class->meta->register_method_attributes($class->can($method->name), \%attributes);
109 method _handle_action_option (Object $ctx, HashRef $attrs) {
112 my $name = $ctx->strip_name
113 or croak "Anonymous actions not yet supported";
116 my $proto = $ctx->strip_proto || '';
117 $proto = join(', ', 'Object $self: Object $ctx', $proto || ());
119 $attrs->{Subname} = $name;
120 $attrs->{Signature} = $proto;
125 method _handle_is_option (Object $ctx, HashRef $attrs) {
127 my $what = $ctx->strip_name
128 or croak "Expected symbol token after is symbol, not " . substr($ctx->get_linestr, $ctx->offset);
133 if ($what eq any qw( end endpoint final )) {
134 my $count = $self->_count_positional_arguments($method);
135 $attrs->{Args} = defined($count) ? $count : '';
137 elsif ($what eq 'private') {
138 $attrs->{Private} = 1;
143 method _handle_under_option (Object $ctx, HashRef $attrs) {
145 my $target = $self->_strip_actionpath($ctx);
146 $attrs->{Chained} = "'$target'";
151 my $count = $self->_count_positional_arguments($method);
152 $attrs->{CaptureArgs} = $count
157 method _handle_chains_option (Object $ctx, HashRef $attrs) {
160 $ctx->strip_name eq 'to'
161 or croak "Expected to token after chains symbol, not " . substr($ctx->get_linestr, $ctx->offset);
163 return $self->_handle_under_option($ctx, $attrs);
166 method _handle_as_option (Object $ctx, HashRef $attrs) {
170 my $path = $self->_strip_actionpath($ctx);
171 $attrs->{PathPart} = "'$path'";
176 method _count_positional_arguments (Object $method) {
177 my $signature = $method->_parsed_signature;
179 if ($signature->has_positional_params) {
180 my $count = @{ scalar($signature->positional_params) };
182 if ($count and ($signature->positional_params)[-1]->sigil eq '@') {
192 method _strip_actionpath (Object $ctx) {
195 my $linestr = $ctx->get_linestr;
196 my $rest = substr($linestr, $ctx->offset);
198 if ($rest =~ /^ ( [_a-z] [_a-z0-9]* ) \b/ix) {
199 substr($linestr, $ctx->offset, length($1)) = '';
200 $ctx->set_linestr($linestr);
203 elsif ($rest =~ /^ ' ( (?:[.:;,_a-z0-9]|\/)* ) ' /ix) {
204 substr($linestr, $ctx->offset, length($1) + 2) = '';
205 $ctx->set_linestr($linestr);
209 croak "Invalid syntax for action path: $rest";