File Coverage: lib/Yukki/Types.pm

# File Coverage

File: | lib/Yukki/Types.pm
Coverage: | 100.0%

line | stmt | bran | cond | sub | pod | time | code
--- | --- | --- | --- | --- | --- | --- | ---
1 |  |  |  |  |  |  | package Yukki::Types;
2 |
3 | 6 6 |  |  | 6 |  | 37 16 | use v5.24;
4 | 6 6 6 |  |  | 6 |  | 18 6 20 | use utf8;
5 |
6 | 6 |  |  | 6 |  | 135 | use Type::Library -base, -declare => qw(
7 |  |  |  |  |  |  | LoginName AccessLevel
8 |  |  |  |  |  |  | NavigationLinks NavigationMenuMap
9 |  |  |  |  |  |  | BaseURL BaseURLEnum BreadcrumbLinks RepositoryMap
10 |  |  |  |  |  |  | PluginConfig PluginList
11 |  |  |  |  |  |  | PrivilegesMap
12 |  |  |  |  |  |  | EmailAddress YukkiSettings
13 |  |  |  |  |  |  | YukkiWebSettings YukkiSettingsAnonymous
14 | 6 6 |  |  |  |  | 8 32 | );
15 | 6 6 6 |  |  | 6 |  | 6622 9 18 | use Type::Utils qw( declare as where message coerce enum from via class_type );
16 |
17 | 6 6 6 |  |  | 6 |  | 3712 9 19 | use Types::Standard qw( Str Int ArrayRef Maybe HashRef Dict );
18 | 6 6 6 |  |  | 6 |  | 5500 155354 39 | use Types::URI qw( Uri );
19 |
20 | 6 6 6 |  |  | 6 |  | 3228 22378 178 | use Email::Address;
21 | 6 6 6 |  |  | 6 |  | 35 9 339 | use List::Util qw( first all );
22 |
23 | 6 6 6 |  |  | 6 |  | 1051 7130 33 | use namespace::clean;
24 |
25 |  |  |  |  |  |  | # ABSTRACT: standard types for use in Yukki
26 |
27 - 44 |  |
    =head1 SYNOPSIS

      use Yukki::Types qw( LoginName AccessLevel );

      has login_name => ( isa => LoginName );
      has access_level => ( isa => AccessLevel );

    =head1 DESCRIPTION

    A standard type library for Yukki.

    =head1 TYPES

    =head2 LoginName

    This is a valid login name. Login names may only contain letters and numbers, as of this writing.

    =cut
45 |
46 |  |  |  |  |  |  | declare LoginName,
47 |  |  |  |  |  |  | as Str,
48 |  |  |  |  |  |  | where { /^[a-zA-Z0-9_-]{3,20}$/ },
49 |  |  |  |  |  |  | message { "login name $_ must only contain letters and numbers" };
50 |
51 - 59 |  |
    =head2 AccessLevel

    This is a valid access level. This includes any of the following values:

      read
      write
      none

    =cut
60 |
61 |  |  |  |  |  |  | enum AccessLevel, [qw( read write none )];
62 |
63 - 73 |  |
    =head2 NavigationLinks

    This is an array of hashes formatted like:

      {
          label => 'Label',
          href  => '/link/to/somewhere',
          sort  => 40,
      }

    =cut
74 |
75 |  |  |  |  |  |  | declare NavigationLinks,
76 |  |  |  |  |  |  | as ArrayRef[
77 |  |  |  |  |  |  | Dict[
78 |  |  |  |  |  |  | label => Str,
79 |  |  |  |  |  |  | href => Str|Uri,
80 |  |  |  |  |  |  | sort => Maybe[Int],
81 |  |  |  |  |  |  | ],
82 |  |  |  |  |  |  | ];
83 |
84 - 88 |  |
    =head2 NavigationMenuMap

    This is a hash of L</NavigationLinks>.

    =cut
89 |
90 |  |  |  |  |  |  | declare NavigationMenuMap,
91 |  |  |  |  |  |  | as HashRef[ NavigationLinks ];
92 |
93 - 97 |  |
    =head2 BaseURL

    This is either an absolute URL or the words C<SCRIPT_NAME> or C<REWRITE>.

    =cut
98 |
99 |  |  |  |  |  |  | enum BaseURLEnum, [qw( SCRIPT_NAME REWRITE )];
100 |
101 |  |  |  |  |  |  | declare BaseURL, as BaseURLEnum|Uri;
102 |
103 |  |  |  |  |  |  | coerce BaseURL,
104 |  |  |  |  |  |  | from Str,
105 |  |  |  |  |  |  | via {
106 |  |  |  |  |  |  | $_ !~ /^(?:SCRIPT_NAME|REWRITE)$/
107 |  |  |  |  |  |  | && URI->new($_)
108 |  |  |  |  |  |  | };
109 |
110 - 119 |  |
    =head2 BreadcrumbLinks

    This is an array of hashes formatted like:

      {
          label => 'Label',
          href  => '/link/to/somewhere',
      }

    =cut
120 |
121 |  |  |  |  |  |  | declare BreadcrumbLinks,
122 |  |  |  |  |  |  | as ArrayRef[
123 |  |  |  |  |  |  | Dict[
124 |  |  |  |  |  |  | label => Str,
125 |  |  |  |  |  |  | href => Str,
126 |  |  |  |  |  |  | ],
127 |  |  |  |  |  |  | ];
128 |
129 - 133 |  |
    =head2 RepositoryMap

    This is a hash of L<Yukki::Settings::Repository> objects.

    =cut
