File Coverage

blib/lib/Config/Apple/Profile/Payload/Tie/Root.pm
Criterion Covered Total %
statement 56 73 76.7
branch 12 18 66.6
condition 3 3 100.0
subroutine 14 19 73.6
pod n/a
total 85 113 75.2


line stmt bran cond sub pod time code
1             # This is the code for Config::Apple::Profile::Payload::Tie::Root.
2             # For Copyright, please see the bottom of the file.
3              
4             package Config::Apple::Profile::Payload::Tie::Root;
5              
6 15     15   118 use 5.10.1;
  15         53  
  15         578  
7 15     15   63 use strict;
  15         16  
  15         381  
8 15     15   57 use warnings FATAL => 'all';
  15         18  
  15         668  
9              
10             our $VERSION = '0.87';
11              
12 15     15   54 use Tie::Hash; # Also gives us Tie::StdHash
  15         19  
  15         229  
13 15     15   5810 use Config::Apple::Profile::Payload::Tie::Array;
  15         27  
  15         391  
14 15     15   5835 use Config::Apple::Profile::Payload::Tie::Dict;
  15         26  
  15         445  
15 15     15   5781 use Config::Apple::Profile::Payload::Types qw(:all);
  15         32  
  15         3193  
16 15     15   6431 use Config::Apple::Profile::Payload::Types::Validation qw(:types);
  15         35  
  15         2514  
17 15     15   92 use Scalar::Util qw(blessed);
  15         25  
  15         8979  
