| File: | lib/Yukki/Types.pm |
| Coverage: | 100.0% |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package Yukki::Types; | ||||||
| 2 | |||||||
| 3 | 6 6 | 37 16 | use v5.24; | ||||
| 4 | 6 6 6 | 18 6 20 | use utf8; | ||||
| 5 | |||||||
| 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 | 6622 9 18 | use Type::Utils qw( declare as where message coerce enum from via class_type ); | ||||
| 16 | |||||||
| 17 | 6 6 6 | 3712 9 19 | use Types::Standard qw( Str Int ArrayRef Maybe HashRef Dict ); | ||||
| 18 | 6 6 6 | 5500 155354 39 | use Types::URI qw( Uri ); | ||||
| 19 | |||||||
| 20 | 6 6 6 | 3228 22378 178 | use Email::Address; | ||||
| 21 | 6 6 6 | 35 9 339 | use List::Util qw( first all ); | ||||
| 22 | |||||||
| 23 | 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; | ||||||