File Coverage

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