File Coverage

blib/lib/Syccess.pm
Criterion Covered Total %
statement 47 47 100.0
branch 5 6 83.3
condition n/a
subroutine 16 16 100.0
pod 2 4 50.0
total 70 73 95.8


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