File: UsingWithOther.pod

package info (click to toggle)
libtype-tiny-perl 2.002001-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 3,948 kB
  • sloc: perl: 14,610; makefile: 2; sh: 1
file content (212 lines) | stat: -rw-r--r-- 5,668 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
=pod

=encoding utf-8

=head1 NAME

Type::Tiny::Manual::UsingWithOther - using Type::Tiny with Class::InsideOut, Params::Check, and Object::Accessor.

=head1 MANUAL

The antlers crew aren't the only object-oriented programming toolkits in
Perl town. Although Type::Tiny might have been built with Moose, Mouse,
and Moo in mind, it can be used with other toolkits.

These toolkits are... well... hmm... okay... they exist.

If you are starting a new project, there's very little reason not to use
Class::Tiny, Moo, or Moose. So you're probably okay to skip this part of
the fine manual and go straight to L<Type::Tiny::Manual::UsingWithTestMore>.

=head2 Class::InsideOut

You want L<Class::InsideOut> 1.13 or above, which has support for blessed
and overloaded objects (including Type::Tiny type constraints) for the
C<get_hook> and C<set_hook> options.

  package Person {
    use Class::InsideOut qw( public );
    use Types::Standard qw( Str Int );
    use Types::Common::Numeric qw( PositiveInt );
    use Type::Params qw( signature );
    
    # Type checks are really easy.
    # Just supply the type as a set hook.
    public name => my %_name, {
      set_hook => Str,
    };
    
    # Define a type that silently coerces negative values
    # to positive. It's silly, but it works as an example!
    my $Years = PositiveInt->plus_coercions(Int, q{ abs($_) });
    
    # Coercions are more annoying, but possible.
    public age => my %_age, {
      set_hook => sub { $_ = $Years->assert_coerce($_) },
    };
    
    # Parameter checking for methods is as expected.
    sub get_older {
      state $check = signature( method => 1, positional => [ $Years ] );
      my ( $self, $years ) = $check->( @_ );
      $self->_set_age( $self->age + $years );
    }
  }

=head2 Params::Check and Object::Accessor

The Params::Check C<< allow() >> function, the C<allow> option for the
Params::Check C<< check() >> function, and the input validation mechanism
for Object::Accessor all work in the same way, which is basically a
limited pure-Perl implementation of the smart match operator. While this
doesn't directly support Type::Tiny constraints, it does support coderefs.
You can use Type::Tiny's C<compiled_check> method to obtain a suitable
coderef.

L<Param::Check> example:

  my $tmpl = {
    name => { allow => Str->compiled_check },
    age  => { allow => Int->compiled_check },
  };
  check($tmpl, { name => "Bob", age => 32 })
    or die Params::Check::last_error();

L<Object::Accessor> example:

  my $obj = Object::Accessor->new;
  $obj->mk_accessors(
    { name => Str->compiled_check },
    { age  => Int->compiled_check },
  );

I<< Caveat: >> Object::Accessor doesn't die when a value fails to meet its
type constraint; instead it outputs a warning to STDERR. This behaviour can
be changed by setting C<< $Object::Accessor::FATAL = 1 >>.

=head2 Class::Struct

This is proof-of-concept of how Type::Tiny can be used to constrain
attributes for Class::Struct. It's probably not a good idea to use this
in production as it slows down C<UNIVERSAL::isa> globally.

  use Types::Standard -types;
  use Class::Struct;

  {
    my %MAP;
    my $orig_isa = \&UNIVERSAL::isa;
    *UNIVERSAL::isa = sub {
      return $MAP{$1}->check($_[0])
        if $_[1] =~ /^CLASSSTRUCT::TYPETINY::(.+)$/ && exists $MAP{$1};
      goto $orig;
    };
    my $orig_dn = \&Type::Tiny::display_name;
    *Type::Tiny::display_name = sub {
      if (caller(1) eq 'Class::Struct') {
        $MAP{$_[0]{uniq}} = $_[0];
        return "CLASSSTRUCT::TYPETINY::".$_[0]{uniq};
      }
      goto $orig_dn;
    };
  }

  struct Person => [ name => Str, age => Int ];

  my $bob = Person->new(
    name => "Bob",
    age  => 21,
  );

  $bob->name("Robert");   # okay
  $bob->name([]);         # dies

=head2 Class::Plain

There is not currently a high level of integration, but here's a quick
example of type checking attributes in the constructor.

If any of your accessors are C<< :rw >> then you would also need to
add type checks to those.

  use Class::Plain;
  
  class Point {
    use Types::Common -types, -sigs;
    
    field x :reader;
    field y :reader;
    
    signature_for new => (
      method => !!1,
      bless  => !!0,
      named  => [
        x => Int,
        y => Int,
      ],
    );
    
    method as_arrayref () {
      return [ $self->x, $self->y ];
    }
  }

The following signature may also be of interest:

  signature_for new => (
    method   => !!1,
    multiple => [
      {
        named => [
          x => Int,
          y => Int,
        ],
        bless => !!0,
      },
      {
        positional => [ Int, Int ],
        goto_next  => sub {
          my ( $class, $x, $y ) = @_;
          return ( $class, { x => $x, y => $y } ),
        },
      },
    ],
  );

This would allow your class to be instantiated using any of the following:

  my $point11 = Point->new( { x => 1, y => 1 } );
  my $point22 = Point->new(   x => 2, y => 2   );
  my $point33 = Point->new( 3, 3 );

=head1 NEXT STEPS

Here's your next step:

=over

=item * L<Type::Tiny::Manual::UsingWithTestMore>

Type::Tiny for test suites.

=back

=head1 AUTHOR

Toby Inkster E<lt>tobyink@cpan.orgE<gt>.

=head1 COPYRIGHT AND LICENCE

This software is copyright (c) 2013-2014, 2017-2023 by Toby Inkster.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=head1 DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

=cut