File Coverage

blib/lib/WebAPI/DBIC/RouteMaker.pm
Criterion Covered Total %
statement 30 85 35.2
branch 0 24 0.0
condition 0 13 0.0
subroutine 10 16 62.5
pod 0 5 0.0
total 40 143 27.9


line stmt bran cond sub pod time code
1             package WebAPI::DBIC::RouteMaker;
2             $WebAPI::DBIC::RouteMaker::VERSION = '0.004001';
3              
4 2     2   10331905 use Moo;
  2         30167  
  2         37  
5              
6 2     2   3043 use Module::Runtime qw(use_module);
  2         7  
  2         17  
7 2     2   75 use Sub::Util qw(subname);
  2         35  
  2         135  
8 2     2   9 use Scalar::Util qw(blessed);
  2         2  
  2         192  
9 2     2   16 use Carp qw(croak confess);
  2         2  
  2         162  
10 2     2   852 use Safe::Isa;
  2         669  
  2         418  
11 2     2   839 use Devel::Dwarn;
  2         16038  
  2         10  
12              
13 2     2   1104 use namespace::clean -except => [qw(meta)];
  2         20604  
  2         17  
14 2     2   1560 use MooX::StrictConstructor;
  2         21090  
  2         11  
15              
16 2     2   33861 use WebAPI::DBIC::Route;
  2         5  
  2         17  
