OptList.pm
Go to the documentation of this file.
1 # Original package Data::OptList downloaded from CPAN on 6/15/2012. This module
2 # has been modified by Andreas Schuh on 6/15/2012 to make it part of BASIS.
3 
4 use strict;
5 use warnings;
6 package BASIS::Data::OptList;
7 BEGIN {
8  $BASIS::Data::OptList::VERSION = '0.107';
9 }
10 # ABSTRACT: parse and validate simple name/value option pairs
11 
12 use List::Util ();
13 use BASIS::Params::Util ();
14 use BASIS::Sub::Install 0.921 ();
15 
16 
17 my %test_for;
18 BEGIN {
19  %test_for = (
20  CODE => \&BASIS::Params::Util::_CODELIKE, ## no critic
21  HASH => \&BASIS::Params::Util::_HASHLIKE, ## no critic
22  ARRAY => \&BASIS::Params::Util::_ARRAYLIKE, ## no critic
23  SCALAR => \&BASIS::Params::Util::_SCALAR0, ## no critic
24  );
25 }
26 
27 sub __is_a {
28  my ($got, $expected) = @_;
29 
30  return List::Util::first { __is_a($got, $_) } @$expected if ref $expected;
31 
32  return defined (
33  exists($test_for{$expected})
34  ? $test_for{$expected}->($got)
35  : BASIS::Params::Util::_INSTANCE($got, $expected) ## no critic
36  );
37 }
38 
39 sub mkopt {
40  my ($opt_list) = shift;
41 
42  my ($moniker, $require_unique, $must_be); # the old positional args
43  my $name_test;
44 
45  if (@_ == 1 and BASIS::Params::Util::_HASHLIKE($_[0])) {
46  my $arg = $_[0];
47  ($moniker, $require_unique, $must_be, $name_test)
48  = @$arg{ qw(moniker require_unique must_be name_test) };
49  } else {
50  ($moniker, $require_unique, $must_be) = @_;
51  }
52 
53  $moniker = 'unnamed' unless defined $moniker;
54 
55  return [] unless $opt_list;
56 
57  $name_test ||= sub { ! ref $_[0] };
58 
59  $opt_list = [
60  map { $_ => (ref $opt_list->{$_} ? $opt_list->{$_} : ()) } keys %$opt_list
61  ] if ref $opt_list eq 'HASH';
62 
63  my @return;
64  my %seen;
65 
66  for (my $i = 0; $i < @$opt_list; $i++) { ## no critic
67  my $name = $opt_list->[$i];
68  my $value;
69 
70  if ($require_unique) {
71  Carp::croak "multiple definitions provided for $name" if $seen{$name}++;
72  }
73 
74  if ($i == $#$opt_list) { $value = undef; }
75  elsif (not defined $opt_list->[$i+1]) { $value = undef; $i++ }
76  elsif ($name_test->($opt_list->[$i+1])) { $value = undef; }
77  else { $value = $opt_list->[++$i] }
78 
79  if ($must_be and defined $value) {
80  unless (__is_a($value, $must_be)) {
81  my $ref = ref $value;
82  Carp::croak "$ref-ref values are not valid in $moniker opt list";
83  }
84  }
85 
86  push @return, [ $name => $value ];
87  }
88 
89  return \@return;
90 }
91 
92 
93 sub mkopt_hash {
94  my ($opt_list, $moniker, $must_be) = @_;
95  return {} unless $opt_list;
96 
97  $opt_list = mkopt($opt_list, $moniker, 1, $must_be);
98  my %hash = map { $_->[0] => $_->[1] } @$opt_list;
99  return \%hash;
100 }
101 
102 
103 BEGIN {
104  *import = BASIS::Sub::Install::exporter {
105  exports => [qw(mkopt mkopt_hash)],
106  };
107 }
108 
109 1;
110 
111 __END__
112 =pod
113 
114 =head1 NAME
115 
116 BASIS::Data::OptList - parse and validate simple name/value option pairs
117 
118 =head1 VERSION
119 
120 version 0.107
121 
122 =head1 SYNOPSIS
123 
124  use BASIS::Data::OptList;
125 
126  my $options = BASIS::Data::OptList::mkopt([
127  qw(key1 key2 key3 key4),
128  key5 => { ... },
129  key6 => [ ... ],
130  key7 => sub { ... },
131  key8 => { ... },
132  key8 => [ ... ],
133  ]);
134 
135 ...is the same thing, more or less, as:
136 
137  my $options = [
138  [ key1 => undef, ],
139  [ key2 => undef, ],
140  [ key3 => undef, ],
141  [ key4 => undef, ],
142  [ key5 => { ... }, ],
143  [ key6 => [ ... ], ],
144  [ key7 => sub { ... }, ],
145  [ key8 => { ... }, ],
146  [ key8 => [ ... ], ],
147  ]);
148 
149 =head1 DESCRIPTION
150 
151 Hashes are great for storing named data, but if you want more than one entry
152 for a name, you have to use a list of pairs. Even then, this is really boring
153 to write:
154 
155  $values = [
156  foo => undef,
157  bar => undef,
158  baz => undef,
159  xyz => { ... },
160  ];
161 
162 Just look at all those undefs! Don't worry, we can get rid of those:
163 
164  $values = [
165  map { $_ => undef } qw(foo bar baz),
166  xyz => { ... },
167  ];
168 
169 Aaaauuugh! We've saved a little typing, but now it requires thought to read,
170 and thinking is even worse than typing... and it's got a bug! It looked right,
171 didn't it? Well, the C<< xyz => { ... } >> gets consumed by the map, and we
172 don't get the data we wanted.
173 
174 With BASIS::Data::OptList, you can do this instead:
175 
176  $values = BASIS::Data::OptList::mkopt([
177  qw(foo bar baz),
178  xyz => { ... },
179  ]);
180 
181 This works by assuming that any defined scalar is a name and any reference
182 following a name is its value.
183 
184 =head1 FUNCTIONS
185 
186 =head2 mkopt
187 
188  my $opt_list = BASIS::Data::OptList::mkopt($input, \%arg);
189 
190 Valid arguments are:
191 
192  moniker - a word used in errors to describe the opt list; encouraged
193  require_unique - if true, no name may appear more than once
194  must_be - types to which opt list values are limited (described below)
195  name_test - a coderef used to test whether a value can be a name
196  (described below, but you probably don't want this)
197 
198 This produces an array of arrays; the inner arrays are name/value pairs.
199 Values will be either "undef" or a reference.
200 
201 Positional parameters may be used for compability with the old C<mkopt>
202 interface:
203 
204  my $opt_list = BASIS::Data::OptList::mkopt($input, $moniker, $req_uni, $must_be);
205 
206 Valid values for C<$input>:
207 
208  undef -> []
209  hashref -> [ [ key1 => value1 ] ... ] # non-ref values become undef
210  arrayref -> every name followed by a non-name becomes a pair: [ name => ref ]
211  every name followed by undef becomes a pair: [ name => undef ]
212  otherwise, it becomes [ name => undef ] like so:
213  [ "a", "b", [ 1, 2 ] ] -> [ [ a => undef ], [ b => [ 1, 2 ] ] ]
214 
215 By default, a I<name> is any defined non-reference. The C<name_test> parameter
216 can be a code ref that tests whether the argument passed it is a name or not.
217 This should be used rarely. Interactions between C<require_unique> and
218 C<name_test> are not yet particularly elegant, as C<require_unique> just tests
219 string equality. B<This may change.>
220 
221 The C<must_be> parameter is either a scalar or array of scalars; it defines
222 what kind(s) of refs may be values. If an invalid value is found, an exception
223 is thrown. If no value is passed for this argument, any reference is valid.
224 If C<must_be> specifies that values must be CODE, HASH, ARRAY, or SCALAR, then
225 BASIS::Params::Util is used to check whether the given value can provide that
226 interface. Otherwise, it checks that the given value is an object of the kind.
227 
228 In other words:
229 
230  [ qw(SCALAR HASH Object::Known) ]
231 
232 Means:
233 
234  _SCALAR0($value) or _HASH($value) or _INSTANCE($value, 'Object::Known')
235 
236 =head2 mkopt_hash
237 
238  my $opt_hash = BASIS::Data::OptList::mkopt_hash($input, $moniker, $must_be);
239 
240 Given valid C<L</mkopt>> input, this routine returns a reference to a hash. It
241 will throw an exception if any name has more than one value.
242 
243 =head1 EXPORTS
244 
245 Both C<mkopt> and C<mkopt_hash> may be exported on request.
246 
247 =head1 AUTHOR
248 
249 Ricardo Signes <rjbs@cpan.org>
250 
251 Modified by Andreas Schuh on 6/15/2012 in order to make it a subpackage
252 of the SBIA namespace for inclusion with the BASIS package.
253 
254 =head1 COPYRIGHT AND LICENSE
255 
256 This software is copyright (c) 2006 by Ricardo Signes.
257 
258 This is free software; you can redistribute it and/or modify it under
259 the same terms as the Perl 5 programming language system itself.
260 
261 =cut
262