File Coverage

lib/UR/Object/View/Aspect.pm
Criterion Covered Total %
statement 77 113 68.1
branch 31 62 50.0
condition 1 3 33.3
subroutine 7 8 87.5
pod 1 2 50.0
total 117 188 62.2


line stmt bran cond sub pod time code
1             package UR::Object::View::Aspect;
2              
3 3     3   114 use warnings;
  3         3  
  3         102  
4 3     3   12 use strict;
  3         3  
  3         1719  
5             require UR;
6              
7             our $VERSION = "0.46"; # UR $VERSION;;
8              
9             class UR::Object::View::Aspect {
10             id_by => [
11             parent_view => { is => 'UR::Object::View', id_by => 'parent_view_id',
12             doc => "the id of the view object this is an aspect-of" },
13            
14             number => { is => 'Integer',
15             doc => "aspects of a view are numbered" },
16             ],
17             has => [
18             name => { is => 'Text',
19             is_mutable => 0,
20             doc => 'the name of the property/method on the subject which returns the value to be viewed' },
21             ],
22             has_optional => [
23             label => { is => 'Text',
24             doc => 'display name for this aspect' },
25            
26             position => { is => 'Scalar',
27             doc => 'position of this aspect within the parent view (meaning is view and toolkit dependent)' },
28              
29             delegate_view => { is => 'UR::Object::View', id_by => 'delegate_view_id',
30             doc => "This aspect gets rendered via another view" },
31             ],
32             };
33              
34             sub create {
35 95     95 1 91 my $class = shift;
36 95         285 my($bx,%extra) = $class->define_boolexpr(@_);
37              
38             # TODO: it would be nice to have this in the class definition:
39             # increment_for => 'parent_view'
40 95 50       321 unless ($bx->value_for('number')) {
41 95 50       205 if (my $parent_view_id = $bx->value_for('parent_view_id')) {
42 95         271 my $parent_view = UR::Object::View->get($parent_view_id);
43 95         383 my @previous_aspects = $parent_view->aspects;
44 95         312 $bx = $bx->add_filter(number => scalar(@previous_aspects)+1);
45             }
46             }
47 95 100       197 unless ($bx->value_for('label')) {
48 89 50       202 if (my $label = $bx->value_for('name')) {
49 89         176 $label =~ s/_/ /g;
50 89         219 $bx = $bx->add_filter(label => $label);
51             }
52             }
53              
54 95 100       218 if (keys %extra) {
55             # This is a sub-view
56 5         9 my $delegate_subject_class_name;
57 5 100       16 if (exists $extra{'subject_class_name'}) {
58 3         5 $delegate_subject_class_name = $extra{'subject_class_name'};
59             } else {
60             # FIXME This duplicates functionality below in generate_delegate_view, but generate_delegate_view()
61             # doesn't take any args to tweak the properties of that delegated view :(
62             # Try to figure it out based on the name of the aspect...
63 2         3 my $parent_view;
64 2 50       6 if (my $view_id = $bx->value_for('parent_view_id')) {
    0          
65 2         11 $parent_view = UR::Object::View->get($view_id);
66             } elsif ($bx->specifies_value_for('parent_view')) {
67 0         0 $parent_view = $bx->value_for('parent_view');
68             }
69 2 50       7 unless ($parent_view) {
70 0         0 Carp::croak("Can't determine parent view from keys/values: ",join(', ', map { sprintf("%s => '%s'", $_, $extra{$_}) } keys %extra));
  0         0  
71             }
72              
73 2         18 my $class_meta = $parent_view->subject_class_name->__meta__;
74 2 50       5 unless ($class_meta) {
75 0         0 Carp::croak("No class metadata for class "
76             . $parent_view->subject_class_meta
77             . ". Can't create delegate view on aspect named "
78             . $bx->value_for('name') );
79             }
80              
81 2         6 my $property_meta = $class_meta->property_meta_for_name($bx->value_for('name'));
82 2 50       5 unless ($property_meta) {
83 0         0 Carp::croak("No property metadata for class " . $class_meta->class_name
84             . " property " . $bx->value_for('name')
85             . ". Can't create delegate view on aspect named " . $bx->value_for('name'));
86             }
87              
88 2 50       6 unless ($property_meta->data_type) {
89 0         0 Carp::croak("Property metadata for class " . $class_meta->class_name
90             . " property " . $property_meta->property_name
91             . " has no data_type. Can't create delegate view on aspect named " . $bx->value_for('name'));
92             }
93              
94 2         4 $delegate_subject_class_name = $property_meta->data_type;
95             }
96 5 50       33 unless ($delegate_subject_class_name) {
97 0         0 Carp::croak("Can't determine subject_class_name for delegate view on aspect named " . $bx->value_for('name'));
98             }
99            
100 5         20 my $delegate_view = $delegate_subject_class_name->create_view(
101             perspective => $bx->value_for('perspective'),
102             toolkit => $bx->value_for('toolkit'),
103             %extra
104             );
105 5 50       16 unless ($delegate_view) {
106 0         0 Carp::croak("Can't create delegate view for aspect named " . $bx->value_for('name') . ": ".$delegate_subject_class_name->error_message);
107             }
108             #$bx->add_filter(delegate_view_id => $delegate_view->id);
109 5         22 $bx = $bx->add_filter(delegate_view => $delegate_view);
110             }
111            
112              
113 95         324 my $self = $class->SUPER::create($bx);
114 95 50       190 return unless $self;
115              
116 95         199 my $name = $self->name;
117 95 50       165 unless ($name) {
118 0         0 $self->error_message("No name specified for aspect!");
119 0         0 $self->delete;
120 0         0 return;
121             }
122              
123 95         294 return $self;
124             }
125              
126             sub _look_for_recursion {
127 0     0   0 my $self = shift;
128              
129 0         0 my $parent_view = $self->parent_view;
130 0         0 my $subject = $parent_view->subject;
131              
132 0         0 $parent_view = $parent_view->parent_view;
133 0         0 while($parent_view) {
134 0 0       0 return 1 if ($parent_view->subject eq $subject);
135 0         0 $parent_view = $parent_view->parent_view;
136             }
137 0         0 return 0;
138             }
139              
140             sub generate_delegate_view {
141 3     3   14 no warnings;
  3         4  
  3         911  
142 38     38 0 47 my $self = shift;
143              
144 38         105 my $parent_view = $self->parent_view;
145              
146 38         98 my $name = $self->name;
147 38         87 my $subject_class_name = $parent_view->subject_class_name;
148              
149 38         48 my $retval;
150            
151 38         125 my $property_meta = $subject_class_name->__meta__->property($name);
152 38         53 my $aspect_type;
153 38 50       70 if ($property_meta) {
154 38         146 $aspect_type = $property_meta->_data_type_as_class_name;
155 38 50       106 unless ($aspect_type) {
156 0         0 Carp::confess("Undefined aspect type. Set 'is' for $name in class " . $property_meta->class_name);
157             }
158              
159 38 50       87 unless ($aspect_type) {
160 0 0       0 if (my $delegated_to_meta = $property_meta->final_property_meta) {
161 0         0 $aspect_type = $delegated_to_meta->data_type;
162             }
163             }
164              
165 38 50       85 unless ($aspect_type) {
166 0         0 Carp::confess("Property meta for class ".$property_meta->class_name." property ".$property_meta->property_name." has no data_type");
167             }
168              
169 38 50       159 unless ($aspect_type->can("__meta__")) {
170 0         0 Carp::croak("$aspect_type has no meta data? cannot generate a view for $subject_class_name $name!");
171             }
172             }
173             else {
174 0 0       0 unless ($subject_class_name->can($name)) {
175 0         0 $self->error_message("No property/method $name found on $subject_class_name! Invalid aspect!");
176 0         0 $self->delete;
177 0         0 Carp::croak($self->error_message);
178             }
179 0         0 $aspect_type = 'UR::Value::Text'
180             }
181              
182 38         313 my $aspect_meta = $aspect_type->__meta__;
183              
184 38         48 my $delegate_view;
185 38         44 eval {
186 38         143 $delegate_view = $aspect_type->create_view(
187             subject_class_name => $aspect_type,
188             perspective => $parent_view->perspective,
189             toolkit => $parent_view->toolkit,
190             parent_view => $parent_view,
191             aspects => [],
192             );
193             };
194              
195 38 50       120 unless ($delegate_view) {
196             # try again using the "default" perspective
197 0         0 my $err1 = $@;
198 0         0 eval {
199 0         0 $delegate_view = $aspect_type->create_view(
200             subject_class_name => $aspect_type,
201             perspective => 'default',
202             toolkit => $parent_view->toolkit,
203             parent_view => $parent_view,
204             aspects => [],
205             );
206             };
207 0         0 my $err2 = $@;
208              
209 0 0       0 unless ($delegate_view) {
210 0         0 $self->error_message(
211             "Error creating delegate view for $name ($aspect_type)! $err1\n"
212             . "Also failed to fall back to the default perspective for $name ($aspect_type)! $err2"
213             );
214 0         0 return;
215             }
216             }
217              
218 38         181 my @default_aspects_params = $delegate_view->_resolve_default_aspects();
219            
220             # add aspects which do not "go backward"
221             # no one wants to see an order, with a list of line items, which re-reprsent thier order on each
222 38         76 for my $aspect_params (@default_aspects_params) {
223 55 50       140 my $aspect_param_name = (ref($aspect_params) ? $aspect_params->{name} : $aspect_params);
224 55         314 my $aspect_property_meta = $aspect_meta->property($aspect_param_name);
225 3     3   16 no strict; no warnings;
  3     3   4  
  3         66  
  3         12  
  3         4  
  3         342  
226 55 50 33     226 next if (!$aspect_property_meta or !$property_meta);
227 55 100       163 if ($aspect_property_meta->reverse_as() eq $name) {
    100          
228            
229             }
230             elsif ($property_meta->reverse_as eq $aspect_param_name) {
231             }
232             else {
233 53 50       247 $delegate_view->add_aspect(ref($aspect_params) ? %$aspect_params : $aspect_params);
234             }
235             }
236 38         151 $self->delegate_view($delegate_view);
237 38         60 $retval = $delegate_view;
238              
239 38         144 return $retval;
240             }
241              
242             1;
243              
244             =pod
245              
246             =head1 NAME
247              
248             UR::Object::View::Aspect - a specification for one aspect of a view
249              
250             =head1 SYNOPSIS
251              
252             my $v = $o->create_view(
253             perspective => 'default',
254             toolkit => 'xml',
255             aspects => [
256             'id',
257             'name',
258             'title',
259             {
260             name => 'department',
261             perspective => 'logo'
262             },
263             {
264             name => 'boss',
265             label => 'Supervisor',
266             aspects => [
267             'name',
268             'title',
269             {
270             name => 'subordinates',
271             perspective => 'graph by title'
272             }
273             ]
274             }
275             ]
276             );
277              
278             =cut
279              
280