17              
18             has resource_class_for_item => (is => 'ro', default => 'WebAPI::DBIC::Resource::GenericItem');
19             has resource_class_for_item_invoke => (is => 'ro', default => 'WebAPI::DBIC::Resource::GenericItemInvoke');
20             has resource_class_for_set => (is => 'ro', default => 'WebAPI::DBIC::Resource::GenericSet');
21             has resource_class_for_set_invoke => (is => 'ro', default => 'WebAPI::DBIC::Resource::GenericSetInvoke');
22             has resource_default_args => (is => 'ro', default => sub { {} });
23              
24             has type_namer => (
25             is => 'ro',
26             default => sub {
27             require WebAPI::DBIC::TypeNamer;
28             return WebAPI::DBIC::TypeNamer->new
29             },
30             );
31              
32             sub _qr_names {
33 0 0   0     my $names_r = join "|", map { quotemeta $_ } @_ or confess "panic";
  0            
34 0           return qr/^(?:$names_r)$/x;
35             }
36              
37             sub make_routes_for_resultset {
38 0     0 0   my ($self, $path, $set, %opts) = @_;
39              
40 0 0         if ($ENV{WEBAPI_DBIC_DEBUG}) {
41 0           warn sprintf "Auto routes for /%s => %s\n",
42             $path, $set->result_class;
43             }
44              
45 0           my @routes;
46              
47 0           push @routes, $self->make_routes_for_set($path, $set, {
48             invokable_methods => delete($opts{invokeable_methods_on_set}),
49             });
50              
51 0           push @routes, $self->make_routes_for_item($path, $set, {
52             invokable_methods => delete($opts{invokeable_methods_on_item})
53             });
54              
55 0 0         croak "Unrecognized options: @{[ keys %opts ]}"
  0            
56             if %opts;
57              
58 0           return @routes;
59             }
60              
61             sub make_routes_for_item {
62 0     0 0   my ($self, $path, $set, $opts) = @_;
63 0   0       $opts ||= {};
64 0           my $methods = $opts->{invokable_methods};
65              
66 0           use_module $self->resource_class_for_item;
67 0           my $id_unique_constraint_name = $self->resource_class_for_item->id_unique_constraint_name;
68 0           my $key_fields = { $set->result_source->unique_constraints }->{ $id_unique_constraint_name };
69              
70 0 0         unless ($key_fields) {
71 0           warn sprintf "/%s/:id route skipped because %s has no '$id_unique_constraint_name' constraint defined\n",
72             $path, $set->result_class;
73 0           return;
74             }
75              
76             # id fields have sequential numeric names
77             # so .../:1 for a resource with a single key field
78             # and .../:1/:2/:3 etc for a resource with multiple key fields
79 0           my $item_path_spec = join "/", map { ":$_" } 1 .. @$key_fields;
  0            
80              
81 0           my @routes;
82              
83 0           push @routes, WebAPI::DBIC::Route->new( # item
84             path => "$path/$item_path_spec",
85             resource_class => $self->resource_class_for_item,
86             resource_args => {
87 0           %{ $self->resource_default_args },
88             set => $set,
89             type_namer => $self->type_namer,
90             },
91             );
92              
93             # XXX temporary hack just for testing
94 0 0         push @$methods, 'get_column'
95             if $set->result_class eq 'TestSchema::Result::Artist';
96              
97 0           push @routes, WebAPI::DBIC::Route->new( # method call on item
98             path => "$path/$item_path_spec/invoke/:method",
99             validations => { method => _qr_names(@$methods), },
100             resource_class => $self->resource_class_for_item_invoke,
101             resource_args => {
102 0 0 0       %{ $self->resource_default_args },
103             set => $set,
104             type_namer => $self->type_namer,
105             },
106             ) if $methods && @$methods;
107              
108 0           return @routes;
109             }
110              
111             sub make_routes_for_set {
112 0     0 0   my ($self, $path, $set, $opts) = @_;
113 0   0       $opts ||= {};
114 0           my $methods = $opts->{invokable_methods};
115              
116 0           my @routes;
117              
118 0           push @routes, WebAPI::DBIC::Route->new(
119             path => $path,
120             resource_class => $self->resource_class_for_set,
121             resource_args => {
122 0           %{ $self->resource_default_args },
123             set => $set,
124             type_namer => $self->type_namer,
125             },
126             );
127              
128             # XXX temporary hack just for testing
129 0 0         push @$methods, 'count'
130             if $set->result_class eq 'TestSchema::Result::Artist';
131              
132 0           push @routes, WebAPI::DBIC::Route->new( # method call on set
133             path => "$path/invoke/:method",
134             validations => { method => _qr_names(@$methods) },
135             resource_class => $self->resource_class_for_set_invoke,
136             resource_args => {
137 0 0 0       %{ $self->resource_default_args },
138             set => $set,
139             type_namer => $self->type_namer,
140             },
141             ) if $methods && @$methods;
142              
143 0           return @routes;
144             }
145              
146             sub make_root_route {
147 0     0 0   my $self = shift;
148 0           my $root_route = WebAPI::DBIC::Route->new(
149             path => '',
150             resource_class => 'WebAPI::DBIC::Resource::GenericRoot',
151             resource_args => {
152 0           %{ $self->resource_default_args },
153             type_namer => $self->type_namer,
154             },
155             );
156 0           return $root_route;
157             }
158              
159             sub make_routes_for {
160 0     0 0   my ($self, $route_spec) = @_;
161              
162             # route_spec:
163             # $schema->source('People')
164             # { set => $schema->source('People'), path => undef }
165             # { set => $schema->resultset('People')->search({ tall=>1 }), path => 'tall_people' }
166             # WebAPI::DBIC::Route->new(...) # gets used directly
167              
168 0 0         return $route_spec if $route_spec->$_isa('WebAPI::DBIC::Route');
169              
170 0           my %opts;
171              
172 0 0         if (ref $route_spec eq 'HASH') {
173             # invokeable_methods_on_item => undef,
174             # invokeable_methods_on_set => undef,
175 0           %opts = %$route_spec;
176 0           $route_spec = delete $opts{set};
177             }
178              
179 0 0         if ($route_spec->$_isa('DBIx::Class::ResultSource')) {
    0          
180 0           $route_spec = $route_spec->resultset;
181             # $opts{is_canonical_source} = 1;
182             } elsif ($route_spec->$_isa('DBIx::Class::ResultSet')) {
183             # $route_spec is already a resultset, but is a non-canonical source
184             # $opts{is_canonical_source} //= 0;
185             } else {
186 0           croak "Don't know how to convert '$route_spec' into to a DBIx::Class::ResultSet or WebAPI::DBIC::Resource::Role::Route";
187             }
188              
189 0   0       my $path = delete($opts{path}) || $self->type_namer->type_name_for_resultset($route_spec);
190              
191 0           return $self->make_routes_for_resultset($path, $route_spec, %opts);
192             }
193              
194             1;
195              
196             __END__
197              
198             =pod
199              
200             =encoding UTF-8
201              
202             =head1 NAME
203              
204             WebAPI::DBIC::RouteMaker
205              
206             =head1 VERSION
207              
208             version 0.004001
209              
210             =head1 NAME
211              
212             WebAPI::DBIC::RouteMaker - Make routes for resultsets
213              
214             =head1 AUTHOR
215              
216             Tim Bunce <Tim.Bunce@pobox.com>
217              
218             =head1 COPYRIGHT AND LICENSE
219              
220             This software is copyright (c) 2015 by Tim Bunce.
221              
222             This is free software; you can redistribute it and/or modify it under
223             the same terms as the Perl 5 programming language system itself.
224              
225             =cut