Newer Older
402 lines | 9.772kb
copy gitweblite soruce code
root authored on 2012-11-23
1
package Mojolicious::Routes::Pattern;
2
use Mojo::Base -base;
3

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
4
has [qw(constraints defaults)] => sub { {} };
5
has [qw(format_regex pattern regex)];
6
has placeholder_start => ':';
7
has [qw(placeholders tree)] => sub { [] };
8
has quote_end      => ')';
9
has quote_start    => '(';
10
has relaxed_start  => '#';
copy gitweblite soruce code
root authored on 2012-11-23
11
has wildcard_start => '*';
12

            
13
sub new { shift->SUPER::new->parse(@_) }
14

            
15
sub match {
16
  my ($self, $path, $detect) = @_;
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
17
  my $captures = $self->match_partial(\$path, $detect);
18
  return !$path || $path eq '/' ? $captures : undef;
19
}
20

            
21
sub match_partial {
22
  my ($self, $pathref, $detect) = @_;
23

            
24
  # Compile on demand
25
  my $regex = $self->regex || $self->_compile;
26
  my $format
27
    = $detect ? ($self->format_regex || $self->_compile_format) : undef;
28

            
29
  # Match
30
  return undef unless my @captures = $$pathref =~ $regex;
update Mojolicious to 4.57
Yuki Kimoto authored on 2013-12-02
31
  $$pathref = pop(@captures);
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
32

            
33
  # Merge captures
34
  my $captures = {%{$self->defaults}};
35
  for my $placeholder (@{$self->placeholders}) {
36
    last unless @captures;
37
    my $capture = shift @captures;
38
    $captures->{$placeholder} = $capture if defined $capture;
39
  }
40

            
41
  # Format
42
  my $constraint = $self->constraints->{format};
43
  return $captures if !$detect || defined $constraint && !$constraint;
44
  if ($$pathref =~ s!^/?$format!!) { $captures->{format} = $1 }
45
  elsif ($constraint) { return undef unless $captures->{format} }
46

            
47
  return $captures;
copy gitweblite soruce code
root authored on 2012-11-23
48
}
49

            
50
sub parse {
51
  my $self = shift;
52

            
53
  # Make sure we have a viable pattern
54
  my $pattern = @_ % 2 ? (shift || '/') : '/';
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
55
  $pattern = "/$pattern" unless $pattern =~ m!^/!;
56
  $self->constraints({@_});
copy gitweblite soruce code
root authored on 2012-11-23
57

            
58
  return $pattern eq '/' ? $self : $self->pattern($pattern)->_tokenize;
59
}
60

            
61
sub render {
62
  my ($self, $values, $render) = @_;
63

            
64
  # Merge values with defaults
65
  my $format = ($values ||= {})->{format};
66
  $values = {%{$self->defaults}, %$values};
67

            
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
68
  # Placeholders can only be optional without a format
69
  my $optional = !$format;
70

            
71
  my $str = '';
copy gitweblite soruce code
root authored on 2012-11-23
72
  for my $token (reverse @{$self->tree}) {
73
    my $op       = $token->[0];
74
    my $rendered = '';
75

            
76
    # Slash
77
    if ($op eq 'slash') { $rendered = '/' unless $optional }
78

            
79
    # Text
80
    elsif ($op eq 'text') {
81
      $rendered = $token->[1];
82
      $optional = 0;
83
    }
84

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
85
    # Placeholder, relaxed or wildcard
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
86
    elsif ($op eq 'placeholder' || $op eq 'relaxed' || $op eq 'wildcard') {
copy gitweblite soruce code
root authored on 2012-11-23
87
      my $name = $token->[1];
88
      $rendered = defined $values->{$name} ? $values->{$name} : '';
89
      my $default = $self->defaults->{$name};
90
      if (!defined $default || ($default ne $rendered)) { $optional = 0 }
91
      elsif ($optional) { $rendered = '' }
92
    }
93

            
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
94
    $str = "$rendered$str";
copy gitweblite soruce code
root authored on 2012-11-23
95
  }
96

            
97
  # Format is optional
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
98
  $str ||= '/';
99
  return $render && $format ? "$str.$format" : $str;
copy gitweblite soruce code
root authored on 2012-11-23
100
}
101

            
102
sub _compile {
103
  my $self = shift;
104

            
105
  my $block = my $regex = '';
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
106
  my $optional    = 1;
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
107
  my $constraints = $self->constraints;
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
108
  my $defaults    = $self->defaults;
copy gitweblite soruce code
root authored on 2012-11-23
109
  for my $token (reverse @{$self->tree}) {
110
    my $op       = $token->[0];
111
    my $compiled = '';
112

            
113
    # Slash
114
    if ($op eq 'slash') {
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
115
      $regex = ($optional ? "(?:/$block)?" : "/$block") . $regex;
copy gitweblite soruce code
root authored on 2012-11-23
116
      $block = '';
117
      next;
118
    }
119

            
120
    # Text
121
    elsif ($op eq 'text') {
122
      $compiled = quotemeta $token->[1];
123
      $optional = 0;
124
    }
125

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
126
    # Placeholder
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
127
    elsif ($op eq 'placeholder' || $op eq 'relaxed' || $op eq 'wildcard') {
copy gitweblite soruce code
root authored on 2012-11-23
128
      my $name = $token->[1];
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
129
      unshift @{$self->placeholders}, $name;
copy gitweblite soruce code
root authored on 2012-11-23
130

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
131
      # Placeholder
132
      if ($op eq 'placeholder') { $compiled = '([^\/\.]+)' }
copy gitweblite soruce code
root authored on 2012-11-23
133

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
134
      # Relaxed
135
      elsif ($op eq 'relaxed') { $compiled = '([^\/]+)' }
copy gitweblite soruce code
root authored on 2012-11-23
136

            
137
      # Wildcard
138
      elsif ($op eq 'wildcard') { $compiled = '(.+)' }
139

            
140
      # Custom regex
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
141
      my $constraint = $constraints->{$name};
142
      $compiled = _compile_req($constraint) if $constraint;
copy gitweblite soruce code
root authored on 2012-11-23
143

            
144
      # Optional placeholder
145
      $optional = 0 unless exists $defaults->{$name};
146
      $compiled .= '?' if $optional;
147
    }
148

            
149
    $block = "$compiled$block";
150
  }
151

            
152
  # Not rooted with a slash
153
  $regex = "$block$regex" if $block;
154

            
update Mojolicious to 4.57
Yuki Kimoto authored on 2013-12-02
155
  return $self->regex(qr/^$regex(.*)/s)->regex;
copy gitweblite soruce code
root authored on 2012-11-23
156
}
157

            
158
sub _compile_format {
159
  my $self = shift;
160

            
161
  # Default regex
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
162
  my $c = $self->constraints;
163
  return $self->format_regex(qr!\.([^/]+)$!)->format_regex
164
    unless defined $c->{format};
165

            
166
  # No regex
167
  return undef unless $c->{format};
copy gitweblite soruce code
root authored on 2012-11-23
168

            
169
  # Compile custom regex
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
170
  my $regex = _compile_req($c->{format});
171
  return $self->format_regex(qr!\.$regex$!)->format_regex;
copy gitweblite soruce code
root authored on 2012-11-23
172
}
173

            
174
sub _compile_req {
175
  my $req = shift;
176
  return "($req)" if ref $req ne 'ARRAY';
177
  return '(' . join('|', map {quotemeta} reverse sort @$req) . ')';
178
}
179

            
180
sub _tokenize {
181
  my $self = shift;
182

            
183
  my $quote_end   = $self->quote_end;
184
  my $quote_start = $self->quote_start;
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
185
  my $placeholder = $self->placeholder_start;
copy gitweblite soruce code
root authored on 2012-11-23
186
  my $relaxed     = $self->relaxed_start;
187
  my $wildcard    = $self->wildcard_start;
188

            
189
  my $pattern = $self->pattern;
190
  my $state   = 'text';
191
  my (@tree, $quoted);
update Mojolicious to 4.57
Yuki Kimoto authored on 2013-12-02
192
  for my $char (split '', $pattern) {
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
193
    my $inside = !!grep { $_ eq $state } qw(placeholder relaxed wildcard);
copy gitweblite soruce code
root authored on 2012-11-23
194

            
195
    # Quote start
196
    if ($char eq $quote_start) {
197
      $quoted = 1;
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
198
      push @tree, ['placeholder', ''];
update Mojolicious to 4.57
Yuki Kimoto authored on 2013-12-02
199
      $state = 'placeholder';
copy gitweblite soruce code
root authored on 2012-11-23
200
    }
201

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
202
    # Placeholder start
203
    elsif ($char eq $placeholder) {
204
      push @tree, ['placeholder', ''] if $state ne 'placeholder';
205
      $state = 'placeholder';
copy gitweblite soruce code
root authored on 2012-11-23
206
    }
207

            
208
    # Relaxed or wildcard start (upgrade when quoted)
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
209
    elsif ($char eq $relaxed || $char eq $wildcard) {
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
210
      push @tree, ['placeholder', ''] unless $quoted;
211
      $tree[-1][0] = $state = $char eq $relaxed ? 'relaxed' : 'wildcard';
copy gitweblite soruce code
root authored on 2012-11-23
212
    }
213

            
214
    # Quote end
215
    elsif ($char eq $quote_end) {
216
      $quoted = 0;
217
      $state  = 'text';
218
    }
219

            
220
    # Slash
221
    elsif ($char eq '/') {
222
      push @tree, ['slash'];
223
      $state = 'text';
224
    }
225

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
226
    # Placeholder, relaxed or wildcard
227
    elsif ($inside && $char =~ /\w/) { $tree[-1][-1] .= $char }
copy gitweblite soruce code
root authored on 2012-11-23
228

            
229
    # Text
230
    else {
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
231
      push @tree, ['text', $char] and next unless $tree[-1][0] eq 'text';
232
      $tree[-1][-1] .= $char;
update Mojolicious to 4.57
Yuki Kimoto authored on 2013-12-02
233
      $state = 'text';
copy gitweblite soruce code
root authored on 2012-11-23
234
    }
235
  }
236

            
237
  return $self->tree(\@tree);
238
}
239

            
240
1;
241

            
update Mojolicious to 4.57
Yuki Kimoto authored on 2013-12-02
242
=encoding utf8
243

            
copy gitweblite soruce code
root authored on 2012-11-23
244
=head1 NAME
245

            
246
Mojolicious::Routes::Pattern - Routes pattern engine
247

            
248
=head1 SYNOPSIS
249

            
250
  use Mojolicious::Routes::Pattern;
