Commit | Line | Data |
0fb58589 |
1 | package SQL::Translator::Role::ListAttr; |
4e43db0d |
2 | |
e559989c |
3 | use warnings; |
4 | use strict; |
5 | |
4e43db0d |
6 | =head1 NAME |
7 | |
8 | SQL::Translator::Role::ListAttr - context-sensitive list attributes |
9 | |
10 | =head1 SYNOPSIS |
11 | |
12 | package Foo; |
13 | use Moo; |
14 | use SQL::Translator::Role::ListAttr; |
15 | |
16 | with ListAttr foo => ( uniq => 1, append => 1 ); |
17 | |
18 | =head1 DESCRIPTION |
19 | |
20 | This package provides a variable L<Moo::Role> for context-sensitive list |
21 | attributes. |
22 | |
23 | =cut |
24 | |
0fb58589 |
25 | use SQL::Translator::Utils qw(parse_list_arg ex2err); |
26 | use List::MoreUtils qw(uniq); |
68d75205 |
27 | use Sub::Quote qw(quote_sub); |
0fb58589 |
28 | |
29 | use Package::Variant ( |
30 | importing => { |
31 | 'Moo::Role' => [], |
32 | }, |
33 | subs => [qw(has around)], |
34 | ); |
35 | |
4e43db0d |
36 | =head1 FUNCTIONS |
37 | |
38 | =head2 ListAttr $name => %parameters; |
39 | |
40 | Returns a L<Moo::Role> providing an arrayref attribute named C<$name>, |
41 | and wrapping the accessor to provide context-sensitivity both for |
42 | setting and getting. If no C<builder> or C<default> is provided, the |
43 | default value is the empty list. |
44 | |
45 | On setting, the arguments are parsed using |
46 | L<SQL::Translator::Utils/parse_list_arg>, and the accessor will return |
47 | an array reference or a list, depending on context. |
48 | |
49 | =head3 Parameters |
50 | |
51 | =over |
52 | |
53 | =item append |
54 | |
55 | If true, the setter will append arguments to the existing ones, rather |
56 | than replacing them. |
57 | |
58 | =item uniq |
59 | |
60 | If true, duplicate items will be removed, keeping the first one seen. |
61 | |
62 | =item may_throw |
63 | |
64 | If accessing the attribute might L<throw|SQL::Translator::Utils/throw> |
65 | an exception (e.g. from a C<builder> or C<isa> check), this should be |
66 | set to make the accessor store the exception using |
67 | L<SQL::Translator::Role::Error> and return undef. |
68 | |
69 | =item undef_if_empty |
70 | |
71 | If true, and the list is empty, the accessor will return C<undef> |
72 | instead of a reference to an empty in scalar context. |
73 | |
74 | =back |
75 | |
9fc1e74a |
76 | Unknown parameters are passed through to the L<has|Moo/has> call for |
4e43db0d |
77 | the attribute. |
78 | |
79 | =cut |
0fb58589 |
80 | |
81 | sub make_variant { |
82 | my ($class, $target_package, $name, %arguments) = @_; |
83 | |
84 | my $may_throw = delete $arguments{may_throw}; |
85 | my $undef_if_empty = delete $arguments{undef_if_empty}; |
86 | my $append = delete $arguments{append}; |
87 | my $coerce = delete $arguments{uniq} |
88 | ? sub { [ uniq @{parse_list_arg($_[0])} ] } |
89 | : \&parse_list_arg; |
90 | |
91 | has($name => ( |
92 | is => 'rw', |
93 | (!$arguments{builder} ? ( |
68d75205 |
94 | default => quote_sub(q{ [] }), |
0fb58589 |
95 | ) : ()), |
96 | coerce => $coerce, |
97 | %arguments, |
98 | )); |
99 | |
100 | around($name => sub { |
101 | my ($orig, $self) = (shift, shift); |
102 | my $list = parse_list_arg(@_); |
103 | $self->$orig([ @{$append ? $self->$orig : []}, @$list ]) |
104 | if @$list; |
105 | |
106 | my $return; |
107 | if ($may_throw) { |
108 | $return = ex2err($orig, $self) or return; |
109 | } |
110 | else { |
111 | $return = $self->$orig; |
112 | } |
113 | my $scalar_return = !@{$return} && $undef_if_empty ? undef : $return; |
114 | return wantarray ? @{$return} : $scalar_return; |
115 | }); |
116 | } |
117 | |
4e43db0d |
118 | =head1 SEE ALSO |
119 | |
120 | =over |
121 | |
122 | =item L<SQL::Translator::Utils> |
123 | |
124 | =item L<SQL::Translator::Role::Error> |
125 | |
126 | =back |
127 | |
128 | =cut |
129 | |
0fb58589 |
130 | 1; |