File Coverage

blib/lib/Data/Object/Attributes.pm
Criterion Covered Total %
statement 63 66 95.4
branch 40 42 95.2
condition 2 3 66.6
subroutine 13 13 100.0
pod 0 2 0.0
total 118 126 93.6


line stmt bran cond sub pod time code
1             package Data::Object::Attributes;
2              
3 1     1   30358 use 5.014;
  1         3  
4              
5 1     1   5 use strict;
  1         2  
  1         18  
6 1     1   4 use warnings;
  1         2  
  1         22  
7 1     1   4 use registry;
  1         2  
  1         15  
8 1     1   4835 use routines;
  1         2  
  1         7  
9              
10 1     1   1422 use Moo;
  1         2  
  1         6  
11              
12             our $VERSION = '0.06'; # VERSION
13              
14             # BUILD
15              
16             my $builders = {};
17              
18 21     21   125489 fun import($class, @args) {
  21         31  
19 21 50       189 my $has = (my $target = caller)->can('has') or return;
20              
21 1     1   394 no strict 'refs';
  1         2  
  1         29  
22 1     1   4 no warnings 'redefine';
  1         2  
  1         69  
23              
24 21         63 *{"${target}::has"} = generate([$class, $target], $has);
  21         76  
25              
26 21         1567 return;
27             }
28              
29 21     21 0 37 fun generate($info, $orig) {
  21         27  
30             # generate "has" keyword
31              
32 21     21   101 return fun(@args) { @_ = options($info, @args); goto $orig };
  21         1911  
  21         26  
  21         47  
  21         74  
33             }
34              
35             $builders->{new} = fun($info, $name, %opts) {
36             if (delete $opts{new}) {
37             $opts{builder} = "new_${name}";
38             $opts{lazy} = 1;
39             }
40              
41             return (%opts);
42             };
43              
44             $builders->{bld} = fun($info, $name, %opts) {
45             $opts{builder} = delete $opts{bld};
46              
47             return (%opts);
48             };
49              
50             $builders->{clr} = fun($info, $name, %opts) {
51             $opts{clearer} = delete $opts{clr};
52              
53             return (%opts);
54             };
55              
56             $builders->{crc} = fun($info, $name, %opts) {
57             $opts{coerce} = delete $opts{crc};
58              
59             return (%opts);
60             };
61              
62             $builders->{def} = fun($info, $name, %opts) {
63             $opts{default} = delete $opts{def};
64              
65             return (%opts);
66             };
67              
68             $builders->{hnd} = fun($info, $name, %opts) {
69             $opts{handles} = delete $opts{hnd};
70              
71             return (%opts);
72             };
73              
74             $builders->{isa} = fun($info, $name, %opts) {
75             return (%opts) if ref($opts{isa});
76              
77             my $registry = registry::access($info->[1]);
78              
79             return (%opts) if !$registry;
80              
81             my $constraint = $registry->lookup($opts{isa});
82              
83             return (%opts) if !$constraint;
84              
85             $opts{isa} = $constraint;
86              
87             return (%opts);
88             };
89              
90             $builders->{lzy} = fun($info, $name, %opts) {
91             $opts{lazy} = delete $opts{lzy};
92              
93             return (%opts);
94             };
95              
96             $builders->{opt} = fun($info, $name, %opts) {
97             delete $opts{opt};
98              
99             $opts{required} = 0;
100              
101             return (%opts);
102             };
103              
104             $builders->{pre} = fun($info, $name, %opts) {
105             $opts{predicate} = delete $opts{pre};
106              
107             return (%opts);
108             };
109              
110             $builders->{rdr} = fun($info, $name, %opts) {
111             $opts{reader} = delete $opts{rdr};
112              
113             return (%opts);
114             };
115              
116             $builders->{req} = fun($info, $name, %opts) {
117             delete $opts{req};
118              
119             $opts{required} = 1;
120              
121             return (%opts);
122             };
123              
124             $builders->{tgr} = fun($info, $name, %opts) {
125             $opts{trigger} = delete $opts{tgr};
126              
127             return (%opts);
128             };
129              
130             $builders->{use} = fun($info, $name, %opts) {
131             if (my $use = delete $opts{use}) {
132             $opts{builder} = $builders->{use_builder}->($info, $name, @$use);
133             $opts{lazy} = 1;
134             }
135              
136             return (%opts);
137             };
138              
139             $builders->{use_builder} = fun($info, $name, $sub, @args) {
140 1     1   2162 return fun($self) {
  1         2  
141 1         3 @_ = ($self, @args);
142              
143 1 50       6 my $point = $self->can($sub) or do {
144 0         0 require Carp;
145              
146 0         0 my $class = $info->[1];
147              
148 0         0 Carp::confess("has '$name' cannot 'use' method '$sub' via package '$class'");
149             };
150              
151 1         16 goto $point;
152             };
153             };
154              
155             $builders->{wkr} = fun($info, $name, %opts) {
156             $opts{weak_ref} = delete $opts{wkr};
157              
158             return (%opts);
159             };
160              
161             $builders->{wrt} = fun($info, $name, %opts) {
162             $opts{writer} = delete $opts{wrt};
163              
164             return (%opts);
165             };
166              
167 21     21 0 72 fun options($info, $name, %opts) {
  21         27  
168 21 100       49 %opts = (is => 'rw') unless %opts;
169              
170 21 100       52 $opts{mod} = 1 if $name =~ s/^\+//;
171              
172 21 100       49 %opts = (%opts, $builders->{new}->($info, $name, %opts)) if defined $opts{new};
173 21 100       44 %opts = (%opts, $builders->{bld}->($info, $name, %opts)) if defined $opts{bld};
174 21 100       44 %opts = (%opts, $builders->{clr}->($info, $name, %opts)) if defined $opts{clr};
175 21 100       36 %opts = (%opts, $builders->{crc}->($info, $name, %opts)) if defined $opts{crc};
176 21 100       44 %opts = (%opts, $builders->{def}->($info, $name, %opts)) if defined $opts{def};
177 21 100       45 %opts = (%opts, $builders->{hnd}->($info, $name, %opts)) if defined $opts{hnd};
178 21 100       38 %opts = (%opts, $builders->{isa}->($info, $name, %opts)) if defined $opts{isa};
179 21 100       39 %opts = (%opts, $builders->{lzy}->($info, $name, %opts)) if defined $opts{lzy};
180 21 100       43 %opts = (%opts, $builders->{opt}->($info, $name, %opts)) if defined $opts{opt};
181 21 100       45 %opts = (%opts, $builders->{pre}->($info, $name, %opts)) if defined $opts{pre};
182 21 100       43 %opts = (%opts, $builders->{rdr}->($info, $name, %opts)) if defined $opts{rdr};
183 21 100       50 %opts = (%opts, $builders->{req}->($info, $name, %opts)) if defined $opts{req};
184 21 100       41 %opts = (%opts, $builders->{tgr}->($info, $name, %opts)) if defined $opts{tgr};
185 21 100       41 %opts = (%opts, $builders->{use}->($info, $name, %opts)) if defined $opts{use};
186 21 100       34 %opts = (%opts, $builders->{wkr}->($info, $name, %opts)) if defined $opts{wkr};
187 21 100       38 %opts = (%opts, $builders->{wrt}->($info, $name, %opts)) if defined $opts{wrt};
188              
189 21 100 66     82 $name = "+$name" if delete $opts{mod} || delete $opts{modify};
190              
191 21         78 return ($name, %opts);
192             }
193              
194             1;
195              
196             =encoding utf8
197              
198             =head1 NAME
199              
200             Data::Object::Attributes
201              
202             =cut
203              
204             =head1 ABSTRACT
205              
206             Attribute Builder for Perl 5
207              
208             =cut
209              
210             =head1 SYNOPSIS
211              
212             package Example;
213              
214             use Moo;
215              
216             use Data::Object::Attributes;
217              
218             has 'data';
219              
220             package main;
221              
222             my $example = Example->new;
223              
224             =cut
225              
226             =head1 DESCRIPTION
227              
228             This package provides options for defining class attributes. Specifically, this
229             package wraps the C<has> attribute keyword and adds shortcuts and enhancements.
230             If no directives are specified, the attribute is declared as C<read-write> and
231             C<optional>.
232              
233             =cut
234              
235             =head1 SCENARIOS
236              
237             This package supports the following scenarios:
238              
239             =cut
240              
241             =head2 has
242              
243             package Example::Has;
244              
245             use Moo;
246              
247             has 'data' => (
248             is => 'ro',
249             isa => sub { die }
250             );
251              
252             package Example::HasData;
253              
254             use Moo;
255              
256             use Data::Object::Attributes;
257              
258             extends 'Example::Has';
259              
260             has '+data' => (
261             is => 'ro',
262             isa => sub { 1 }
263             );
264              
265             package main;
266              
267             my $example = Example::HasData->new(data => time);
268              
269             This package supports the C<has> keyword function and all of its
270             configurations. See the L<Moo> documentation for more details.
271              
272             =cut
273              
274             =head2 has-bld
275              
276             package Example::HasBld;
277              
278             use Moo;
279             use routines;
280              
281             use Data::Object::Attributes;
282              
283             has data => (
284             is => 'ro',
285             bld => 1
286             );
287              
288             method _build_data() {
289              
290             return time;
291             }
292              
293             package main;
294              
295             my $example = Example::HasBld->new;
296              
297             This package supports the C<bld> and C<builder> directives, expects a C<1>, a
298             method name, or coderef and builds the attribute value if it wasn't provided to
299             the constructor. See the L<Moo> documentation for more details.
300              
301             =cut
302              
303             =head2 has-clr
304              
305             package Example::HasClr;
306              
307             use Moo;
308              
309             use Data::Object::Attributes;
310              
311             has data => (
312             is => 'ro',
313             clr => 1
314             );
315              
316             package main;
317              
318             my $example = Example::HasClr->new(data => time);
319              
320             # $example->clear_data;
321              
322             This package supports the C<clr> and C<clearer> directives expects a C<1> or a
323             method name of the clearer method. See the L<Moo> documentation for more
324             details.
325              
326             =cut
327              
328             =head2 has-crc
329              
330             package Example::HasCrc;
331              
332             use Moo;
333              
334             use Data::Object::Attributes;
335              
336             has data => (
337             is => 'ro',
338             crc => sub {'0'}
339             );
340              
341             package main;
342              
343             my $example = Example::HasCrc->new(data => time);
344              
345             This package supports the C<crc> and C<coerce> directives denotes whether an
346             attribute's value should be automatically coerced. See the L<Moo> documentation
347             for more details.
348              
349             =cut
350              
351             =head2 has-def
352              
353             package Example::HasDef;
354              
355             use Moo;
356              
357             use Data::Object::Attributes;
358              
359             has data => (
360             is => 'ro',
361             def => '0'
362             );
363              
364             package main;
365              
366             my $example = Example::HasDef->new;
367              
368             This package supports the C<def> and C<default> directives expects a
369             non-reference or a coderef to be used to build a default value if one is not
370             provided to the constructor. See the L<Moo> documentation for more details.
371              
372             =cut
373              
374             =head2 has-hnd
375              
376             package Example::Time;
377              
378             use Moo;
379             use routines;
380              
381             method maketime() {
382              
383             return time;
384             }
385              
386             package Example::HasHnd;
387              
388             use Moo;
389              
390             use Data::Object::Attributes;
391              
392             has data => (
393             is => 'ro',
394             hnd => ['maketime']
395             );
396              
397             package main;
398              
399             my $example = Example::HasHnd->new(data => Example::Time->new);
400              
401             This package supports the C<hnd> and C<handles> directives denotes the methods
402             created on the object which dispatch to methods available on the attribute's
403             object. See the L<Moo> documentation for more details.
404              
405             =cut
406              
407             =head2 has-is
408              
409             package Example::HasIs;
410              
411             use Moo;
412              
413             use Data::Object::Attributes;
414              
415             has data => (
416             is => 'ro'
417             );
418              
419             package main;
420              
421             my $example = Example::HasIs->new(data => time);
422              
423             This package supports the C<is> directive, used to denote whether the attribute
424             is read-only or read-write. See the L<Moo> documentation for more details.
425              
426             =cut
427              
428             =head2 has-isa
429              
430             package Example::HasIsa;
431              
432             use Moo;
433             use registry;
434              
435             use Data::Object::Attributes;
436              
437             has data => (
438             is => 'ro',
439             isa => 'Str' # e.g. Types::Standard::Str
440             );
441              
442             package main;
443              
444             my $example = Example::HasIsa->new(data => time);
445              
446             This package supports the C<isa> directive, used to define the type constraint
447             to validate the attribute against. See the L<Moo> documentation for more
448             details.
449              
450             =cut
451              
452             =head2 has-lzy
453              
454             package Example::HasLzy;
455              
456             use Moo;
457              
458             use Data::Object::Attributes;
459              
460             has data => (
461             is => 'ro',
462             def => sub {time},
463             lzy => 1
464             );
465              
466             package main;
467              
468             my $example = Example::HasLzy->new;
469              
470             This package supports the C<lzy> and C<lazy> directives denotes whether the
471             attribute will be constructed on-demand, or on-construction. See the L<Moo>
472             documentation for more details.
473              
474             =cut
475              
476             =head2 has-mod
477              
478             package Example::HasNomod;
479              
480             use Moo;
481              
482             use Data::Object::Attributes;
483              
484             has data => (
485             is => 'rw',
486             opt => 1
487             );
488              
489             package Example::HasMod;
490              
491             use Moo;
492              
493             use Data::Object::Attributes;
494              
495             extends 'Example::HasNomod';
496              
497             has data => (
498             is => 'ro',
499             req => 1,
500             mod => 1
501             );
502              
503             package main;
504              
505             my $example = Example::HasMod->new;
506              
507             This package supports the C<mod> and C<modify> directives denotes whether a
508             pre-existing attribute's definition is being modified. This ability is not
509             supported by the L<Moo> object superclass.
510              
511             =cut
512              
513             =head2 has-new
514              
515             package Example::HasNew;
516              
517             use Moo;
518             use routines;
519              
520             use Data::Object::Attributes;
521              
522             has data => (
523             is => 'ro',
524             new => 1
525             );
526              
527             fun new_data($self) {
528              
529             return time;
530             }
531              
532             package main;
533              
534             my $example = Example::HasNew->new(data => time);
535              
536             This package supports the C<new> directive, if truthy, denotes that the
537             attribute will be constructed on-demand, i.e. is lazy, with a builder named
538             new_{attribute}. This ability is not supported by the L<Moo> object superclass.
539              
540             =cut
541              
542             =head2 has-opt
543              
544             package Example::HasOpt;
545              
546             use Moo;
547              
548             use Data::Object::Attributes;
549              
550             has data => (
551             is => 'ro',
552             opt => 1
553             );
554              
555             package main;
556              
557             my $example = Example::HasOpt->new(data => time);
558              
559             This package supports the C<opt> and C<optional> directives, used to denote if
560             an attribute is optional or required. See the L<Moo> documentation for more
561             details.
562              
563             =cut
564              
565             =head2 has-pre
566              
567             package Example::HasPre;
568              
569             use Moo;
570              
571             use Data::Object::Attributes;
572              
573             has data => (
574             is => 'ro',
575             pre => 1
576             );
577              
578             package main;
579              
580             my $example = Example::HasPre->new(data => time);
581              
582             This package supports the C<pre> and C<predicate> directives expects a C<1> or
583             a method name and generates a method for checking the existance of the
584             attribute. See the L<Moo> documentation for more details.
585              
586             =cut
587              
588             =head2 has-rdr
589              
590             package Example::HasRdr;
591              
592             use Moo;
593              
594             use Data::Object::Attributes;
595              
596             has data => (
597             is => 'ro',
598             rdr => 'get_data'
599             );
600              
601             package main;
602              
603             my $example = Example::HasRdr->new(data => time);
604              
605             This package supports the C<rdr> and C<reader> directives denotes the name of
606             the method to be used to "read" and return the attribute's value. See the
607             L<Moo> documentation for more details.
608              
609             =cut
610              
611             =head2 has-req
612              
613             package Example::HasReq;
614              
615             use Moo;
616              
617             use Data::Object::Attributes;
618              
619             has data => (
620             is => 'ro',
621             req => 1 # required
622             );
623              
624             package main;
625              
626             my $example = Example::HasReq->new(data => time);
627              
628             This package supports the C<req> and C<required> directives, used to denote if
629             an attribute is required or optional. See the L<Moo> documentation for more
630             details.
631              
632             =cut
633              
634             =head2 has-tgr
635              
636             package Example::HasTgr;
637              
638             use Moo;
639             use routines;
640              
641             use Data::Object::Attributes;
642              
643             has data => (
644             is => 'ro',
645             tgr => 1
646             );
647              
648             method _trigger_data() {
649             $self->{triggered} = 1;
650              
651             return $self;
652             }
653              
654             package main;
655              
656             my $example = Example::HasTgr->new(data => time);
657              
658             This package supports the C<tgr> and C<trigger> directives expects a C<1> or a
659             coderef and is executed whenever the attribute's value is changed. See the
660             L<Moo> documentation for more details.
661              
662             =cut
663              
664             =head2 has-use
665              
666             package Example::HasUse;
667              
668             use Moo;
669             use routines;
670              
671             use Data::Object::Attributes;
672              
673             has data => (
674             is => 'ro',
675             use => ['service', 'time']
676             );
677              
678             method service($type, @args) {
679             $self->{serviced} = 1;
680              
681             return time if $type eq 'time';
682             }
683              
684             package main;
685              
686             my $example = Example::HasUse->new;
687              
688             This package supports the C<use> directive denotes that the attribute will be
689             constructed on-demand, i.e. is lazy, using a custom builder meant to perform
690             service construction. This directive exists to provide a simple dependency
691             injection mechanism for class attributes. This ability is not supported by the
692             L<Moo> object superclass.
693              
694             =cut
695              
696             =head2 has-wkr
697              
698             package Example::HasWkr;
699              
700             use Moo;
701              
702             use Data::Object::Attributes;
703              
704             has data => (
705             is => 'ro',
706             wkr => 1
707             );
708              
709             package main;
710              
711             my $data = do {
712             my ($a, $b);
713              
714             $a = { time => time };
715             $b = { time => $a };
716              
717             $a->{time} = $b;
718             $a
719             };
720              
721             my $example = Example::HasWkr->new(data => $data);
722              
723             This package supports the C<wkr> and C<weak_ref> directives is used to denote if
724             the attribute's value should be weakened. See the L<Moo> documentation for more
725             details.
726              
727             =cut
728              
729             =head2 has-wrt
730              
731             package Example::HasWrt;
732              
733             use Moo;
734              
735             use Data::Object::Attributes;
736              
737             has data => (
738             is => 'ro',
739             wrt => 'set_data'
740             );
741              
742             package main;
743              
744             my $example = Example::HasWrt->new;
745              
746             This package supports the C<wrt> and C<writer> directives denotes the name of
747             the method to be used to "write" and return the attribute's value. See the
748             L<Moo> documentation for more details.
749              
750             =cut
751              
752             =head1 AUTHOR
753              
754             Al Newkirk, C<awncorp@cpan.org>
755              
756             =head1 LICENSE
757              
758             Copyright (C) 2011-2019, Al Newkirk, et al.
759              
760             This is free software; you can redistribute it and/or modify it under the terms
761             of the The Apache License, Version 2.0, as elucidated in the L<"license
762             file"|https://github.com/iamalnewkirk/data-object-attributes/blob/master/LICENSE>.
763              
764             =head1 PROJECT
765              
766             L<Wiki|https://github.com/iamalnewkirk/data-object-attributes/wiki>
767              
768             L<Project|https://github.com/iamalnewkirk/data-object-attributes>
769              
770             L<Initiatives|https://github.com/iamalnewkirk/data-object-attributes/projects>
771              
772             L<Milestones|https://github.com/iamalnewkirk/data-object-attributes/milestones>
773              
774             L<Contributing|https://github.com/iamalnewkirk/data-object-attributes/blob/master/CONTRIBUTE.md>
775              
776             L<Issues|https://github.com/iamalnewkirk/data-object-attributes/issues>
777              
778             =cut