File Coverage

blib/lib/Syccess.pm
Criterion Covered Total %
statement 46 46 100.0
branch 5 6 83.3
condition n/a
subroutine 15 15 100.0
pod 2 4 50.0
total 68 71 95.7


line stmt bran cond sub pod time code
1             package Syccess;
2             our $AUTHORITY = 'cpan:GETTY';
3             # ABSTRACT: Easy Validation Handler
4             $Syccess::VERSION = '0.104';
5 9     9   107345 use Moo;
  9         81620  
  9         31  
6 9     9   9154 use Module::Runtime qw( use_module );
  9         693  
  9         715  
7 9     9   4881 use Tie::IxHash;
  9         29079  
  9         4224  
8              
9             with qw(
10             MooX::Traits
11             );
12              
13             has validator_namespaces => (
14             is => 'lazy',
15             );
16              
17             sub _build_validator_namespaces {
18 9     9   64 my ( $self ) = @_;
19             return [
20 9         9 @{$self->custom_validator_namespaces},
  9         130  
21             'Syccess::Validator',
22             'SyccessX::Validator',
23             ];
24             }
25              
26             has custom_validator_namespaces => (
27             is => 'lazy',
28             );
29              
30             sub _build_custom_validator_namespaces {
31 9     9   101 return [];
32             }
33              
34             has field_class => (
35             is => 'lazy',
36             );
37              
38             sub _build_field_class {
39 9     9   83 return 'Syccess::Field';
40             }
41              
42             has field_traits => (
43             is => 'ro',
44             predicate => 1,
45             );
46              
47             has result_class => (
48             is => 'lazy',
49             );
50              
51             sub _build_result_class {
52 9     9   79 return 'Syccess::Result';
53             }
54              
55             has result_traits => (
56             is => 'ro',
57             predicate => 1,
58             );
59              
60             has error_class => (
61             is => 'lazy',
62             );
63              
64             sub _build_error_class {
65 9     9   88 return 'Syccess::Error';
66             }
67              
68             has error_traits => (
69             is => 'ro',
70             predicate => 1,
71             );
72              
73             has errors_args => (
74             is => 'ro',
75             predicate => 1,
76             );
77              
78             has fields_list => (
79             is => 'ro',
80             required => 1,
81             init_arg => 'fields',
82             );
83              
84             has fields => (
85             is => 'lazy',
86             init_arg => undef,
87             );
88              
89             sub _build_fields {
90 9     9   59 my ( $self ) = @_;
91 9         11 my @fields;
92 9         13 my $fields_list = Tie::IxHash->new(@{$self->fields_list});
  9         104  
93 9         350 for my $key ($fields_list->Keys) {
94 19         169 push @fields, $self->new_field($key,$fields_list->FETCH($key));
95             }
96 9         179 return [ @fields ];
97             }
98              
99             sub field {
100 2     2 1 917 my ( $self, $name ) = @_;
101 2         3 my @field = grep { $_->name eq $name } @{$self->fields};
  2         19  
  2         39  
102 2 50       10 return scalar @field ? $field[0] : undef;
103             }
104              
105             has resulting_field_class => (
106             is => 'lazy',
107             init_arg => undef,
108             );
109              
110             sub _build_resulting_field_class {
111 9     9   58 my ( $self ) = @_;
112 9         125 my $field_class = use_module($self->field_class);
113 9 100       108 if ($self->has_field_traits) {
114 2         1 $field_class = $field_class->with_traits(@{$self->field_traits});
  2         13  
115             }
116 9         368 return $field_class;
117             }
118              
119             sub new_field {
120 19     19 0 96 my ( $self, $name, $validators_list ) = @_;
121 19         281 return $self->resulting_field_class->new(
122             syccess => $self,
123             name => $name,
124             validators => $validators_list,
125             );
126             }
127              
128             has resulting_result_class => (
129             is => 'lazy',
130             init_arg => undef,
131             );
132              
133             sub _build_resulting_result_class {
134 9     9   60 my ( $self ) = @_;
135 9         130 my $result_class = use_module($self->result_class);
136 9 100       92 if ($self->has_result_traits) {
137 2         3 $result_class = $result_class->with_traits(@{$self->result_traits});
  2         14  
138             }
139 9         409 return $result_class;
140             }
141              
142             sub validate {
143 19     19 1 3615 my ( $self, %params ) = @_;
144 19         426 return $self->resulting_result_class->new(
145             syccess => $self,
146             params => { %params },
147             );
148             }
149              
150             sub BUILD {
151 9     9 0 25228 my ( $self ) = @_;
152 9         147 $self->fields;
153             }
154              
155             1;
156              
157             __END__
158              
159             =pod
160              
161             =head1 NAME
162              
163             Syccess - Easy Validation Handler
164              
165             =head1 VERSION
166              
167             version 0.104
168              
169             =head1 SYNOPSIS
170              
171             use Syccess;
172              
173             my $syccess = Syccess->new(
174             fields => [
175             foo => [ required => 1, length => 4, label => 'PIN Code' ],
176             bar => [ required => { message => 'You have 5 seconds to comply.' } ],
177             # if no label is given its made out of the name so 'Bar' in this case
178             baz => [ length => { min => 2, max => 4 }, label => 'Ramba Zamba' ],
179             ],
180             );
181              
182             my $result = $syccess->validate( foo => 1, bar => 1 );
183             if ($result->success) {
184             print "Yeah!\n";
185             }
186              
187             my $failed = $syccess->validate();
188             unless ($failed->success) {
189             for my $message (@{$failed->errors}) {
190             print $message->message."\n";
191             }
192             }
193              
194             my $traitsful_syccess = Syccess->new(
195             result_traits => [qw( MyApp::Syccess::ResultRole )],
196             error_traits => [qw( MyApp::Syccess::ErrorRole )],
197             field_traits => [qw( MyApp::Syccess::FieldRole )],
198             fields => [
199             # ...
200             ],
201             );
202              
203             =head1 DESCRIPTION
204              
205             I<Syccess> is developed for L<SyContent|https://sycontent.de/>.
206              
207             I<Syccess> is a simple validation layer, which allows to check a hash of values
208             against a validation definition and give back success or allow to see the
209             error messages of the failure. I<Syccess> is not made for caring about anything
210             else, so for a higher level library you integrate Syccess and not try to extend
211             it. I<Syccess> is not made for giving extra attributes to the fields, the
212             validator should be the key topic here, and it is very easy to make own
213             validators specific for your environment, see L<Syccess::Validator> and
214             L<Syccess::ValidatorSimple>, but you should be aware that most requirements
215             should be covered with L<Syccess::Validator::Code> and
216             L<Syccess::Validator::Call>, as both allow you to use simple validation methods
217             you already may have in your model. This way you don't end up making I<Syccess>
218             specific procedures, that might be harder to maintain.
219              
220             The complete concept of Syccess is read only, which means, a call to
221             L</validate> will produce a L<Syccess::Result> which contains the resulting
222             information, while the I<Syccess> object stays unchanged. A I<Syccess> object
223             contains a I<Syccess::Field> object for every field of your L</fields>
224             definition. On this field you have an object for every validator. Be aware that
225             the validators are given as ArrayRef and you can use the same validator several
226             times.
227              
228             B<BEHAVIOUR INFO:> The validators provided by the Syccess core are all
229             designed to ignore a non existing value, an undefined value or an empty
230             string. If you want that giving those leads to an error, then you must use the
231             B<required> validator of L<Syccess::Validator::Required>. If you need to check
232             against those values, for example you use L<Syccess::Validator::Code> and in
233             some cases an undefined value is valid and sometimes not, then you must make
234             a custom validator, see L</custom_validator_namespaces>.
235              
236             =head1 ATTRIBUTES
237              
238             =head2 fields
239              
240             Required ArrayRef containing the definition of the fields for the validation.
241             It always first the name of the field and then an ArrayRef again with the
242             validators. Those will be dispatched to instantiation L<Syccess::Field> to
243             create the B<fields> objects. See more about validators on its attribute at
244             L<Syccess::Field>. You can provide a validator several times, like several
245             B<regex> or several B<code> validators.
246              
247             =head2 validator_namespaces
248              
249             This attribute is the main namespace collection, where Syccess searches for
250             its validators. Normally you do not set it directly, instead you set
251             L</custom_validator_namespaces>, else you would remove I<Syccess::Validator>
252             and the I<SyccessX::Validator>, which are automatically added after the
253             L</custom_validator_namespaces> by default here.
254              
255             =head2 custom_validator_namespaces
256              
257             Here you define an ArrayRef of the namespaces that should be used additional
258             to the default ones. For example, if you add validator B<foo_bar>, then
259             Syccess would search first with your custom namespace, for example
260             I<MyApp::Validator::FooBar>, and after that it checks for
261             I<Syccess::Validator::FooBar> and finally I<SyccessX::Validator::FooBar>.
262              
263             For making custom validator, you must use the L<Syccess::Validator> role, which
264             allows to check over all params given. If you just want to make a simple
265             validator that checks against only the relevant value of the field, then you
266             can use L<Syccess::ValidatorSimple>.
267              
268             Please use B<SyccessX::Validator> as namespace if you want to upload a new
269             general validator to B<CPAN>.
270              
271             =head2 result_class
272              
273             The class which is used for the result. Default is L<Syccess::Result>.
274              
275             =head2 error_class
276              
277             The class which is used for errors. Default is L<Syccess::Error>.
278              
279             =head2 field_class
280              
281             The class which is used for the fields. Default is L<Syccess::Field>.
282              
283             =head2 result_traits
284              
285             Traits to be added to the L<Syccess::Result> class. See B<with_traits> at
286             L<MooX::Traits>.
287              
288             =head2 error_traits
289              
290             Traits to be added to the L<Syccess::Error> class. See B<with_traits> at
291             L<MooX::Traits>.
292              
293             =head2 field_traits
294              
295             Traits to be added to the L<Syccess::Field> class. See B<with_traits> at
296             L<MooX::Traits>.
297              
298             =head2 errors_args
299              
300             Here you can give custom attributes which are dispatched to the instantiation
301             of the L</error_class> objects.
302              
303             =head1 METHODS
304              
305             =head2 new_with_traits
306              
307             See L<MooX::Traits>.
308              
309             =head2 field
310              
311             Get the L<Syccess::Field> for the name given as parameter.
312              
313             =head2 fields
314              
315             Get all L<Syccess::Field> of the Syccess object.
316              
317             =head2 validate
318              
319             This is the main function to produce a L</result_class> object, which will
320             then hold the result and the errors of the validation process. This function
321             must be called with a Hash (no HashRef yet supported) of the values to check
322             for the validation.
323              
324             =encoding utf8
325              
326             =head1 Core Validators
327              
328             =head2 call
329              
330             L<Syccess::Validator::Call> - Calling a method on an object for validation
331              
332             =head2 code
333              
334             L<Syccess::Validator::Code> - Using a CodeRef to validate
335              
336             =head2 in
337              
338             L<Syccess::Validator::In> - Checking if a value is in a given list of values
339              
340             =head2 is_number
341              
342             L<Syccess::Validator::IsNumber> - Check if the value is a number
343              
344             =head2 length
345              
346             L<Syccess::Validator::Length> - Check for length of the string, if its
347             specific or min or max values.
348              
349             =head2 regex
350              
351             L<Syccess::Validator::Regex> - Check the value against a regex
352              
353             =head2 required
354              
355             L<Syccess::Validator::Required> - Value must be provided, and cant be empty
356              
357             =head1 Label Concept
358              
359             The system is designed to deliver a validation only, which leaded to the
360             decision to not include the ability to give fields specific attributes. As
361             a consequence out of this, the implementation of a label concept (so a visual
362             representation of the field name in the error message) is done with a special
363             trick, as seen in the L</SYNOPSIS>, through giving it as just another
364             validator, it will then be consumed as label for the field instead of the load
365             of another validator object.
366              
367             =head1 TODO
368              
369             One bigger feature planned is adding the ability to stack I<Syccess> objects to
370             allow cascaded parameters for validation. Currently this is not implemented,
371             because, if you integrate I<Syccess> in a bigger context, you will want to control
372             the cascading yourself (in my case L<SyForm> takes the control of this). But I
373             hope I will find later the time to make this possible.
374              
375             =head1 SUPPORT
376              
377             IRC
378              
379             Join irc.perl.org and msg Getty
380              
381             Repository
382              
383             http://github.com/Getty/p5-syccess
384             Pull request and additional contributors are welcome
385              
386             Issue Tracker
387              
388             http://github.com/Getty/p5-syccess/issues
389              
390             =head1 AUTHOR
391              
392             Torsten Raudssus <torsten@raudss.us>
393              
394             =head1 COPYRIGHT AND LICENSE
395              
396             This software is copyright (c) 2017 by Torsten Raudssus.
397              
398             This is free software; you can redistribute it and/or modify it under
399             the same terms as the Perl 5 programming language system itself.
400              
401             =cut