Commit | Line | Data |
44babf1f |
1 | package Moose::Meta::Method::Accessor::Native::Collection; |
2 | |
3 | use strict; |
4 | use warnings; |
5 | |
8b9641b8 |
6 | use Moose::Role; |
7 | |
8 | requires qw( _adds_members ); |
9 | |
53a4677c |
10 | sub _inline_coerce_new_values { |
7bf5e58d |
11 | my $self = shift; |
12 | |
53a4677c |
13 | return unless $self->associated_attribute->should_coerce; |
7bf5e58d |
14 | |
53a4677c |
15 | return unless $self->_tc_member_type_can_coerce; |
7bf5e58d |
16 | |
53a4677c |
17 | return ( |
ec02b571 |
18 | '(' . $self->_new_members . ') = map { $member_coercion->($_) }', |
53a4677c |
19 | $self->_new_members . ';', |
20 | ); |
21 | } |
7bf5e58d |
22 | |
23 | sub _tc_member_type_can_coerce { |
24 | my $self = shift; |
25 | |
26 | my $member_tc = $self->_tc_member_type; |
27 | |
28 | return $member_tc && $member_tc->has_coercion; |
29 | } |
30 | |
31 | sub _tc_member_type { |
32 | my $self = shift; |
33 | |
53a4677c |
34 | my $tc = $self->associated_attribute->type_constraint; |
35 | while ($tc) { |
7bf5e58d |
36 | return $tc->type_parameter |
37 | if $tc->can('type_parameter'); |
53a4677c |
38 | $tc = $tc->parent; |
7bf5e58d |
39 | } |
40 | |
41 | return; |
42 | } |
43 | |
6e50f7e9 |
44 | sub _writer_value_needs_copy { |
44babf1f |
45 | my $self = shift; |
46 | |
47 | return $self->_constraint_must_be_checked |
48 | && !$self->_check_new_members_only; |
53a4677c |
49 | } |
44babf1f |
50 | |
53a4677c |
51 | sub _inline_tc_code { |
52 | my $self = shift; |
a619fc2f |
53 | my ($value, $tc, $coercion, $message, $is_lazy) = @_; |
44babf1f |
54 | |
53a4677c |
55 | return unless $self->_constraint_must_be_checked; |
44babf1f |
56 | |
53a4677c |
57 | if ($self->_check_new_members_only) { |
58 | return unless $self->_adds_members; |
44babf1f |
59 | |
53a4677c |
60 | return $self->_inline_check_member_constraint($self->_new_members); |
44babf1f |
61 | } |
62 | else { |
53a4677c |
63 | return ( |
c40e4359 |
64 | $self->_inline_check_coercion($value, $tc, $coercion, $is_lazy), |
a619fc2f |
65 | $self->_inline_check_constraint($value, $tc, $message, $is_lazy), |
53a4677c |
66 | ); |
44babf1f |
67 | } |
53a4677c |
68 | } |
44babf1f |
69 | |
70 | sub _check_new_members_only { |
71 | my $self = shift; |
72 | |
73 | my $attr = $self->associated_attribute; |
74 | |
75 | my $tc = $attr->type_constraint; |
76 | |
77 | # If we have a coercion, we could come up with an entirely new value after |
78 | # coercing, so we need to check everything, |
79 | return 0 if $attr->should_coerce && $tc->has_coercion; |
80 | |
81 | # If the parent is our root type (ArrayRef, HashRef, etc), that means we |
82 | # can just check the new members of the collection, because we know that |
83 | # we will always be generating an appropriate collection type. |
84 | # |
85 | # However, if this type has its own constraint (it's Parameteriz_able_, |
86 | # not Paramet_erized_), we don't know what is being checked by the |
87 | # constraint, so we need to check the whole value, not just the members. |
88 | return 1 |
89 | if $self->_is_root_type( $tc->parent ) |
d85da60f |
90 | && ( $tc->isa('Moose::Meta::TypeConstraint::Parameterized') |
91 | || $tc->isa('Type::Constraint::Parameterized') ); |
44babf1f |
92 | |
93 | return 0; |
94 | } |
95 | |
96 | sub _inline_check_member_constraint { |
53a4677c |
97 | my $self = shift; |
98 | my ($new_value) = @_; |
44babf1f |
99 | |
100 | my $attr_name = $self->associated_attribute->name; |
101 | |
31056177 |
102 | my $check |
7c047a36 |
103 | = $self->_tc_member_type->can_be_inlined |
0e8cddd7 |
104 | ? '! (' . $self->_tc_member_type->_inline_check('$new_val') . ')' |
105 | : ' !$member_tc->($new_val) '; |
31056177 |
106 | |
53a4677c |
107 | return ( |
0e8cddd7 |
108 | 'for my $new_val (' . $new_value . ') {', |
31056177 |
109 | "if ($check) {", |
53a4677c |
110 | $self->_inline_throw_error( |
111 | '"A new member value for ' . $attr_name |
ec02b571 |
112 | . ' does not pass its type constraint because: "' . ' . ' |
113 | . 'do { local $_ = $new_val; $member_message->($new_val) }', |
0e8cddd7 |
114 | 'data => $new_val', |
53a4677c |
115 | ) . ';', |
116 | '}', |
117 | '}', |
118 | ); |
44babf1f |
119 | } |
120 | |
53a4677c |
121 | sub _inline_get_old_value_for_trigger { |
122 | my $self = shift; |
123 | my ($instance, $old) = @_; |
44babf1f |
124 | |
125 | my $attr = $self->associated_attribute; |
53a4677c |
126 | return unless $attr->has_trigger; |
44babf1f |
127 | |
53a4677c |
128 | return ( |
1e2c801e |
129 | 'my ' . $old . ' = ' . $self->_has_value($instance), |
130 | '? ' . $self->_copy_old_value($self->_get_value($instance)), |
53a4677c |
131 | ': ();', |
132 | ); |
133 | } |
44babf1f |
134 | |
8b9641b8 |
135 | around _eval_environment => sub { |
44babf1f |
136 | my $orig = shift; |
137 | my $self = shift; |
138 | |
139 | my $env = $self->$orig(@_); |
140 | |
7bf5e58d |
141 | my $member_tc = $self->_tc_member_type; |
142 | |
143 | return $env unless $member_tc; |
144 | |
7bf5e58d |
145 | $env->{'$member_tc'} = \( $member_tc->_compiled_type_constraint ); |
ec02b571 |
146 | $env->{'$member_coercion'} = \( |
147 | $member_tc->coercion->_compiled_type_coercion |
148 | ) if $member_tc->has_coercion; |
149 | $env->{'$member_message'} = \( |
150 | $member_tc->has_message |
151 | ? $member_tc->message |
152 | : $member_tc->_default_message |
153 | ); |
44babf1f |
154 | |
0e8cddd7 |
155 | my $tc_env = $member_tc->inline_environment(); |
156 | |
157 | $env = { %{$env}, %{$tc_env} }; |
158 | |
44babf1f |
159 | return $env; |
8b9641b8 |
160 | }; |
44babf1f |
161 | |
162 | no Moose::Role; |
163 | |
164 | 1; |