File Coverage

blib/lib/Specio/Coercion.pm
Criterion Covered Total %
statement 46 46 100.0
branch 7 10 70.0
condition 7 9 77.7
subroutine 13 13 100.0
pod 4 5 80.0
total 77 83 92.7


line stmt bran cond sub pod time code
1              
2             use strict;
3 30     30   161 use warnings;
  30         45  
  30         712  
4 30     30   117  
  30         50  
  30         925  
5             our $VERSION = '0.48';
6              
7             use Specio::OO;
8 30     30   4134  
  30         56  
  30         1250  
9             use Role::Tiny::With;
10 30     30   4287  
  30         2562  
  30         1181  
11             use Specio::Role::Inlinable;
12 30     30   4018 with 'Specio::Role::Inlinable';
  30         52  
  30         11702  
13              
14             {
15             ## no critic (Subroutines::ProtectPrivateSubs)
16             my $role_attrs = Specio::Role::Inlinable::_attrs();
17             ## use critic
18              
19             my $attrs = {
20             %{$role_attrs},
21             from => {
22             does => 'Specio::Constraint::Role::Interface',
23             required => 1,
24             },
25             to => {
26             does => 'Specio::Constraint::Role::Interface',
27             required => 1,
28             weak_ref => 1,
29             },
30             _coercion => {
31             isa => 'CodeRef',
32             predicate => '_has_coercion',
33             init_arg => 'coercion',
34             },
35             _optimized_coercion => {
36             isa => 'CodeRef',
37             init_arg => undef,
38             lazy => 1,
39             builder => '_build_optimized_coercion',
40             },
41             };
42              
43             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
44             return $attrs;
45             }
46 61     61   108 }
47              
48             my $self = shift;
49              
50             die
51 19     19 0 278 'A type coercion should have either a coercion or inline_generator parameter, not both'
52             if $self->_has_coercion && $self->_has_inline_generator;
53 19 50 66     46  
54             die
55             'A type coercion must have either a coercion or inline_generator parameter'
56             unless $self->_has_coercion || $self->_has_inline_generator;
57 19 100 100     127  
58             return;
59             }
60              
61 18         118 my $self = shift;
62             my $value = shift;
63              
64             return $self->_optimized_coercion->($value);
65 11     11 1 17 }
66 11         17  
67             my $self = shift;
68 11         29  
69             return $self->_inline_generator->( $self, @_ );
70             }
71              
72 13     13 1 26 my $self = shift;
73              
74 13         29 if ( $self->_has_inline_generator ) {
75             return $self->_generated_inline_sub;
76             }
77             else {
78 8     8   39 return $self->_coercion;
79             }
80 8 100       17 }
81 1         6  
82             my $self = shift;
83              
84 7         33 return $self->_has_inline_generator && $self->from->can_be_inlined;
85             }
86              
87             my $self = shift;
88              
89 27     27 1 34 my $from_name
90             = defined $self->from->name ? $self->from->name : 'anonymous type';
91 27   66     48 my $to_name
92             = defined $self->to->name ? $self->to->name : 'anonymous type';
93             my $desc = "coercion from $from_name to $to_name";
94              
95 1     1   10 $desc .= q{ } . $self->declared_at->description;
96              
97 1 50       2 return $desc;
98             }
99 1 50       41  
100             my $self = shift;
101 1         13 my $new_to = shift;
102              
103 1         4 my $from = $self->from;
104              
105 1         3 local $self->{from} = undef;
106             local $self->{to} = undef;
107              
108             my $clone = $self->clone;
109 1     1 1 2  
110 1         1 $clone->{from} = $from;
111             $clone->{to} = $new_to;
112 1         2  
113             return $clone;
114 1         4 }
115 1         2  
116             __PACKAGE__->_ooify;
117 1         3  
118             1;
119 1         2  
120 1         3 # ABSTRACT: A class representing a coercion from one type to another
121              
122 1         3  
123             =pod
124              
125             =encoding UTF-8
126              
127             =head1 NAME
128              
129             Specio::Coercion - A class representing a coercion from one type to another
130              
131             =head1 VERSION
132              
133             version 0.48
134              
135             =head1 SYNOPSIS
136              
137             my $coercion = $type->coercion_from_type('Int');
138              
139             my $new_value = $coercion->coerce_value(42);
140              
141             if ( $coercion->can_be_inlined() ) {
142             my $code = $coercion->inline_coercion('$_[0]');
143             }
144              
145             =head1 DESCRIPTION
146              
147             This class represents a coercion from one type to another. Internally, a
148             coercion is a piece of code that takes a value of one type returns a new value
149             of a new type. For example, a coercion from c<Num> to C<Int> might round a
150             number to its nearest integer and return that integer.
151              
152             Coercions can be implemented either as a simple subroutine reference or as an
153             inline generator subroutine. Using an inline generator is faster but more
154             complicated.
155              
156             =for Pod::Coverage BUILD clone_with_new_to
157              
158             =head1 API
159              
160             This class provides the following methods.
161              
162             =head2 Specio::Coercion->new( ... )
163              
164             This method creates a new coercion object. It accepts the following named
165             parameters:
166              
167             =over 4
168              
169             =item * from => $type
170              
171             The type this coercion is from. The type must be an object which does the
172             L<Specio::Constraint::Role::Interface> interface.
173              
174             This parameter is required.
175              
176             =item * to => $type
177              
178             The type this coercion is to. The type must be an object which does the
179             L<Specio::Constraint::Role::Interface> interface.
180              
181             This parameter is required.
182              
183             =item * coercion => sub { ... }
184              
185             A subroutine reference implementing the coercion. It will be called as a method
186             on the object and passed a single argument, the value to coerce.
187              
188             It should return the new value.
189              
190             This parameter is mutually exclusive with C<inline_generator>.
191              
192             Either this parameter or the C<inline_generator> parameter is required.
193              
194             You can also pass this option with the key C<using> in the parameter list.
195              
196             =item * inline_generator => sub { ... }
197              
198             This should be a subroutine reference which returns a string containing a
199             single term. This code should I<not> end in a semicolon. This code should
200             implement the coercion.
201              
202             The generator will be called as a method on the coercion with a single
203             argument. That argument is the name of the variable being coerced, something
204             like C<'$_[0]'> or C<'$var'>.
205              
206             This parameter is mutually exclusive with C<coercion>.
207              
208             Either this parameter or the C<coercion> parameter is required.
209              
210             You can also pass this option with the key C<inline> in the parameter list.
211              
212             =item * inline_environment => {}
213              
214             This should be a hash reference of variable names (with sigils) and values for
215             that variable. The values should be I<references> to the values of the
216             variables.
217              
218             This environment will be used when compiling the coercion as part of a
219             subroutine. The named variables will be captured as closures in the generated
220             subroutine, using L<Eval::Closure>.
221              
222             It should be very rare to need to set this in the constructor. It's more likely
223             that a special coercion subclass would need to provide values that it generates
224             internally.
225              
226             This parameter defaults to an empty hash reference.
227              
228             =item * declared_at => $declared_at
229              
230             This parameter must be a L<Specio::DeclaredAt> object.
231              
232             This parameter is required.
233              
234             =back
235              
236             =head2 $coercion->from(), $coercion->to(), $coercion->declared_at()
237              
238             These methods are all read-only attribute accessors for the corresponding
239             attribute.
240              
241             =head2 $coercion->description
242              
243             This returns a string describing the coercion. This includes the names of the
244             to and from type and where the coercion was declared, so you end up with
245             something like C<'coercion from Foo to Bar declared in package My::Lib
246             (lib/My/Lib.pm) at line 42'>.
247              
248             =head2 $coercion->coerce($value)
249              
250             Given a value of the right "from" type, returns a new value of the "to" type.
251              
252             This method does not actually check that the types of given or return values.
253              
254             =head2 $coercion->inline_coercion($var)
255              
256             Given a variable name like C<'$_[0]'> this returns a string with code for the
257             coercion.
258              
259             Note that this method will die if the coercion does not have an inline
260             generator.
261              
262             =head2 $coercion->can_be_inlined()
263              
264             This returns true if the coercion has an inline generator I<and> the constraint
265             it is from can be inlined. This exists primarily for the benefit of the
266             C<inline_coercion_and_check()> method for type constraint object.
267              
268             =head2 $coercion->inline_environment()
269              
270             This returns a hash defining the variables that need to be closed over when
271             inlining the coercion. The keys are full variable names like C<'$foo'> or
272             C<'@bar'>. The values are I<references> to a variable of the matching type.
273              
274             =head2 $coercion->clone()
275              
276             Returns a clone of this object.
277              
278             =head2 $coercion->clone_with_new_to($new_to_type)
279              
280             This returns a clone of the coercion, replacing the "to" type with a new one.
281             This is intended for use when the to type itself is being cloned as part of
282             importing that type. We need to make sure the newly cloned coercion has the
283             newly cloned type as well.
284              
285             =head1 ROLES
286              
287             This class does the L<Specio::Role::Inlinable> role.
288              
289             =head1 SUPPORT
290              
291             Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
292              
293             =head1 SOURCE
294              
295             The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
296              
297             =head1 AUTHOR
298              
299             Dave Rolsky <autarch@urth.org>
300              
301             =head1 COPYRIGHT AND LICENSE
302              
303             This software is Copyright (c) 2012 - 2022 by Dave Rolsky.
304              
305             This is free software, licensed under:
306              
307             The Artistic License 2.0 (GPL Compatible)
308              
309             The full text of the license can be found in the
310             F<LICENSE> file included with this distribution.
311              
312             =cut