251

            
252
  # Create pattern
253
  my $pattern = Mojolicious::Routes::Pattern->new('/test/:name');
254

            
255
  # Match routes
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
256
  my $captures = $pattern->match('/test/sebastian');
257
  say $captures->{name};
copy gitweblite soruce code
root authored on 2012-11-23
258

            
259
=head1 DESCRIPTION
260

            
261
L<Mojolicious::Routes::Pattern> is the core of L<Mojolicious::Routes>.
262

            
263
=head1 ATTRIBUTES
264

            
265
L<Mojolicious::Routes::Pattern> implements the following attributes.
266

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
267
=head2 constraints
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
268

            
269
  my $constraints = $pattern->constraints;
270
  $pattern        = $pattern->constraints({foo => qr/\w+/});
271

            
272
Regular expression constraints.
273

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
274
=head2 defaults
copy gitweblite soruce code
root authored on 2012-11-23
275

            
276
  my $defaults = $pattern->defaults;
277
  $pattern     = $pattern->defaults({foo => 'bar'});
278

            
279
Default parameters.
280

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
281
=head2 format_regex
copy gitweblite soruce code
root authored on 2012-11-23
282

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
283
  my $regex = $pattern->format_regex;
284
  $pattern  = $pattern->format_regex($regex);
copy gitweblite soruce code
root authored on 2012-11-23
285

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
286
Compiled regular expression for format matching.
copy gitweblite soruce code
root authored on 2012-11-23
287

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
288
=head2 pattern
copy gitweblite soruce code
root authored on 2012-11-23
289

            
290
  my $pattern = $pattern->pattern;
