| 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 |  |  |  |  |  |  |  |