File Coverage

blib/lib/MooX/ProtectedAttributes.pm
Criterion Covered Total %
statement 56 56 100.0
branch 15 18 83.3
condition 5 9 55.5
subroutine 9 9 100.0
pod n/a
total 85 92 92.3


line stmt bran cond sub pod time code
1             #
2             # This file is part of MooX-ProtectedAttributes
3             #
4             # This software is copyright (c) 2013 by celogeek <me@celogeek.com>.
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9             package MooX::ProtectedAttributes;
10              
11             # ABSTRACT: Create attribute only usable inside your package
12              
13 1     1   99464 use strict;
  1         3  
  1         24  
14 1     1   5 use warnings;
  1         3  
  1         30  
15             our $VERSION = '0.03'; # VERSION
16 1     1   5 use Carp;
  1         2  
  1         366  
17              
18             sub import {
19 16     16   512411 my $target = caller;
20              
21             return
22 16 50 33     212 if $target->can('protected_has')
23             && $target->can('protected_with_deprecated_has');
24              
25 16         61 my $around = $target->can('around');
26 16         52 my $has = $target->can('has');
27              
28             my $ensure_call_in_target = sub {
29 24     24   66 my ( $name, $deprecated_mode, $unless_method ) = @_;
30             return sub {
31 112         98731 my $orig = shift;
32 112         202 my $self = shift;
33 112         238 my @params = @_;
34              
35 112 100       665 return $self->$orig(@params) if @params; #write is permitted
36 88 100       222 if ( defined $unless_method ) {
37 24 100       67 return $self->$orig(@params) if $unless_method->();
38             }
39              
40 80         214 my $caller = caller(2);
41              
42 80 100       1343 return $self->$orig if $caller->DOES($target);
43              
44 24 100       59 if ($deprecated_mode) {
45 12         1177 carp
46             "DEPRECATED: You can't use the attribute <$name> outside the package <$target> or anyone that consume it!";
47 12         1184 return $self->$orig;
48             }
49             else {
50 12         1197 croak
51             "You can't use the attribute <$name> outside the package <$target> or anyone that consume it!";
52             }
53             }
54 16         77 };
  24         162  
55              
56             my $protected_has = sub {
57 12     12   15697 my ( $name, %attributes ) = @_;
58 12         35 my $unless_method = delete $attributes{'unless'};
59 12 50 66     71 croak "unless option should be a CODE REF"
60             if defined $unless_method && ref $unless_method ne 'CODE';
61 12         66 $has->( $name, %attributes );
62 12         13843 $around->(
63             $name, $ensure_call_in_target->( $name, 0, $unless_method )
64             );
65 16         88 };
66              
67             my $protected_with_deprecated_has = sub {
68 12     12   8147 my ( $name, %attributes ) = @_;
69 12         24 my $unless_method = delete $attributes{'unless'};
70 12 50 66     54 croak "unless option should be a CODE REF"
71             if defined $unless_method && ref $unless_method ne 'CODE';
72 12         52 $has->( $name, %attributes );
73 12         2520 $around->(
74             $name, $ensure_call_in_target->( $name, 1, $unless_method )
75             );
76 16         65 };
77              
78 16 100       63 if ( my $info = $Role::Tiny::INFO{$target} ) {
79 4         15 $info->{not_methods}{$protected_has} = $protected_has;
80 4         9 $info->{not_methods}{$protected_with_deprecated_has}
81             = $protected_with_deprecated_has;
82             }
83              
84 1     1   7 { no strict 'refs'; *{"${target}::protected_has"} = $protected_has }
  1         2  
  1         39  
  16         29  
  16         68  
85             {
86 1     1   5 no strict 'refs';
  1         2  
  1         84  
  16         31  
  16         26  
87 16         27 *{"${target}::protected_with_deprecated_has"}
  16         79  
88             = $protected_with_deprecated_has
89             }
90              
91 16         1910 return;
92             }
93              
94             1;
95              
96             __END__
97              
98             =pod
99              
100             =head1 NAME
101              
102             MooX::ProtectedAttributes - Create attribute only usable inside your package
103              
104             =head1 VERSION
105              
106             version 0.03
107              
108             =head1 SYNOPSIS
109              
110             You can use it in a role (Moo, Moose, Mo with a trick)
111              
112             package myRole;
113             use Moo::Role;
114             use MooX::ProtectedAttributes;
115              
116             protected_has 'foo' => (is => 'ro');
117              
118             sub display_foo { print shift->foo, "\n" }
119              
120             1;
121              
122             Or also directly in you class
123              
124             package myApp;
125             use Moo;
126             use MooX::ProtectedAttributes;
127              
128             protected_has 'bar' => (is => 'ro');
129              
130             sub display_bar { print shift->bar, "\n" }
131              
132             1;
133              
134             Then
135              
136             myApp->bar("123");
137             myApp->bar # croak
138             myApp->display_bar # 123
139              
140             =head1 DESCRIPTION
141              
142             It happend that you may want to create a class with private attributes that can't be used outside this package.
143              
144             For example, you want to create in lazy, a DB connection, but you want to handle it in your class in a specific way (with possible handle of errors ....).
145             You really want, even with the "_" before your attribute (which mean private), to avoid access of this attribute by any other packages.
146              
147             The goal of this package is to allow the init of the private attribute, but forbid reading from outside the package.
148              
149             With a protected attribute named "foo" for example, you can't do this outside the current package or any package that consume it :
150              
151             my $foo = $myObj->foo;
152              
153             or
154              
155             $myObj->foo->stuff();
156              
157             But this method is allowed inside the package where it has been declared.
158              
159             =head1 METHODS
160              
161             =head2 import
162              
163             The method provide 2 methods :
164              
165             =over
166              
167             =item protected_has
168              
169             Like a "has", disable read access outside the current class.
170              
171             =item protected_with_deprecated_has
172              
173             Instead of dying, it will display a DEPRECATED message and run as usual.
174             This allow you to alert user of the protected method to fix their program before you forbid the access to the attribute.
175              
176             =item unless attribute option
177              
178             You can use the "unless" => sub { $condition } option to your attribute.
179              
180             If the condition match, the attribute will not generate any warnings or die
181              
182             protect_has "foo" => (is => 'ro'), unless => sub { $ENV{SKIP_WARNING} };
183              
184             $myObj->foo # croak
185            
186             {
187             local $ENV{SKIP_WARNING} = 1;
188             $myObj->foo # works
189             }
190              
191             You can use it for your test, or may be to match some condition like 'OK if it is call from this package'
192              
193             =back
194              
195             =head1 BUGS
196              
197             Please report any bugs or feature requests on the bugtracker website
198             https://tasks.celogeek.com/projects/moox-protectedattributes
199              
200             When submitting a bug or request, please include a test-file or a
201             patch to an existing test-file that illustrates the bug or desired
202             feature.
203              
204             =head1 AUTHOR
205              
206             celogeek <me@celogeek.com>
207              
208             =head1 COPYRIGHT AND LICENSE
209              
210             This software is copyright (c) 2013 by celogeek <me@celogeek.com>.
211              
212             This is free software; you can redistribute it and/or modify it under
213             the same terms as the Perl 5 programming language system itself.
214              
215             =cut