biblesearch / mojo / lib / Mojo / Parameters.pm /
Newer Older
331 lines | 7.627kb
add files
Yuki Kimoto authored on 2014-03-26
1
package Mojo::Parameters;
2
use Mojo::Base -base;
3
use overload
4
  '@{}'    => sub { shift->params },
5
  bool     => sub {1},
6
  '""'     => sub { shift->to_string },
7
  fallback => 1;
8

            
9
use Mojo::Util qw(decode encode url_escape url_unescape);
10

            
11
has charset => 'UTF-8';
12

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

            
15
sub append {
16
  my ($self, @pairs) = @_;
17

            
18
  my $params = $self->params;
19
  for (my $i = 0; $i < @pairs; $i += 2) {
20
    my $key   = $pairs[$i]     // '';
21
    my $value = $pairs[$i + 1] // '';
22

            
23
    # Single value
24
    if (ref $value ne 'ARRAY') { push @$params, $key => $value }
25

            
26
    # Multiple values
27
    else { push @$params, $key => (defined $_ ? "$_" : '') for @$value }
28
  }
29

            
30
  return $self;
31
}
32

            
33
sub clone {
34
  my $self = shift;
35

            
36
  my $clone = $self->new->charset($self->charset);
37
  if (defined $self->{string}) { $clone->{string} = $self->{string} }
38
  else                         { $clone->params([@{$self->params}]) }
39

            
40
  return $clone;
41
}
42

            
43
sub merge {
44
  my $self = shift;
45
  push @{$self->params}, @{$_->params} for @_;
46
  return $self;
47
}
48

            
49
sub param {
50
  my ($self, $name) = (shift, shift);
51

            
52
  # List names
53
  return sort keys %{$self->to_hash} unless $name;
54

            
55
  # Replace values
56
  $self->remove($name) if defined $_[0];
57
  $self->append($name, $_) for @_;
58

            
59
  # List values
60
  my @values;
61
  my $params = $self->params;
62
  for (my $i = 0; $i < @$params; $i += 2) {
63
    push @values, $params->[$i + 1] if $params->[$i] eq $name;
64
  }
65

            
66
  return wantarray ? @values : $values[0];
67
}
68

            
69
sub params {
70
  my $self = shift;
71

            
72
  # Replace parameters
73
  if (@_) {
74
    $self->{params} = shift;
75
    delete $self->{string};
76
    return $self;
77
  }
78

            
79
  # Parse string
80
  if (defined(my $str = delete $self->{string})) {
81
    my $params = $self->{params} = [];
82
    return $params unless length $str;
83

            
84
    # W3C suggests to also accept ";" as a separator
85
    my $charset = $self->charset;
86
    for my $pair (split /&|;/, $str) {
87
      next unless $pair =~ /^([^=]+)(?:=(.*))?$/;
88
      my $name = $1;
89
      my $value = $2 // '';
90

            
91
      # Replace "+" with whitespace, unescape and decode
92
      s/\+/ /g for $name, $value;
93
      $name  = url_unescape $name;
94
      $name  = decode($charset, $name) // $name if $charset;
95
      $value = url_unescape $value;
96
      $value = decode($charset, $value) // $value if $charset;
97

            
98
      push @$params, $name, $value;
99
    }
100
  }
101

            
102
  return $self->{params} ||= [];
103
}
104

            
105
sub parse {
106
  my $self = shift;
107

            
108
  # Pairs
109
  if (@_ > 1) { $self->append(@_) }
110

            
111
  # String
112
  else { $self->{string} = $_[0] }
113

            
114
  return $self;
115
}
116

            
117
sub remove {
118
  my $self = shift;
119
  my $name = shift // '';
120

            
121
  my $params = $self->params;
122
  for (my $i = 0; $i < @$params;) {
123
    if ($params->[$i] eq $name) { splice @$params, $i, 2 }
124
    else                        { $i += 2 }
125
  }
126

            
127
  return $self->params($params);
128
}
129

            
130
sub to_hash {
131
  my $self = shift;
132

            
133
  my $params = $self->params;
134
  my %hash;
135
  for (my $i = 0; $i < @$params; $i += 2) {
136
    my ($name, $value) = @{$params}[$i, $i + 1];
137

            
138
    # Array
139
    if (exists $hash{$name}) {
140
      $hash{$name} = [$hash{$name}] unless ref $hash{$name} eq 'ARRAY';
141
      push @{$hash{$name}}, $value;
142
    }
143

            
144
    # String
145
    else { $hash{$name} = $value }
146
  }
147

            
148
  return \%hash;
149
}
150

            
151
sub to_string {
152
  my $self = shift;
153

            
154
  # String
155
  my $charset = $self->charset;
156
  if (defined(my $str = $self->{string})) {
157
    $str = encode $charset, $str if $charset;
158
    return url_escape $str, '^A-Za-z0-9\-._~!$&\'()*+,;=%:@/?';
159
  }
160

            
161
  # Build pairs
162
  my $params = $self->params;
163
  return '' unless @$params;
164
  my @pairs;
165
  for (my $i = 0; $i < @$params; $i += 2) {
166
    my ($name, $value) = @{$params}[$i, $i + 1];
167

            
168
    # Escape and replace whitespace with "+"
169
    $name  = encode $charset,   $name if $charset;
170
    $name  = url_escape $name,  '^A-Za-z0-9\-._~!$\'()*,:@/?';
171
    $value = encode $charset,   $value if $charset;
172
    $value = url_escape $value, '^A-Za-z0-9\-._~!$\'()*,:@/?';
173
    s/\%20/\+/g for $name, $value;
174

            
175
    push @pairs, "$name=$value";
176
  }
177

            
178
  return join '&', @pairs;
179
}
180

            
181
1;
182

            
183
=encoding utf8
184

            
185
=head1 NAME
186

            
187
Mojo::Parameters - Parameters
188

            
189
=head1 SYNOPSIS
190

            
191
  use Mojo::Parameters;