18              
19              
20             =encoding utf8
21              
22             =head1 NAME
23              
24             Config::Apple::Profile::Payload::Tie::Root - Tying class for payload storage.
25              
26             =head1 DESCRIPTION
27              
28             This class is used to store the payload keys, and their values, for each of the
29             payload classes under C.
30              
31             In the configuration profile XML, each payload is represented by a series of
32             keys and their values. This matches up fairly well with a Perl hash, so that
33             is the mechanism that was chosen for actually getting (and messing with) the
34             data in a payload class!
35              
36             This class is used directly only by L,
37             and acts as storage for the payload keys. Subclasses are involved indirectly,
38             by providing their own list of payload keys, either replacing or supplementing
39             the list from C.
40              
41             =cut
42              
43             =head2 "CLASS" METHODS
44              
45             =head3 tie %hash, 'Config::Apple::Profile::Payload::Tie::Root', $self
46              
47             This method is not useful in client code, but it is documented for future
48             developers of this software.
49              
50             When this class is used to tie a hash, C will be called, with the class
51             name as the first argument. The second argument is expected to be a reference
52             to the object that will be containing this tied hash. The containing object
53             needs to implement two methods:
54              
55             =over 4
56              
57             =item _validate($key, $value)
58              
59             C<_validate> needs to return, if the value is valid, the de-tainted value. If
60             the value is not valid, then C must be returned.
61              
62             =item keys()
63              
64             C needs to return a reference to the hash of payload keys, as defined in
65             L. No attempts will be made to modify
66             the hash, so it can (and should) be read-only.
67              
68             =back
69              
70             Since the second argument is a reference pointing back to the object which
71             contains us, we are introducing a circular reference. We take responsibility
72             for "weakening" the reference provided to us.
73              
74             =cut
75              
76             sub TIEHASH {
77 240     240   338 my ($class, $object_ref) = @_;
78            
79             # $object_ref points to our containing object. In other words, $object_ref,
80             # if de-referenced, would give us our instance of this class.
81             # Using $object_ref around like this does, I believe, create a circular
82             # reference, which we need to break.
83 240         698 Scalar::Util::weaken($object_ref);
84            
85             # Construct our object. We need a hash for the payload, and we'll also
86             # bring along the reference to our containing instance.
87             # Our class name is made-up, to keep clients from doing weird stuff.
88 240         1280 return bless {
89             payload => {},
90             object => $object_ref,
91             }, "$class";
92             }
93              
94              
95             =head3 FETCH
96              
97             Works as one would expect with a Perl hash. Either the value is returned, or
98             C is returned. Exactly I you get depends on the payload class and
99             the key you are accessing. For more details, check the payload class
100             documentation, as well as L.
101              
102             =cut
103              
104             sub FETCH {
105 914     914   196356 my ($self, $key) = @_;
106            
107             # Grab the information on our requested key
108 914 50       3891 if (!exists $self->{object}->keys()->{$key}) {
109 0         0 die "Payload key $key not defined in class "
110             . blessed($self->{object}) . "\n";
111             }
112 914         6524 my $key_info = $self->{object}->keys()->{$key};
113            
114             # If the payload key has a fixed value, return that
115 914 50       4928 if (exists $key_info->{value}) {
116 0         0 return $key_info->{value};
117             }
118            
119             # Our EXISTS check returns true if the key is a valid payload key name.
120             # Therefore, we need to do our own exists check, and possible return undef.
121 914 100       5800 if (exists $self->{payload}->{$key}) {
122 737         2478 return $self->{payload}->{$key};
123             }
124            
125             # At this point, our key doesn't exist right now, but we need to check for
126             # some complex types.
127 177         406 my $type = $key_info->{type};
128            
129             # If the key is an array or a dict, get the validator and make the tie
130 177 100 100     1029 if ( ($type == $ProfileArray)
    50          
131             || ($type == $ProfileDict)
132             ) {
133             # Set up the appropriate validator, based on array/dict content type
134             my $validator_ref = sub {
135 380     380   411 my ($value) = @_;
136 380         1904 return $self->{object}->validate_key($key, $value);
137 163         1409 };
138            
139             # If the key is an array, set up a new Array tie
140 163 100       369 if ($type == $ProfileArray) {
    50          
141 141         1155 tie my @array, 'Config::Apple::Profile::Payload::Tie::Array',
142             $validator_ref;
143 141         332 $self->{payload}->{$key} = \@array;
144 141         756 return $self->{payload}->{$key};
145             }
146            
147             # If the key is a dictionary, set up a new Hash tie
148             elsif ($type == $ProfileDict) {
149 22         251 tie my %hash, 'Config::Apple::Profile::Payload::Tie::Dict',
150             $validator_ref;
151 22         52 $self->{payload}->{$key} = \%hash;
152 22         141 return $self->{payload}->{$key};
153             }
154             }
155            
156             # If the key is a class, instantiate it, add it to the payload, and return
157             elsif ($type == $ProfileClass) {
158 0         0 my $object = $self->{object}->construct($key);
159 0         0 $self->{payload}->{$key} = $object;
160 0         0 return $object;
161             }
162            
163             # The catch-all: The key doesn't exist, and isn't special, so return undef.
164             else {
165             ## no critic (ProhibitExplicitReturnUndef)
166 14         245 return undef;
167             ## use critic
168             }
169             }
170              
171              
172             =head3 STORE
173              
174             Works I as one would expect with a Perl hash. When setting a value to
175             a key, two checks are performed:
176              
177             =over 4
178              
179             =item *
180              
181             The key must be a valid payload key name for this payload class.
182              
183             =item *
184              
185             The value must be a valid value for the given payload key.
186              
187             =back
188              
189             Exactly what validation is performed depends first on the type of value (be it
190             a string, a boolean, data, etc.), and next on any special validation performed
191             by the payload class itself. For more details, check the payload class
192             documentation, as well as L.
193              
194             If the validation fails, the program dies.
195              
196             =cut
197              
198             sub STORE {
199 441     441   103882 my ($self, $key, $value) = @_;
200            
201             # Check if the proposed value is valid, and store if it is.
202             # (Validating also de-taints the value, if it's valid)
203 441         2097 $value = $self->{object}->validate_key($key, $value);
204 433 100       1822 if (defined($value)) {
205 281         1909 $self->{payload}->{$key} = $value;
206             }
207             else {
208 152         1466 die('Invalid value for key');
209             }
210             }
211              
212              
213             =head3 delete
214              
215             Deleting a key works as one would expect with a Perl hash. Once deleted,
216             unless a new value is set, attempts to access the key will return C.
217              
218             =cut
219              
220             sub DELETE {
221 6     6   594 my ($self, $key) = @_;
222 6         23 delete $self->{payload}->{$key};
223             }
224              
225              
226             =head3 clear
227              
228             Clearing a hash works as one would expect with a Perl hash. Unless new values
229             are set, attempts to access keys will return C.
230              
231             =cut
232              
233             sub CLEAR {
234 0     0     my ($self) = @_;
235             # The CLEAR method implemented in Tie::Hash uses calls to $self
236             # (specifically, calls to FIRSTKEY, NEXTKEY, and DELETE), so let's just
237             # call that code instead of reimplementing it!
238 0           Tie::Hash::CLEAR($self);
239             }
240              
241              
242             =head3 exists
243              
244             The operation of C is a little different from what is normally expected.
245              
246             C will return true iff the key provided is a valid payload key for this
247             payload class.
248              
249             To check if a payload key actually has a value, use C. Of course, you
250             should continue to use C if you do not know if a payload has a
251             particular key.
252              
253             =cut
254              
255             sub EXISTS {
256 0     0     my ($self, $key) = @_;
257 0 0         return 1 if exists($self->{object}->keys()->{$key});
258 0           return 0;
259             }
260              
261              
262             =head3 keys
263              
264             C returns a list of keys I.
265              
266             To get the a list of all keys that exist for this payload class, don't look
267             at the payload. Instead, use C on the hash returned by C.
268              
269             =head2 each
270              
271             C returns the key/value pairs I.
272              
273             =cut
274              
275             sub FIRSTKEY {
276 0     0     my ($self) = @_;
277             # We can use the code from Tie::StdHash::FIRSTKEY, instead of rewriting it.
278 0           return Tie::StdHash::FIRSTKEY($self->{payload});
279             }
280              
281              
282             sub NEXTKEY {
283 0     0     my ($self, $previous) = @_;
284             # We can use the code from Tie::StdHash::NEXTKEY, instead of rewriting it.
285 0           return Tie::StdHash::NEXTKEY($self->{payload});
286             }
287              
288              
289             =head3 scalar
290              
291             C returns the number of payload keys that have values set.
292              
293             To get the total number of keys that exist for this payload class, don't look
294             at the payload. Instead, use C on the hash returned by C.
295              
296             =cut
297              
298             sub SCALAR {
299 0     0     my ($self) = @_;
300 0           return scalar %{$self->{payload}};
  0            
301             }
302              
303              
304             =head1 ACKNOWLEDGEMENTS
305              
306             Refer to the L for acknowledgements.
307              
308             =head1 AUTHOR
309              
310             A. Karl Kornel, C<< >>
311              
312             =head1 COPYRIGHT AND LICENSE
313              
314             Copyright © 2014 A. Karl Kornel.
315              
316             This program is free software; you can redistribute it and/or modify it
317             under the terms of either: the GNU General Public License as published
318             by the Free Software Foundation; or the Artistic License.
319              
320             See L for more information.
321              
322             =cut
323              
324             1;