File Coverage

blib/lib/Rosetta/Model.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             #!perl
2 1     1   4535 use 5.008001;
  1         4  
  1         45  
3 1     1   6 use utf8;
  1         4  
  1         7  
4 1     1   22 use strict;
  1         2  
  1         50  
5 1     1   6 use warnings;
  1         1  
  1         54  
6              
7             # External packages used by packages in this file, that don't export symbols:
8 1     1   548 use only 'Parse::RecDescent' => '1.94-'; # TODO: make load of this optional
  0            
  0            
9             use only 'Locale::KeyedText' => '1.72.0-';
10              
11             ###########################################################################
12             ###########################################################################
13              
14             # Constant values used by packages in this file:
15             use only 'Readonly' => '1.03-';
16             Readonly my $EMPTY_STR => q{};
17              
18             ###########################################################################
19             ###########################################################################
20              
21             { package Rosetta::Model; # package
22             use version; our $VERSION = qv('0.724.0');
23             # Note: This given version applies to all of this file's packages.
24             } # package Rosetta::Model
25              
26             ###########################################################################
27             ###########################################################################
28              
29             { package Rosetta::Model::Document; # class
30              
31             # External packages used by the Rosetta::Model::Document class, that do export symbols:
32             use only 'Class::Std' => '0.0.8-';
33             use only 'Class::Std::Utils' => '0.0.2-';
34              
35             # Attributes of every Rosetta::Model::Document object:
36             my %all_nodes_of :ATTR;
37             # Array of Rosetta::Model::Node
38             # The set of all Nodes that this Document contains.
39             my %root_nodes_of :ATTR;
40             # Array of Rosetta::Model::Node
41             # List of all Nodes that have no parent Nodes; a sub-set of all_nodes.
42              
43             # Entrust the Rosetta::Model::Node class to see our private attributes.
44             sub _entrust_attrs_to_node_class {
45             # TODO: Add gate code to die if caller not the Node class.
46             return (\%all_nodes_of, \%root_nodes_of);
47             }
48              
49             ###########################################################################
50              
51             sub BUILD {
52             my ($self, $ident, $arg_ref) = @_;
53             my $root_nodes_ref
54             = exists $arg_ref->{'root_nodes'} ? $arg_ref->{'root_nodes'} : [];
55              
56             $self->_assert_arg_rt_nd_aoh(
57             'new', ':@root_nodes?', $root_nodes_ref );
58              
59             $all_nodes_of{$ident} = [];
60             $root_nodes_of{$ident} = [];
61             for my $root_node (@{$root_nodes_ref}) {
62             Rosetta::Model::Node->new({ 'document' => $self, %{$root_node} });
63             }
64              
65             return;
66             }
67              
68             ###########################################################################
69              
70             sub export_as_hash {
71             my ($self) = @_;
72             return {
73             'root_nodes'
74             => [map { $_->export_as_hash() }
75             @{$root_nodes_of{ident $self}}],
76             };
77             }
78              
79             ###########################################################################
80              
81             sub _die_with_msg : PRIVATE {
82             my ($self, $msg_key, $msg_vars_ref) = @_;
83             $msg_vars_ref ||= {};
84             $msg_vars_ref->{'CLASS'} = 'Rosetta::Model::Document';
85             die Locale::KeyedText::Message->new({
86             'msg_key' => $msg_key, 'msg_vars' => $msg_vars_ref });
87             }
88              
89             sub _assert_arg_rt_nd_aoh : PRIVATE {
90             my ($self, $meth, $arg, $val) = @_;
91             $self->_die_with_msg( 'LKT_ARG_UNDEF',
92             { 'METH' => $meth, 'ARG' => $arg } )
93             if !defined $val;
94             $self->_die_with_msg( 'LKT_ARG_NO_ARY',
95             { 'METH' => $meth, 'ARG' => $arg, 'VAL' => $val } )
96             if ref $val ne 'ARRAY';
97             for my $val_elem (@{$val}) {
98             $self->_die_with_msg( 'LKT_ARG_ARY_ELEM_UNDEF',
99             { 'METH' => $meth, 'ARG' => $arg } )
100             if !defined $val_elem;
101             $self->_die_with_msg( 'LKT_ARG_ARY_ELEM_NO_HASH',
102             { 'METH' => $meth, 'ARG' => $arg, 'VAL' => $val_elem } )
103             if ref $val_elem ne 'HASH';
104             for my $k ('document', 'parent_node') {
105             $self->_die_with_msg(
106             'ROS_M_D_ARG_AOH_TO_CONSTR_CH_ND_HAS_KEY_CONFL',
107             { 'METH' => $meth, 'ARG' => $arg, 'KEY' => $k } )
108             if exists $val_elem->{$k};
109             }
110             }
111             }
112              
113             ###########################################################################
114              
115             } # class Rosetta::Model::Document
116              
117             ###########################################################################
118             ###########################################################################
119              
120             { package Rosetta::Model::Node; # class
121              
122             # External packages used by the Rosetta::Model::Node class, that do export symbols:
123             use only 'Class::Std' => '0.0.8-';
124             use only 'Class::Std::Utils' => '0.0.2-';
125             use Scalar::Util qw( blessed );
126              
127             # Attributes of every Rosetta::Model::Node object:
128             my %document_of :ATTR;
129             # Rosetta::Model::Document
130             # The Document that this Node lives in.
131             my %parent_node_of :ATTR;
132             # Rosetta::Model::Node
133             # The parent Node of this Node, if there is one.
134             my %node_type_of :ATTR;
135             # Str
136             # What type of Node this is.
137             my %attributes_of :ATTR;
138             # Hash(Str) of Any
139             # Named attribute values that this Node has, if any.
140             my %child_nodes_of :ATTR;
141             # Array of Rosetta::Model::Node
142             # List of this Node's child Nodes, if there are any.
143              
144             # Aliases of attributes of Document objects we are trusted to see:
145             my ($all_nodes_of_document, $root_nodes_of_document)
146             = Rosetta::Model::Document->_entrust_attrs_to_node_class();
147              
148             ###########################################################################
149              
150             sub BUILD {
151             my ($self, $ident, $arg_ref) = @_;
152             my $document = $arg_ref->{'document'};
153             my $parent_node = $arg_ref->{'parent_node'}; # or defaults to undef
154             my $node_type = $arg_ref->{'node_type'};
155             my $attributes_ref
156             = exists $arg_ref->{'attributes'} ? $arg_ref->{'attributes'} : {};
157             my $child_nodes_ref
158             = exists $arg_ref->{'child_nodes'}
159             ? $arg_ref->{'child_nodes'} : [];
160              
161             $self->_assert_arg_doc( 'new', ':$document!', $document );
162             if (defined $parent_node) {
163             $self->_assert_arg_node_assume_def(
164             'new', ':$parent_node?', $parent_node );
165             }
166             $self->_assert_arg_str( 'new', ':$node_type!', $node_type );
167             $self->_assert_arg_hash( 'new', ':%attributes?', $attributes_ref );
168             $self->_assert_arg_ch_nd_aoh(
169             'new', ':@child_nodes?', $child_nodes_ref );
170              
171             $document_of{$ident} = $document;
172             push @{$all_nodes_of_document->{ident $document}}, $self;
173             if (defined $parent_node) {
174             $parent_node_of{$ident} = $parent_node;
175             push @{$child_nodes_of{ident $parent_node}}, $self;
176             }
177             else {
178             push @{$root_nodes_of_document->{ident $document}}, $self;
179             }
180             $node_type_of{$ident} = $node_type;
181             $attributes_of{$ident} = {%{$attributes_ref}};
182             $child_nodes_of{$ident} = [];
183             for my $child_node (@{$child_nodes_ref}) {
184             (ref $self)->new({
185             'document' => $document,
186             'parent_node' => $self,
187             %{$child_node},
188             });
189             }
190              
191             return;
192             }
193              
194             ###########################################################################
195              
196             sub export_as_hash {
197             my ($self) = @_;
198             my $ident = ident $self;
199             return {
200             'node_type' => $node_type_of{$ident},
201             'attributes' => {%{$attributes_of{$ident}}},
202             'child_nodes'
203             => [map { $_->export_as_hash() }
204             @{$child_nodes_of{$ident}}],
205             };
206             }
207              
208             ###########################################################################
209              
210             sub _die_with_msg : PRIVATE {
211             my ($self, $msg_key, $msg_vars_ref) = @_;
212             $msg_vars_ref ||= {};
213             $msg_vars_ref->{'CLASS'} = 'Rosetta::Model::Node';
214             die Locale::KeyedText::Message->new({
215             'msg_key' => $msg_key, 'msg_vars' => $msg_vars_ref });
216             }
217              
218             sub _assert_arg_str : PRIVATE {
219             my ($self, $meth, $arg, $val) = @_;
220             $self->_die_with_msg( 'LKT_ARG_UNDEF',
221             { 'METH' => $meth, 'ARG' => $arg } )
222             if !defined $val;
223             $self->_die_with_msg( 'LKT_ARG_EMP_STR',
224             { 'METH' => $meth, 'ARG' => $arg } )
225             if $val eq $EMPTY_STR;
226             }
227              
228             sub _assert_arg_hash : PRIVATE {
229             my ($self, $meth, $arg, $val) = @_;
230             $self->_die_with_msg( 'LKT_ARG_UNDEF',
231             { 'METH' => $meth, 'ARG' => $arg } )
232             if !defined $val;
233             $self->_die_with_msg( 'LKT_ARG_NO_HASH',
234             { 'METH' => $meth, 'ARG' => $arg, 'VAL' => $val } )
235             if ref $val ne 'HASH';
236             $self->_die_with_msg( 'LKT_ARG_HASH_KEY_EMP_STR',
237             { 'METH' => $meth, 'ARG' => $arg } )
238             if exists $val->{$EMPTY_STR};
239             }
240              
241             sub _assert_arg_doc : PRIVATE {
242             my ($self, $meth, $arg, $val) = @_;
243             $self->_die_with_msg( 'LKT_ARG_UNDEF',
244             { 'METH' => $meth, 'ARG' => $arg } )
245             if !defined $val;
246             $self->_die_with_msg( 'LKT_ARG_NO_EXP_TYPE', { 'METH' => $meth,
247             'ARG' => $arg, 'EXP_TYPE' => 'Rosetta::Model::Document',
248             'VAL' => $val } )
249             if !blessed $val or !$val->isa( 'Rosetta::Model::Document' );
250             }
251              
252             sub _assert_arg_node_assume_def : PRIVATE {
253             my ($self, $meth, $arg, $val) = @_;
254             $self->_die_with_msg( 'LKT_ARG_NO_EXP_TYPE', { 'METH' => $meth,
255             'ARG' => $arg, 'EXP_TYPE' => 'Rosetta::Model::Node',
256             'VAL' => $val } )
257             if !blessed $val or !$val->isa( 'Rosetta::Model::Node' );
258             }
259              
260             sub _assert_arg_ch_nd_aoh : PRIVATE {
261             my ($self, $meth, $arg, $val) = @_;
262             $self->_die_with_msg( 'LKT_ARG_UNDEF',
263             { 'METH' => $meth, 'ARG' => $arg } )
264             if !defined $val;
265             $self->_die_with_msg( 'LKT_ARG_NO_ARY',
266             { 'METH' => $meth, 'ARG' => $arg, 'VAL' => $val } )
267             if ref $val ne 'ARRAY';
268             for my $val_elem (@{$val}) {
269             $self->_die_with_msg( 'LKT_ARG_ARY_ELEM_UNDEF',
270             { 'METH' => $meth, 'ARG' => $arg } )
271             if !defined $val_elem;
272             $self->_die_with_msg( 'LKT_ARG_ARY_ELEM_NO_HASH',
273             { 'METH' => $meth, 'ARG' => $arg, 'VAL' => $val_elem } )
274             if ref $val_elem ne 'HASH';
275             for my $k ('document', 'parent_node') {
276             $self->_die_with_msg(
277             'ROS_M_N_ARG_AOH_TO_CONSTR_CH_ND_HAS_KEY_CONFL',
278             { 'METH' => $meth, 'ARG' => $arg, 'KEY' => $k } )
279             if exists $val_elem->{$k};
280             }
281             }
282             }
283              
284             ###########################################################################
285              
286             } # class Rosetta::Model::Node
287              
288             ###########################################################################
289             ###########################################################################
290              
291             1; # Magic true value required at end of a reuseable file's code.
292             __END__