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   131 use 5.10.1;
  15         62  
  15         585  
7 15     15   69 use strict;
  15         22  
  15         459  
8 15     15   61 use warnings FATAL => 'all';
  15         17  
  15         691  
9              
10             our $VERSION = '0.87.1';
11              
12 15     15   63 use Tie::Hash; # Also gives us Tie::StdHash
  15         18  
  15         248  
13 15     15   6441 use Config::Apple::Profile::Payload::Tie::Array;
  15         31  
  15         413  
14 15     15   5936 use Config::Apple::Profile::Payload::Tie::Dict;
  15         29  
  15         541  
15 15     15   5751 use Config::Apple::Profile::Payload::Types qw(:all);
  15         30  
  15         3243  
16 15     15   6826 use Config::Apple::Profile::Payload::Types::Validation qw(:types);
  15         57  
  15         2609  
17 15     15   92 use Scalar::Util qw(blessed);
  15         23  
  15         9229  
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   341 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         803 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         1389 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   180042 my ($self, $key) = @_;
106            
107             # Grab the information on our requested key
108 914 50       3827 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         6734 my $key_info = $self->{object}->keys()->{$key};
113            
114             # If the payload key has a fixed value, return that
115 914 50       5109 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       5746 if (exists $self->{payload}->{$key}) {
122 737         2503 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         446 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     1060 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   463 my ($value) = @_;
136 380         1913 return $self->{object}->validate_key($key, $value);
137 163         1444 };
138            
139             # If the key is an array, set up a new Array tie
140 163 100       365 if ($type == $ProfileArray) {
    50          
141 141         1361 tie my @array, 'Config::Apple::Profile::Payload::Tie::Array',
142             $validator_ref;
143 141         373 $self->{payload}->{$key} = \@array;
144 141         721 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         246 tie my %hash, 'Config::Apple::Profile::Payload::Tie::Dict',
150             $validator_ref;
151 22         41 $self->{payload}->{$key} = \%hash;
152 22         139 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         242 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   99187 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         2048 $value = $self->{object}->validate_key($key, $value);
204 433 100       1788 if (defined($value)) {
205 281         2093 $self->{payload}->{$key} = $value;
206             }
207             else {
208 152         1470 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   793 my ($self, $key) = @_;
222 6         25 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;