134 |
135 |  |  |  |  |  |  | my $Repository = class_type 'Yukki::Settings::Repository';
136 |  |  |  |  |  |  | declare RepositoryMap,
137 |  |  |  |  |  |  | as HashRef[$Repository];
138 |
139 |  |  |  |  |  |  | coerce RepositoryMap,
140 |  |  |  |  |  |  | from HashRef,
141 |  |  |  |  |  |  | via {
142 |  |  |  |  |  |  | my $source = $_;
143 |  |  |  |  |  |  | +{
144 |  |  |  |  |  |  | map { $_ => Yukki::Settings::Repository->new($source->{$_}) }
145 |  |  |  |  |  |  | keys %$source
146 |  |  |  |  |  |  | }
147 |  |  |  |  |  |  | };
148 |
149 - 153 |  |
    =head2 PrivilegeMap

    This is a hash of L<Yukki::Settings::Privileges> objects.

    =cut
154 |
155 |  |  |  |  |  |  | my $Privileges = class_type 'Yukki::Settings::Privileges';
156 |  |  |  |  |  |  | declare PrivilegesMap,
157 |  |  |  |  |  |  | as HashRef[$Privileges];
158 |
159 |  |  |  |  |  |  | coerce PrivilegesMap,
160 |  |  |  |  |  |  | from HashRef,
161 |  |  |  |  |  |  | via {
162 |  |  |  |  |  |  | my $source = $_;
163 |  |  |  |  |  |  | +{
164 |  |  |  |  |  |  | map { $_ => Yukki::Settings::Privileges->new($source->{$_}) }
165 |  |  |  |  |  |  | keys %$source
166 |  |  |  |  |  |  | }
167 |  |  |  |  |  |  | };
168 |
169 - 173 |  |
    =head2 PluginConfig

    A plugin configuration is an array of hashes. Each hash must have at least one key named "module" defined.

    =cut
174 |
175 |  |  |  |  |  |  | declare PluginConfig,
176 |  |  |  |  |  |  | as ArrayRef[HashRef],
177 |  |  |  |  |  |  | where { all { defined $_->{module} } @$_ };
178 |
179 - 183 |  |
    =head2 PluginList

    A plugin list is a loaded set of plugin objects.

    =cut
184 |
185 |  |  |  |  |  |  | my $Plugin = class_type 'Yukki::Web::Plugin';
186 |  |  |  |  |  |  | declare PluginList,
187 |  |  |  |  |  |  | as ArrayRef[$Plugin],
188 |  |  |  |  |  |  | message {
189 |  |  |  |  |  |  | return 'It is not an array of objects.' unless ref $_ eq 'ARRAY';
190 |  |  |  |  |  |  | my $bad = first { not blessed $_ or not $_->isa('Yukki::Web::Plugin') }
191 |  |  |  |  |  |  | @$_;
192 |  |  |  |  |  |  | $bad = blessed $bad if blessed $bad;
193 |  |  |  |  |  |  | return "It contains $bad, which is not a Yukki::Web::Plugin.";
194 |  |  |  |  |  |  | };
195 |
196 - 204 |  |
    =head1 COERCIONS

    In addition to the types above, these coercions are provided for other types.

    =head2 EmailAddress

    Coerces a C<Str> into an L<Email::Address>.

    =cut
205 |
206 |  |  |  |  |  |  | class_type EmailAddress, { class => 'Email::Address' };
207 |  |  |  |  |  |  | coerce EmailAddress,
208 |  |  |  |  |  |  | from Str,
209 |  |  |  |  |  |  | via { (Email::Address->parse($_))[0] };
210 |
211 - 215 |  |
    =head2 YukkiSettings

    Coerces a C<HashRef> into this object by passing the value to the constructor.

    =cut
216 |
217 |  |  |  |  |  |  | class_type YukkiSettings, { class => 'Yukki::Settings' };
218 |  |  |  |  |  |  | coerce YukkiSettings,
219 |  |  |  |  |  |  | from HashRef,
220 |  |  |  |  |  |  | via { Yukki::Settings->new($_) };
221 |
222 - 226 |  |
    =head2 YukkiWebSettings

    Coerces a C<HashRef> into a L<Yukki::Web::Settings>.

    =cut
227 |
228 |  |  |  |  |  |  | class_type YukkiWebSettings, { class => 'Yukki::Web::Settings' };
229 |  |  |  |  |  |  | coerce YukkiWebSettings,
230 |  |  |  |  |  |  | from HashRef,
231 |  |  |  |  |  |  | via { Yukki::Web::Settings->new($_) };
232 |
233 - 237 |  |
    =head2 YukkiSettingsAnonymous

    Coerces a C<HashRef> into this object by passing the value to the constructor.

    =cut
238 |
239 |  |  |  |  |  |  | class_type YukkiSettingsAnonymous, { class => 'Yukki::Settings::Anonymous' };
240 |  |  |  |  |  |  | coerce YukkiSettingsAnonymous,
241 |  |  |  |  |  |  | from HashRef,
242 |  |  |  |  |  |  | via { Yukki::Settings::Anonymous->new($_) };
243 |
244 |  |  |  |  |  |  | 1;