192

            
193
  # Parse
194
  my $params = Mojo::Parameters->new('foo=bar&baz=23');
195
  say $params->param('baz');
196

            
197
  # Build
198
  my $params = Mojo::Parameters->new(foo => 'bar', baz => 23);
199
  push @$params, i => '♥ mojolicious';
200
  say "$params";
201

            
202
=head1 DESCRIPTION
203

            
204
L<Mojo::Parameters> is a container for form parameters used by L<Mojo::URL>.
205

            
206
=head1 ATTRIBUTES
207

            
208
L<Mojo::Parameters> implements the following attributes.
209

            
210
=head2 charset
211

            
212
  my $charset = $params->charset;
213
  $params     = $params->charset('UTF-8');
214

            
215
Charset used for encoding and decoding parameters, defaults to C<UTF-8>.
216

            
217
  # Disable encoding and decoding
218
  $params->charset(undef);
219

            
220
=head1 METHODS
221

            
222
L<Mojo::Parameters> inherits all methods from L<Mojo::Base> and implements the
223
following new ones.
224

            
225
=head2 new
226

            
227
  my $params = Mojo::Parameters->new;
228
  my $params = Mojo::Parameters->new('foo=b%3Bar&baz=23');
229
  my $params = Mojo::Parameters->new(foo => 'b;ar');
230
  my $params = Mojo::Parameters->new(foo => ['ba;r', 'b;az']);
231
  my $params = Mojo::Parameters->new(foo => ['ba;r', 'b;az'], bar => 23);
232

            
233
Construct a new L<Mojo::Parameters> object and L</"parse"> parameters if
234
necessary.
235

            
236
=head2 append
237

            
238
  $params = $params->append(foo => 'ba;r');
239
  $params = $params->append(foo => ['ba;r', 'b;az']);
240
  $params = $params->append(foo => ['ba;r', 'b;az'], bar => 23);
241

            
242
Append parameters. Note that this method will normalize the parameters.
243

            
244
  # "foo=bar&foo=baz"
245
  Mojo::Parameters->new('foo=bar')->append(foo => 'baz');
246

            
247
  # "foo=bar&foo=baz&foo=yada"
248
  Mojo::Parameters->new('foo=bar')->append(foo => ['baz', 'yada']);
249

            
250
  # "foo=bar&foo=baz&foo=yada&bar=23"
251
  Mojo::Parameters->new('foo=bar')->append(foo => ['baz', 'yada'], bar => 23);
252

            
253
=head2 clone
254

            
255
  my $params2 = $params->clone;
256

            
257
Clone parameters.
258

            
259
=head2 merge
260

            
261
  $params = $params->merge(Mojo::Parameters->new(foo => 'b;ar', baz => 23));
262

            
263
Merge L<Mojo::Parameters> objects. Note that this method will normalize the
264
parameters.
265

            
266
=head2 param
267

            
268
  my @names = $params->param;
269
  my $foo   = $params->param('foo');
270
  my @foo   = $params->param('foo');
271
  my $foo   = $params->param(foo => 'ba;r');
272
  my @foo   = $params->param(foo => qw(ba;r ba;z));
273

            
274
Check and replace parameter value. Be aware that if you request a parameter by
275
name in scalar context, you will receive only the I<first> value for that
276
parameter, if there are multiple values for that name. In list context you
277
will receive I<all> of the values for that name. Note that this method will
278
normalize the parameters.
279

            
280
=head2 params
281

            
282
  my $array = $params->params;
283
  $params   = $params->params([foo => 'b;ar', baz => 23]);
284

            
285
Parsed parameters. Note that this method will normalize the parameters.
286

            
287
=head2 parse
288

            
289
  $params = $params->parse('foo=b%3Bar&baz=23');
290

            
291
Parse parameters.
292

            
293
=head2 remove
294

            
295
  $params = $params->remove('foo');
296

            
297
Remove parameters. Note that this method will normalize the parameters.
298

            
299
  # "bar=yada"
300
  Mojo::Parameters->new('foo=bar&foo=baz&bar=yada')->remove('foo');
301

            
302
=head2 to_hash
303

            
304
  my $hash = $params->to_hash;
305

            
306
Turn parameters into a hash reference. Note that this method will normalize
307
the parameters.
308

            
309
  # "baz"
310
  Mojo::Parameters->new('foo=bar&foo=baz')->to_hash->{foo}[1];
311

            
312
=head2 to_string
313

            
314
  my $str = $params->to_string;
315
  my $str = "$params";
316

            
317
Turn parameters into a string.
318

            
319
=head1 PARAMETERS
320

            
321
Direct array reference access to the parsed parameters is also possible. Note
322
that this will normalize the parameters.
323

            
324
  say $params->[0];
325
  say for @$params;
326

            
327
=head1 SEE ALSO
328

            
329
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
330

            
331
=cut