File Coverage

File:lib/Railsish/Router.pm
Coverage:89.6%

linestmtbrancondsubpodtimecode
1package Railsish::Router;
2
13
13
13
603
48
156
use Any::Moose;
3
4
13
13
13
1074
67
213
use Path::Router;
5
6has "routers" => (
7    is => "rw",
8    isa => "HashRef[Path::Router]",
9    lazy_build => 1,
10);
11
12has named_routes => (
13    is => "rw",
14    isa => "HashRef",
15    default => sub {{}}
16);
17
18sub _build_routers {
19    return {
20
12
894
        get => Path::Router->new,
21        post => Path::Router->new,
22        put => Path::Router->new,
23        delete => Path::Router->new
24    }
25}
26
27my $APP_ROUTER;
28
29sub connect {
30
39
0
441
    my ($self, $urlish, %vars) = @_;
31
39
290
    $self = $APP_ROUTER unless ref($self);
32
33
39
366
    $urlish =~ s/^\/*/\//;
34
35
39
638
    if (defined $vars{path_prefix}) {
36
4
25
        $vars{path_prefix} =~ s/^\/*/\//;
37
4
20
        $urlish = $vars{path_prefix} . $urlish;
38    }
39
40
39
161
    my $conditions = delete $vars{conditions};
41
39
253
    my $routers = $self->routers;
42
43
105
1737
    return map {
44
105
401
        $_->add_route($urlish => (defaults => \%vars));
45
105
9874
        $_->routes->[-1];
46    }
47    map {
48
39
2564
        $routers->{$_};
49    }
50    (
51        $conditions
52        ? lc($conditions->{method})
53        : qw(get post put delete)
54    );
55}
56
57sub match {
58
27
0
259
    my ($self, $uri, %args) = @_;
59
27
213
    $self = $APP_ROUTER unless ref($self);
60
61
27
217
    my $routers = $self->routers;
62
27
552
    my $conditions = delete $args{conditions};
63
64
27
215
    for($conditions ? lc($conditions->{method}) : qw(get post put delete)) {
65
27
245
        if (my $matched = $routers->{$_}->match($uri)) {
66
23
13867
            return $matched;
67        }
68    }
69}
70
71sub uri_for {
72
3
0
48
    my ($self, @args) = @_;
73
3
35
    $self = $APP_ROUTER unless ref($self);
74
75
3
26
    my $routers = $self->routers;
76
3
38
    for (qw(get post put delete)) {
77
3
33
        if (my $url = $routers->{$_}->uri_for(@args)) {
78
3
3402
            return "/$url";
79        }
80    }
81}
82
83
13
13
13
766
61
172
use Railsish::TextHelpers qw(singularize pluralize);
84
85sub resources {
86
3
0
26
    my ($self, $name, @vars) = @_;
87
3
29
    $self = $APP_ROUTER unless ref($self);
88
89
3
22
    my $resource = singularize($name);
90
3
29307
    my $resources = pluralize($name);
91
92
3
3325
    my $edit = "edit_${resource}";
93
3
101
    $self->$edit(
94        "/$resources/:id/edit",
95        controller => $resources,
96        action => "edit",
97        @vars
98    );
99
100
3
14
    my $new = "new_${resource}";
101
3
115
    $self->$new(
102        "/$resources/new",
103        controller => $resources,
104        action => "new",
105        @vars
106    );
107
108
3
74
    $self->$resource(
109        "/${resources}/:id",
110        controller => $resources,
111        action => "show",
112        @vars,
113        conditions => {method => "get"}
114    );
115
116
3
39
    $self->connect(
117        "/${resources}",
118        controller => $resources,
119        action => "create",
120        conditions => {method => "post"}
121    );
122
123
3
92
    $self->connect(
124        "/${resources}/:id",
125        controller => $resources,
126        action => "update",
127        conditions => {method => "put"}
128    );
129
130
3
159
    $self->connect(
131        "/${resources}/:id",
132        controller => $resources,
133        action => "destroy",
134        conditions => {method => "delete"}
135    );
136
137
3
127
    $self->$resources(
138        "/${resources}",
139        controller => $resources,
140        action => "index",
141        @vars,
142        conditions => {method => "get"}
143    );
144
145}
146
147# this one should be invoked like: Railsish::Router->draw;
148sub draw {
149
12
0
100
    my ($class, $cb) = @_;
150
12
127
    $APP_ROUTER = $class->new;
151
12
429
    $cb->($APP_ROUTER);
152
153
12
271
    return $APP_ROUTER;
154}
155
156
13
13
13
165
49
214
use Sub::Install;
157
13
13
13
612
52
69
use Railsish::PathHelpers ();
158
159our $AUTOLOAD;
160sub AUTOLOAD {
161
14
191
    my ($self, $urlish, %vars) = @_;
162
14
128
    $self = $APP_ROUTER unless ref($self);
163
164
14
44
    my $name = $AUTOLOAD;
165
14
181
    $name =~ s/^.*:://;
166
167
14
119
    my @routes = $self->connect($urlish, %vars);
168
14
322
    my $route = $routes[0];
169
170
14
91
    $self->named_routes->{$name} = $route;
171
172
14
118
    my $helper_name = "${name}_path";
173    Sub::Install::install_sub({
174        into => __PACKAGE__,
175        code => sub {
176
12
79
            my ($self, @args) = @_;
177
12
95
            $self = $APP_ROUTER unless ref($self);
178
179
12
90
            if (@args == 1 && ref($args[0]) eq 'HASH') {
180
2
2
7
19
                @args = (%{$args[0]});
181            }
182
183
12
83
            my $temp_router = Path::Router->new;
184
12
12
979
77
            push @{$temp_router->routes}, $self->named_routes->{$name};
185
186
12
84
            return "/" . $temp_router->uri_for(@args);
187        },
188
14
360
        as => $helper_name
189    });
190
191    Sub::Install::install_sub({
192        into => "Railsish::PathHelpers",
193        as => $helper_name,
194        code => sub {
195
2
17
            return Railsish::Router->$helper_name(@_);
196        }
197
14
281
    });
198
14
182
    push @Railsish::PathHelpers::HELPERS, $helper_name;
199}
200
201__PACKAGE__->meta->make_immutable;