From f0f43f5fdf1af2e92ba04aab7cc7ecd83f7c06ff Mon Sep 17 00:00:00 2001 From: Dave Baird Date: Sun, 8 Nov 2015 21:51:26 +0100 Subject: [PATCH] Fix method names clash in Moose Role When flattening all endpoint API methods into a single class, some method names may clash, e.g. every API has a new() method. So we skip them, they must be accessed via the API method. Warnings are emitted to document skipped methods. --- .gitignore | 4 +- .../src/main/resources/perl/Role.mustache | 38 ++++++++++--- .../perl/lib/WWW/SwaggerClient/Role.pm | 38 ++++++++++--- samples/client/petstore/perl/pom.xml | 13 +++++ .../client/petstore/perl/t/03_api_factory.t | 14 ++++- samples/client/petstore/perl/t/04_role.t | 56 +++++++++++++++++++ 6 files changed, 143 insertions(+), 20 deletions(-) create mode 100644 samples/client/petstore/perl/t/04_role.t diff --git a/.gitignore b/.gitignore index 37b18e5e3cc..d55a8014a34 100644 --- a/.gitignore +++ b/.gitignore @@ -58,4 +58,6 @@ samples/client/petstore/python/.venv/ *.mustache~ *.java~ -*.pm~ \ No newline at end of file +*.pm~ +*.xml~ +*.t~ \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/perl/Role.mustache b/modules/swagger-codegen/src/main/resources/perl/Role.mustache index f349317c9ab..fee13feb040 100644 --- a/modules/swagger-codegen/src/main/resources/perl/Role.mustache +++ b/modules/swagger-codegen/src/main/resources/perl/Role.mustache @@ -4,7 +4,7 @@ use utf8; use Moose::Role; use namespace::autoclean; use Class::Inspector; - +use Log::Any qw($log); use WWW::{{moduleName}}::ApiFactory; requires 'auth_setup_handler'; @@ -23,21 +23,41 @@ has api_factory => ( is => 'ro', sub BUILD { my $self = shift; - my %outsiders = map {$_ => 1} qw( croak ); + # ignore these symbols imported into API namespaces + my %outsiders = map {$_ => 1} qw( new croak ); - foreach my $name ($self->api_factory->apis_available) { - - my $att_name = sprintf "%s_api", lc($name); - my $api_class = $self->api_factory->classname_for($name); + my %delegates; + + # collect the methods callable on each API + foreach my $api_name ($self->api_factory->apis_available) { + my $api_class = $self->api_factory->classname_for($api_name); my $methods = Class::Inspector->methods($api_class, 'expanded'); my @local_methods = grep {! $outsiders{$_}} map {$_->[2]} grep {$_->[1] eq $api_class} @$methods; - + push( @{$delegates{$_}}, {api_name => $api_name, api_class => $api_class} ) for @local_methods; + } + + # remove clashes + foreach my $method (keys %delegates) { + if ( @{$delegates{$method}} > 1 ) { + my ($apis) = delete $delegates{$method}; + foreach my $api (@$apis) { + warn sprintf "Cannot delegate %s (use \$self->%s_api->%s instead)\n", $method, lc($api->{api_name}), $method; + } + } + } + + # build the flattened API + foreach my $api_name ($self->api_factory->apis_available) { + my $att_name = sprintf "%s_api", lc($api_name); + my $api_class = $self->api_factory->classname_for($api_name); + my @delegated = grep { $delegates{$_}->[0]->{api_name} eq $api_name } keys %delegates; + $log->debugf("Adding API: '%s' handles %s", $att_name, join ', ', @delegated); $self->meta->add_attribute( $att_name => ( is => 'ro', isa => $api_class, - default => sub {$self->api_factory->get_api($name)}, + default => sub {$self->api_factory->get_api($api_name)}, lazy => 1, - handles => \@local_methods, + handles => \@delegated, ) ); } } diff --git a/samples/client/petstore/perl/lib/WWW/SwaggerClient/Role.pm b/samples/client/petstore/perl/lib/WWW/SwaggerClient/Role.pm index c47f0908844..4b63a483eae 100644 --- a/samples/client/petstore/perl/lib/WWW/SwaggerClient/Role.pm +++ b/samples/client/petstore/perl/lib/WWW/SwaggerClient/Role.pm @@ -4,7 +4,7 @@ use utf8; use Moose::Role; use namespace::autoclean; use Class::Inspector; - +use Log::Any qw($log); use WWW::SwaggerClient::ApiFactory; requires 'auth_setup_handler'; @@ -23,21 +23,41 @@ has api_factory => ( is => 'ro', sub BUILD { my $self = shift; - my %outsiders = map {$_ => 1} qw( croak ); + # ignore these symbols imported into API namespaces + my %outsiders = map {$_ => 1} qw( new croak ); - foreach my $name ($self->api_factory->apis_available) { - - my $att_name = sprintf "%s_api", lc($name); - my $api_class = $self->api_factory->classname_for($name); + my %delegates; + + # collect the methods callable on each API + foreach my $api_name ($self->api_factory->apis_available) { + my $api_class = $self->api_factory->classname_for($api_name); my $methods = Class::Inspector->methods($api_class, 'expanded'); my @local_methods = grep {! $outsiders{$_}} map {$_->[2]} grep {$_->[1] eq $api_class} @$methods; - + push( @{$delegates{$_}}, {api_name => $api_name, api_class => $api_class} ) for @local_methods; + } + + # remove clashes + foreach my $method (keys %delegates) { + if ( @{$delegates{$method}} > 1 ) { + my ($apis) = delete $delegates{$method}; + foreach my $api (@$apis) { + warn sprintf "Cannot delegate %s (use \$self->%s_api->%s instead)\n", $method, lc($api->{api_name}), $method; + } + } + } + + # build the flattened API + foreach my $api_name ($self->api_factory->apis_available) { + my $att_name = sprintf "%s_api", lc($api_name); + my $api_class = $self->api_factory->classname_for($api_name); + my @delegated = grep { $delegates{$_}->[0]->{api_name} eq $api_name } keys %delegates; + $log->debugf("Adding API: '%s' handles %s", $att_name, join ', ', @delegated); $self->meta->add_attribute( $att_name => ( is => 'ro', isa => $api_class, - default => sub {$self->api_factory->get_api($name)}, + default => sub {$self->api_factory->get_api($api_name)}, lazy => 1, - handles => \@local_methods, + handles => \@delegated, ) ); } } diff --git a/samples/client/petstore/perl/pom.xml b/samples/client/petstore/perl/pom.xml index 4034cf0a687..4aa0040f82e 100644 --- a/samples/client/petstore/perl/pom.xml +++ b/samples/client/petstore/perl/pom.xml @@ -65,6 +65,19 @@ + + Test::More for Role + integration-test + + exec + + + perl + + t/04_role.t + + + diff --git a/samples/client/petstore/perl/t/03_api_factory.t b/samples/client/petstore/perl/t/03_api_factory.t index f96bffa7a84..2e6ee6317f9 100644 --- a/samples/client/petstore/perl/t/03_api_factory.t +++ b/samples/client/petstore/perl/t/03_api_factory.t @@ -1,4 +1,4 @@ -use Test::More tests => 13; +use Test::More tests => 19; use Test::Exception; use lib 'lib'; @@ -32,3 +32,15 @@ is $pet->category->id, '22', 'got the proper category id'; is $pet->category->name, 'perl', 'got the proper category name'; is $pet->tags->[0]->name, 'just kidding', 'got the proper tag name'; is $pet->tags->[0]->id, '11', 'got the proper tag id'; + + +my $add_pet = $pet_api->add_pet(body => $pet); +my $get_pet = $pet_api->get_pet_by_id(pet_id => $pet_id); + +is $get_pet->id, '10008', 'stored and retrieved: got the proper pet id'; +is $get_pet->name, 'perl test', 'stored and retrieved: got the proper pet name'; +is $get_pet->category->id, '22', 'stored and retrieved: got the proper category id'; +is $get_pet->category->name, 'perl', 'stored and retrieved: got the proper category name'; +is $get_pet->tags->[0]->name, 'just kidding', 'stored and retrieved: got the proper tag name'; +is $get_pet->tags->[0]->id, '11', 'stored and retrieved: got the proper tag id'; + diff --git a/samples/client/petstore/perl/t/04_role.t b/samples/client/petstore/perl/t/04_role.t new file mode 100644 index 00000000000..b35149c9d83 --- /dev/null +++ b/samples/client/petstore/perl/t/04_role.t @@ -0,0 +1,56 @@ +use Test::More tests => 15; +use Test::Exception; + +use lib 'lib'; +use strict; +use warnings; + +use MooseX::amine; +use Class::Inspector; +use Data::Dumper; + +use Log::Any::Adapter ('File', '/home/dave/bookeo_api.log'); + + +SKIP: { + eval " + package MyApp; + use Moose; + with 'WWW::SwaggerClient::Role'; + + sub auth_setup_handler {} + "; + + skip 'Moose not installed', 15 if $@; + + +my $api = MyApp->new; + +my $pet_id = 10008; +# note - we don't need to 'use' these modules because they've already been loaded by ApiFactory +my ($category, $tag, $pet); +lives_ok { $category = WWW::SwaggerClient::Object::Category->new('id' => '22', 'name' => 'perl') } 'Category.pm loaded OK'; +lives_ok { $tag = WWW::SwaggerClient::Object::Tag->new('id' => '11', 'name' => 'just kidding') } 'Tag.pm loaded OK'; +lives_ok { $pet = WWW::SwaggerClient::Object::Pet->new('id' => $pet_id, 'name' => 'perl test', + "photoUrls" => ['123', 'oop'], 'tags' => [$tag], 'status' => 'pending', 'category' => $category) } 'Pet.pm loaded OK'; + +is $pet->id, '10008', 'got the proper pet id'; +is $pet->name, 'perl test', 'got the proper pet name'; +is $pet->category->id, '22', 'got the proper category id'; +is $pet->category->name, 'perl', 'got the proper category name'; +is $pet->tags->[0]->name, 'just kidding', 'got the proper tag name'; +is $pet->tags->[0]->id, '11', 'got the proper tag id'; + + +my $add_pet = $api->add_pet(body => $pet); +my $get_pet = $api->get_pet_by_id(pet_id => $pet_id); + +is $get_pet->id, '10008', 'stored and retrieved: got the proper pet id'; +is $get_pet->name, 'perl test', 'stored and retrieved: got the proper pet name'; +is $get_pet->category->id, '22', 'stored and retrieved: got the proper category id'; +is $get_pet->category->name, 'perl', 'stored and retrieved: got the proper category name'; +is $get_pet->tags->[0]->name, 'just kidding', 'stored and retrieved: got the proper tag name'; +is $get_pet->tags->[0]->id, '11', 'stored and retrieved: got the proper tag id'; + +} # / SKIP +