291
  $pattern    = $pattern->pattern('/(foo)/(bar)');
292

            
293
Raw unparsed pattern.
294

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
295
=head2 placeholder_start
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
296

            
297
  my $start = $pattern->placeholder_start;
298
  $pattern  = $pattern->placeholder_start(':');
299

            
300
Character indicating a placeholder, defaults to C<:>.
301

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
302
=head2 placeholders
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
303

            
304
  my $placeholders = $pattern->placeholders;
305
  $pattern         = $pattern->placeholders(['foo', 'bar']);
306

            
307
Placeholder names.
308

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
309
=head2 quote_end
copy gitweblite soruce code
root authored on 2012-11-23
310

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
311
  my $end  = $pattern->quote_end;
312
  $pattern = $pattern->quote_end(']');
copy gitweblite soruce code
root authored on 2012-11-23
313

            
314
Character indicating the end of a quoted placeholder, defaults to C<)>.
315

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
316
=head2 quote_start
copy gitweblite soruce code
root authored on 2012-11-23
317

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
318
  my $start = $pattern->quote_start;
copy gitweblite soruce code
root authored on 2012-11-23
319
  $pattern  = $pattern->quote_start('[');
320

            
321
Character indicating the start of a quoted placeholder, defaults to C<(>.
322

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
323
=head2 regex
copy gitweblite soruce code
root authored on 2012-11-23
324

            
325
  my $regex = $pattern->regex;
326
  $pattern  = $pattern->regex($regex);
327

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
328
Pattern in compiled regular expression form.
copy gitweblite soruce code
root authored on 2012-11-23
329

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
330
=head2 relaxed_start
copy gitweblite soruce code
root authored on 2012-11-23
331

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
332
  my $start = $pattern->relaxed_start;
333
  $pattern  = $pattern->relaxed_start('*');
copy gitweblite soruce code
root authored on 2012-11-23
334

            
335
Character indicating a relaxed placeholder, defaults to C<#>.
336

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
337
=head2 tree
copy gitweblite soruce code
root authored on 2012-11-23
338

            
339
  my $tree = $pattern->tree;
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
340
  $pattern = $pattern->tree([['slash'], ['text', 'foo']]);
copy gitweblite soruce code
root authored on 2012-11-23
341

            
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
342
Pattern in parsed form. Note that this structure should only be used very
343
carefully since it is very dynamic.
copy gitweblite soruce code
root authored on 2012-11-23
344

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
345
=head2 wildcard_start
copy gitweblite soruce code
root authored on 2012-11-23
346

            
upgraded Mojolicious to v3.7...
Yuki Kimoto authored on 2013-01-28
347
  my $start = $pattern->wildcard_start;
348
  $pattern  = $pattern->wildcard_start('*');
copy gitweblite soruce code
root authored on 2012-11-23
349

            
350
Character indicating the start of a wildcard placeholder, defaults to C<*>.
351

            
352
=head1 METHODS
353

            
354
L<Mojolicious::Routes::Pattern> inherits all methods from L<Mojo::Base> and
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
355
implements the following new ones.
copy gitweblite soruce code
root authored on 2012-11-23
356

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
357
=head2 new
copy gitweblite soruce code
root authored on 2012-11-23
358

            
359
  my $pattern = Mojolicious::Routes::Pattern->new('/:action');
360
  my $pattern
361
    = Mojolicious::Routes::Pattern->new('/:action', action => qr/\w+/);
362
  my $pattern = Mojolicious::Routes::Pattern->new(format => 0);
363

            
update Mojolicious to 4.57
Yuki Kimoto authored on 2013-12-02
364
Construct a new L<Mojolicious::Routes::Pattern> object and L</"parse"> pattern
365
if necessary.
copy gitweblite soruce code
root authored on 2012-11-23
366

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
367
=head2 match
copy gitweblite soruce code
root authored on 2012-11-23
368

            
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
369
  my $captures = $pattern->match('/foo/bar');
370
  my $captures = $pattern->match('/foo/bar', 1);
copy gitweblite soruce code
root authored on 2012-11-23
371

            
372
Match pattern against entire path, format detection is disabled by default.
373

            
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
374
=head2 match_partial
375

            
376
  my $captures = $pattern->match_partial(\$path);
377
  my $captures = $pattern->match_partial(\$path, 1);
378

            
379
Match pattern against path and remove matching parts, format detection is
380
disabled by default.
381

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
382
=head2 parse
copy gitweblite soruce code
root authored on 2012-11-23
383

            
384
  $pattern = $pattern->parse('/:action');
385
  $pattern = $pattern->parse('/:action', action => qr/\w+/);
386
  $pattern = $pattern->parse(format => 0);
387

            
update Mojolicious 4.07
Yuki Kimoto authored on 2013-06-03
388
Parse pattern.
copy gitweblite soruce code
root authored on 2012-11-23
389

            
update Mojolicious and added...
Yuki Kimoto authored on 2013-03-20
390
=head2 render
copy gitweblite soruce code
root authored on 2012-11-23
391

            
392
  my $path = $pattern->render({action => 'foo'});
393
  my $path = $pattern->render({action => 'foo'}, 1);
394

            
395
Render pattern into a path with parameters, format rendering is disabled by
396
default.
397

            
398
=head1 SEE ALSO
399

            
400
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
401

            
402
=cut