Subject: | [FEATURE PATCH] Add support for Moose v1.03 -version declarations |
Hello,
When I was fooling around with dzil, I brought up that it was
counter-intuitive to repeat the module twice just so the prereq scanner
could pick up the version.
use "Foo::Baz" 2.5;
with 'Foo::Baz';
Rafl++ noticed this and immediately made a Moose 1.03 release that
added support for declaring versions in the with/extends methods.
with 'Foo::Baz' => { -version => 2.5 };
This patch adds support for that, and a bunch of tests :) This was my
"first" time trying to do something moderately complicated in PPI, ha!
Hope others will find this useful, and thanks again for your excellent
work on this!
--
~Apocalypse
Subject: | prereqscanner.patch |
diff --git a/lib/Perl/PrereqScanner/Scanner.pm b/lib/Perl/PrereqScanner/Scanner.pm
index 31240ea..11c7973 100644
--- a/lib/Perl/PrereqScanner/Scanner.pm
+++ b/lib/Perl/PrereqScanner/Scanner.pm
@@ -24,9 +24,12 @@ requires 'scan_for_prereqs';
# -- rjbs, 2010-04-06
sub _q_contents {
my ($self, $token) = @_;
- my @contents = $token->isa('PPI::Token::QuoteLike::Words')
- ? ( $token->literal )
- : ( $token->string );
+ my @contents;
+ if ( $token->isa('PPI::Token::QuoteLike::Words') || $token->isa('PPI::Token::Number') ) {
+ @contents = $token->literal;
+ } else {
+ @contents = $token->string;
+ }
return @contents;
}
diff --git a/lib/Perl/PrereqScanner/Scanner/Moose.pm b/lib/Perl/PrereqScanner/Scanner/Moose.pm
index 01ce2ef..016d938 100644
--- a/lib/Perl/PrereqScanner/Scanner/Moose.pm
+++ b/lib/Perl/PrereqScanner/Scanner/Moose.pm
@@ -21,15 +21,72 @@ sub scan_for_prereqs {
my ($self, $ppi_doc, $req) = @_;
# Moose-based roles / inheritance
- my @bases =
- map { $self->_q_contents( $_ ) }
- grep { $_->isa('PPI::Token::Quote') || $_->isa('PPI::Token::QuoteLike') }
- map { $_->children }
+ my @chunks =
+ map { [ $_->children ] }
grep { $_->child(0)->literal =~ m{\Awith|extends\z} }
grep { $_->child(0)->isa('PPI::Token::Word') }
@{ $ppi_doc->find('PPI::Statement') || [] };
- $req->add_minimum($_ => 0) for @bases;
+ foreach my $hunk ( @chunks ) {
+ # roles/inheritance *WITH* version declaration ( added in Moose 1.03 )
+ if ( grep { $_->isa('PPI::Structure::Constructor') } @$hunk ) {
+ # possibly contains a version declaration!
+ my( $pkg, $done );
+ foreach my $elem ( @$hunk ) {
+ # Scan for the first quote-like word, which is the package name
+ if ( $elem->isa('PPI::Token::Quote') || $elem->isa('PPI::Token::QuoteLike') ) {
+ $pkg = ( $self->_q_contents( $elem ) )[0];
+ undef $done;
+ next;
+ }
+
+ # skip over the fluff and look for the version block
+ if ( $pkg and $elem->isa('PPI::Structure::Constructor') ) {
+ foreach my $subelem ( $elem->children ) {
+ # skip over the fluff and look for the real version code
+ if ( $subelem->isa('PPI::Statement') ) {
+ my $found_ver;
+ foreach my $code ( $subelem->children ) {
+ # skip over the fluff until we're sure we saw the version declaration
+ if ( $code->isa('PPI::Token::Word') and $code->literal eq '-version' ) {
+ $found_ver++;
+ next;
+ }
+
+ if ( $found_ver and ( $code->isa('PPI::Token::Quote') || $code->isa('PPI::Token::QuoteLike') || $code->isa('PPI::Token::Number') ) ) {
+ $req->add_minimum( $pkg => ( $self->_q_contents( $code ) )[0] );
+ $done++;
+ last;
+ }
+ }
+
+ # Did we fail to find the ver?
+ if ( $found_ver and ! $done ) {
+ die "Possible internal error!";
+ }
+ }
+ }
+
+ # Failed to find version-specific stuff
+ if ( ! $done ) {
+ $req->add_minimum( $pkg => 0 );
+ next;
+ }
+ }
+ }
+
+ # If we found a pkg but no done, this must be the "last" pkg to be declared and it has no version
+ if ( $pkg and ! $done ) {
+ $req->add_minimum( $pkg => 0 );
+ }
+ } else {
+ # regular parse
+ $req->add_minimum( $_ => 0 ) for
+ map { $self->_q_contents( $_ ) }
+ grep { $_->isa('PPI::Token::Quote') || $_->isa('PPI::Token::QuoteLike') }
+ @$hunk;
+ }
+ }
}
1;
diff --git a/t/autoprereq.t b/t/autoprereq.t
index 957738e..acf5eaa 100644
--- a/t/autoprereq.t
+++ b/t/autoprereq.t
@@ -46,10 +46,29 @@ sub prereq_is {
};
}
-prereq_is('', { }, '(empty string)');
-prereq_is('use Use::NoVersion;', { 'Use::NoVersion' => 0 });
-prereq_is('use Use::Version 0.50;', { 'Use::Version' => '0.50' });
-prereq_is('require Require;', { Require => 0 });
+# basic sanity tests
+prereq_is('', {}, '(empty string)');
+
+prereq_is(
+ 'use Use::NoVersion;',
+ {
+ 'Use::NoVersion' => 0,
+ },
+);
+
+prereq_is(
+ 'use Use::Version 0.50;',
+ {
+ 'Use::Version' => '0.50',
+ },
+);
+
+prereq_is(
+ 'require Require;',
+ {
+ Require => 0,
+ },
+);
prereq_is(
'use Use::Version 0.50; use Use::Version 1.00;',
@@ -67,12 +86,34 @@ prereq_is(
prereq_is(
'use Import::IgnoreAPI require => 1;',
- { 'Import::IgnoreAPI' => 0 },
+ {
+ 'Import::IgnoreAPI' => 0,
+ },
);
-
# Moose features
-prereq_is("with 'With::Single';", { 'With::Single' => 0 });
+prereq_is(
+ 'extends "Foo::Bar";',
+ {
+ 'Foo::Bar' => 0,
+ },
+);
+
+prereq_is(
+ 'extends "Foo::Bar"; extends "Foo::Baz";',
+ {
+ 'Foo::Bar' => 0,
+ 'Foo::Baz' => 0,
+ },
+);
+
+prereq_is(
+ "with 'With::Single';",
+ {
+ 'With::Single' => 0,
+ },
+);
+
prereq_is(
"extends 'Extends::List1', 'Extends::List2';",
{
@@ -113,7 +154,13 @@ prereq_is(
},
);
-prereq_is('use base "Base::QQ1";', { 'Base::QQ1' => 0 });
+prereq_is(
+ 'use base "Base::QQ1";',
+ {
+ 'Base::QQ1' => 0,
+ },
+);
+
prereq_is(
'use base 10 "Base::QQ1";',
{
@@ -121,9 +168,13 @@ prereq_is(
base => 10,
},
);
+
prereq_is(
'use base qw{ Base::QW1 Base::QW2 };',
- { 'Base::QW1' => 0, 'Base::QW2' => 0 },
+ {
+ 'Base::QW1' => 0,
+ 'Base::QW2' => 0,
+ },
);
prereq_is(
@@ -190,4 +241,67 @@ prereq_is(
q{use strict; use warnings; use lib '.'; use feature ':5.10';},
{},
);
+
+# test cases for Moose 1.03 -version extension
+prereq_is(
+ 'extends "Foo::Bar"=>{-version=>"1.1"};',
+ {
+ 'Foo::Bar' => '1.1',
+ },
+);
+
+prereq_is(
+ 'extends "Foo::Bar" => { -version => \'1.1\' };',
+ {
+ 'Foo::Bar' => '1.1',
+ },
+);
+
+prereq_is(
+ 'extends "Foo::Bar" => { -version => 13.3 };',
+ {
+ 'Foo::Bar' => '13.3',
+ },
+);
+
+prereq_is(
+ 'extends "Foo::Bar" => { -version => \'1.1\' }; extends "Foo::Baz" => { -version => 5 };',
+ {
+ 'Foo::Bar' => '1.1',
+ 'Foo::Baz' => 5,
+ },
+);
+
+prereq_is(
+ 'extends "Foo::Bar"=>{-version=>1},"Foo::Baz"=>{-version=>2};',
+ {
+ 'Foo::Bar' => 1,
+ 'Foo::Baz' => 2,
+ },
+);
+
+prereq_is(
+ 'extends "Foo::Bar" => { -version => "4.3.2" }, "Foo::Baz" => { -version => 2.44894 };',
+ {
+ 'Foo::Bar' => '4.3.2',
+ 'Foo::Baz' => 2.44894,
+ },
+);
+
+prereq_is(
+ 'with "With::Single" => { -excludes => "method", -version => "1.1.1" }, "With::Double";',
+ {
+ 'With::Single' => '1.1.1',
+ 'With::Double' => 0,
+ },
+);
+
+prereq_is(
+ 'with "With::Single" => { -wow => { -wow => { a => b } }, -version => "1.1.1" }, "With::Double";',
+ {
+ 'With::Single' => '1.1.1',
+ 'With::Double' => 0,
+ },
+);
+
done_testing;