| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 1 |  |  | 1 |  | 1200 | use 5.008; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 45 |  | 
| 2 | 1 |  |  | 1 |  | 6 | use strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 40 |  | 
| 3 | 1 |  |  | 1 |  | 5 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 68 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | package Class::Value; | 
| 6 |  |  |  |  |  |  | our $VERSION = '1.100840'; | 
| 7 |  |  |  |  |  |  | # ABSTRACT: Implements the Value Object Design Pattern | 
| 8 | 1 |  |  | 1 |  | 1290 | use Error ':try'; | 
|  | 1 |  |  |  |  | 17120 |  | 
|  | 1 |  |  |  |  | 7 |  | 
| 9 | 1 |  |  | 1 |  | 844 | use Class::Value::DefaultNotify; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 15 |  | 
| 10 | 1 |  |  | 1 |  | 925 | use Error::Hierarchy::Container; | 
|  | 1 |  |  |  |  | 22456 |  | 
|  | 1 |  |  |  |  | 16 |  | 
| 11 | 1 |  |  | 1 |  | 51 | use parent qw(Class::Accessor::Complex Class::Accessor::Constructor); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 6 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | # Use strings for overloading sub names so they're interpreted as method names | 
| 14 |  |  |  |  |  |  | # and the methods can be overridden in subclasses while continuing to work as | 
| 15 |  |  |  |  |  |  | # expected. The overloaded operations have been chosen so that as much as | 
| 16 |  |  |  |  |  |  | # necessary can be autogenerated (see "perldoc overload": MAGIC | 
| 17 |  |  |  |  |  |  | # AUTOGENERATION). Subclasses are free to provide custom overloads of | 
| 18 |  |  |  |  |  |  | # autogenerated methods, of course. | 
| 19 |  |  |  |  |  |  | use overload | 
| 20 | 1 |  |  |  |  | 9 | '+'     => 'add', | 
| 21 |  |  |  |  |  |  | '-'     => 'subtract', | 
| 22 |  |  |  |  |  |  | '*'     => 'multiply', | 
| 23 |  |  |  |  |  |  | '/'     => 'divide', | 
| 24 |  |  |  |  |  |  | '**'    => 'power', | 
| 25 |  |  |  |  |  |  | '%'     => 'modulo', | 
| 26 |  |  |  |  |  |  | cmp     => 'str_cmp', | 
| 27 |  |  |  |  |  |  | '<=>'   => 'num_cmp', | 
| 28 |  |  |  |  |  |  | '<<'    => 'bit_shift_left', | 
| 29 |  |  |  |  |  |  | '>>'    => 'bit_shift_right', | 
| 30 |  |  |  |  |  |  | '&'     => 'bit_and', | 
| 31 |  |  |  |  |  |  | '|'     => 'bit_or', | 
| 32 |  |  |  |  |  |  | '^'     => 'bit_xor', | 
| 33 |  |  |  |  |  |  | '~'     => 'bit_not', | 
| 34 |  |  |  |  |  |  | 'atan2' => 'atan2', | 
| 35 |  |  |  |  |  |  | 'cos'   => 'cos', | 
| 36 |  |  |  |  |  |  | 'sin'   => 'sin', | 
| 37 |  |  |  |  |  |  | 'exp'   => 'exp', | 
| 38 |  |  |  |  |  |  | 'log'   => 'log', | 
| 39 |  |  |  |  |  |  | 'sqrt'  => 'sqrt', | 
| 40 |  |  |  |  |  |  | 'int'   => 'int', | 
| 41 |  |  |  |  |  |  | '""'    => 'stringify', | 
| 42 | 1 |  |  | 1 |  | 2851 | '<>'    => 'iterate'; | 
|  | 1 |  |  |  |  | 2 |  | 
| 43 |  |  |  |  |  |  | __PACKAGE__ | 
| 44 |  |  |  |  |  |  | ->mk_constructor | 
| 45 |  |  |  |  |  |  | ->mk_scalar_accessors(qw(notify_delegate)) | 
| 46 |  |  |  |  |  |  | ->mk_object_accessors( | 
| 47 |  |  |  |  |  |  | 'Error::Hierarchy::Container' => 'exception_container' | 
| 48 |  |  |  |  |  |  | ); | 
| 49 | 1 |  |  | 1 |  | 387 | use constant UNHYGIENIC => (qw/value/); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 278 |  | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | # our(), not a static boolean method, so it can be local()'ed. | 
| 52 |  |  |  |  |  |  | our $SkipChecks           = 1; | 
| 53 |  |  |  |  |  |  | our $SkipNormalizations   = 0; | 
| 54 |  |  |  |  |  |  | our $ThrowSingleException = 0; | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | sub skip_checks { | 
| 57 | 0 |  |  | 0 | 1 | 0 | our $SkipChecks; | 
| 58 | 0 | 0 |  |  |  | 0 | return $SkipChecks if @_ == 1; | 
| 59 | 0 |  |  |  |  | 0 | $SkipChecks = $_[1]; | 
| 60 |  |  |  |  |  |  | } | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | sub skip_normalizations { | 
| 63 | 0 |  |  | 0 | 1 | 0 | our $SkipNormalizations; | 
| 64 | 0 | 0 |  |  |  | 0 | return $SkipNormalizations if @_ == 1; | 
| 65 | 0 |  |  |  |  | 0 | $SkipNormalizations = $_[1]; | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | sub throw_single_exception { | 
| 69 | 0 |  |  | 0 | 1 | 0 | our $ThrowSingleException; | 
| 70 | 0 | 0 |  |  |  | 0 | return $ThrowSingleException if @_ == 1; | 
| 71 | 0 |  |  |  |  | 0 | $ThrowSingleException = $_[1]; | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | # Every value object gets the same notify delegate object | 
| 75 | 1 |  | 33 |  |  | 27 | use constant DEFAULTS => | 
| 76 | 1 |  |  | 1 |  | 8 | (notify_delegate => (our $DELEGATE ||= Class::Value::DefaultNotify->new),); | 
|  | 1 |  |  |  |  | 1 |  | 
| 77 | 1 |  |  | 1 |  | 78 | use constant FIRST_CONSTRUCTOR_ARGS => ('notify_delegate'); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 1300 |  | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | sub MUNGE_CONSTRUCTOR_ARGS { | 
| 80 | 3 |  |  | 3 | 1 | 1645 | my $self = shift; | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | # if (@_ == 1 && ref($_[0]) eq 'HASH') { | 
| 83 |  |  |  |  |  |  | #     @_ = %{ $_[0] }; | 
| 84 |  |  |  |  |  |  | # } elsif (@_ % 2) { | 
| 85 | 3 | 50 |  |  |  | 16 | if (@_ % 2) { | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | # odd number of args | 
| 88 | 0 |  |  |  |  | 0 | unshift @_, 'value'; | 
| 89 |  |  |  |  |  |  | } | 
| 90 | 3 |  |  |  |  | 7 | my $ref = ref $self; | 
| 91 | 3 |  |  |  |  | 6 | our %cache_isa; | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | # cache hash value is 0 or 1, so need to use exists() | 
| 94 | 3 | 100 |  |  |  | 13 | unless (exists $cache_isa{$ref}) { | 
| 95 | 1 |  |  |  |  | 8 | $cache_isa{$ref} = UNIVERSAL::isa($self, 'Class::Scaffold::Storable'); | 
| 96 |  |  |  |  |  |  | } | 
| 97 | 3 | 50 |  |  |  | 9 | if ($cache_isa{$ref}) { | 
| 98 | 0 |  |  |  |  | 0 | return $self->Class::Scaffold::Storable::MUNGE_CONSTRUCTOR_ARGS(@_); | 
| 99 |  |  |  |  |  |  | } | 
| 100 | 3 |  |  |  |  | 16 | @_; | 
| 101 |  |  |  |  |  |  | } | 
| 102 | 3 |  |  | 3 | 1 | 42 | sub init { } | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | sub value { | 
| 105 | 118 |  |  | 118 | 1 | 3390 | my $self = shift; | 
| 106 | 118 | 100 |  |  |  | 418 | return $self->get_value unless @_; | 
| 107 | 3 |  |  |  |  | 6 | my $value = shift; | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | # run_checks() returns normalized value; check even undefined values - | 
| 110 |  |  |  |  |  |  | # individual value objects have to decide whether undef is an acceptable | 
| 111 |  |  |  |  |  |  | # value for them. | 
| 112 | 3 | 50 |  |  |  | 9 | if (our $SkipChecks) { | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 3 |  |  |  |  | 5 | our $SkipNormalizations; | 
| 115 | 3 | 50 | 33 |  |  | 21 | if (defined($value) && !$SkipNormalizations) { | 
| 116 | 3 |  |  |  |  | 10 | my $normalized = $self->normalize_value($value); | 
| 117 | 3 | 50 |  |  |  | 10 | $value = $normalized if defined $normalized; | 
| 118 |  |  |  |  |  |  | } | 
| 119 |  |  |  |  |  |  | } else { | 
| 120 | 0 |  |  |  |  | 0 | $value = $self->run_checks($value); | 
| 121 |  |  |  |  |  |  | } | 
| 122 | 3 |  |  |  |  | 11 | $self->set_value($value); | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 3 |  |  |  |  | 9 | $value;    # return for convenience | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | # Subclasses might want to override this if they don't use a custom notify | 
| 128 |  |  |  |  |  |  | # delegate but choose to throw a fixed exception. | 
| 129 |  |  |  |  |  |  | sub send_notify_value_not_wellformed { | 
| 130 | 0 |  |  | 0 | 1 | 0 | my ($self, $value) = @_; | 
| 131 | 0 |  |  |  |  | 0 | $self->notify_delegate->notify_value_not_wellformed(ref($self), $value); | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | sub send_notify_value_invalid { | 
| 135 | 0 |  |  | 0 | 1 | 0 | my ($self, $value) = @_; | 
| 136 | 0 |  |  |  |  | 0 | $self->notify_delegate->notify_value_invalid(ref($self), $value); | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | sub send_notify_value_normalized { | 
| 140 | 0 |  |  | 0 | 1 | 0 | my ($self, $value, $normalized) = @_; | 
| 141 | 0 |  |  |  |  | 0 | $self->notify_delegate->notify_value_normalized(ref($self), $value, | 
| 142 |  |  |  |  |  |  | $normalized); | 
| 143 |  |  |  |  |  |  | } | 
| 144 | 115 |  |  | 115 | 1 | 806 | sub get_value { $_[0]->{_value} } | 
| 145 | 3 |  |  | 3 | 1 | 9 | sub set_value { $_[0]->{_value} = $_[1] } | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | sub is_defined { | 
| 148 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 149 | 0 |  |  |  |  | 0 | defined $self->get_value; | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | sub is_well_formed { | 
| 153 | 2 |  |  | 2 | 1 | 595 | my $self = shift; | 
| 154 | 2 | 100 |  |  |  | 10 | $self->is_well_formed_value(@_ ? shift : $self->value); | 
| 155 |  |  |  |  |  |  | } | 
| 156 | 2 |  |  | 2 | 1 | 16 | sub is_well_formed_value { 1 } | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | sub is_valid { | 
| 159 | 1 |  |  | 1 | 1 | 3 | my $self = shift; | 
| 160 | 1 | 50 |  |  |  | 6 | $self->is_valid_value(@_ ? shift : $self->value); | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | sub is_valid_value { | 
| 164 | 1 |  |  | 1 | 1 | 3 | my ($self, $value) = @_; | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | # value can be undef | 
| 167 | 1 | 50 |  |  |  | 5 | return 1 unless defined $value; | 
| 168 | 1 |  |  |  |  | 4 | my $normalized = $self->normalize_value($value); | 
| 169 | 1 | 50 |  |  |  | 5 | return 0 unless defined $normalized; | 
| 170 | 1 |  |  |  |  | 4 | $self->is_valid_normalized_value($normalized); | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | sub is_valid_normalized_value { | 
| 174 | 1 |  |  | 1 | 1 | 3 | my ($self, $normalized) = @_; | 
| 175 | 1 | 50 |  |  |  | 7 | defined $normalized && $self->is_well_formed($normalized); | 
| 176 |  |  |  |  |  |  | } | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | sub normalize_value { | 
| 179 | 4 |  |  | 4 | 1 | 8 | my ($self, $value) = @_; | 
| 180 | 4 |  |  |  |  | 9 | $value; | 
| 181 |  |  |  |  |  |  | } | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | sub check { | 
| 184 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 185 | 0 | 0 |  |  |  | 0 | my $value = @_ ? shift : $self->value; | 
| 186 | 0 | 0 |  |  |  | 0 | $self->is_well_formed($value) && $self->is_valid($value); | 
| 187 |  |  |  |  |  |  | } | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | sub run_checks { | 
| 190 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 191 | 0 |  |  |  |  | 0 | $self->exception_container->items_clear; | 
| 192 | 0 | 0 |  |  |  | 0 | my $value = @_ ? shift : $self->value; | 
| 193 | 0 | 0 |  |  |  | 0 | $self->is_well_formed($value) | 
| 194 |  |  |  |  |  |  | || $self->send_notify_value_not_wellformed($value); | 
| 195 | 0 | 0 |  |  |  | 0 | $self->is_valid($value) || $self->send_notify_value_invalid($value); | 
| 196 | 0 |  |  |  |  | 0 | my $normalized = $self->normalize($value); | 
| 197 | 0 | 0 | 0 |  |  | 0 | if (defined($value) && defined($normalized) && ($value ne $normalized)) { | 
|  |  |  | 0 |  |  |  |  | 
| 198 | 0 |  |  |  |  | 0 | $self->send_notify_value_normalized($value, $normalized); | 
| 199 |  |  |  |  |  |  | } | 
| 200 | 0 | 0 |  |  |  | 0 | if (my $count = $self->exception_container->items_count) { | 
| 201 | 0 | 0 | 0 |  |  | 0 | if ($count == 1 && our $ThrowSingleException) { | 
| 202 | 0 |  |  |  |  | 0 | $self->exception_container->items->[0]->throw; | 
| 203 |  |  |  |  |  |  | } else { | 
| 204 | 0 |  |  |  |  | 0 | $self->exception_container->throw; | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  | } | 
| 207 | 0 |  |  |  |  | 0 | $normalized; | 
| 208 |  |  |  |  |  |  | } | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | sub run_checks_with_exception_container { | 
| 211 | 0 |  |  | 0 | 1 | 0 | my $self                = shift; | 
| 212 | 0 |  |  |  |  | 0 | my $exception_container = shift; | 
| 213 | 0 | 0 |  |  |  | 0 | my $value               = @_ ? shift : $self->value; | 
| 214 |  |  |  |  |  |  | try { | 
| 215 | 0 |  |  | 0 |  | 0 | $self->run_checks($value); | 
| 216 |  |  |  |  |  |  | } | 
| 217 |  |  |  |  |  |  | catch Error with { | 
| 218 | 0 |  |  | 0 |  | 0 | $exception_container->items_set_push($_[0]); | 
| 219 | 0 |  |  |  |  | 0 | }; | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | # We only needed to fill the value object's exception container during | 
| 222 |  |  |  |  |  |  | # run_checks; now the exceptions have wandered into the exception | 
| 223 |  |  |  |  |  |  | # container that was passed to us, we don't need the value object's | 
| 224 |  |  |  |  |  |  | # exception container anymore. | 
| 225 | 0 |  |  |  |  | 0 | $self->exception_container->items_clear; | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | sub normalize { | 
| 229 | 0 |  |  | 0 | 1 | 0 | my ($self, $value) = @_; | 
| 230 | 0 |  |  |  |  | 0 | my $normalized = $self->normalize_value($value); | 
| 231 | 0 | 0 |  |  |  | 0 | if (defined $value) { | 
| 232 | 0 | 0 |  |  |  | 0 | if (defined $normalized) { | 
| 233 | 0 | 0 |  |  |  | 0 | if ($value ne $normalized) { | 
| 234 | 0 |  |  |  |  | 0 | $self->send_notify_value_normalized($value, $normalized); | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  | } else { | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | # can't normalize; treat as invalid value | 
| 239 | 0 |  |  |  |  | 0 | $self->send_notify_value_invalid($value); | 
| 240 |  |  |  |  |  |  | } | 
| 241 |  |  |  |  |  |  | } | 
| 242 | 0 |  |  |  |  | 0 | $normalized; | 
| 243 |  |  |  |  |  |  | } | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | # have the Class::Value be restrictive with respect to operations on the | 
| 246 |  |  |  |  |  |  | # value; subclasses can then define certain operations. | 
| 247 |  |  |  |  |  |  | for my $op ( | 
| 248 |  |  |  |  |  |  | qw/add subtract multiply divide power modulo num_cmp | 
| 249 |  |  |  |  |  |  | bit_shift_left bit_shift_right bit_and bit_or bit_xor bit_not | 
| 250 |  |  |  |  |  |  | atan2 cos sin exp log sqrt int iterate | 
| 251 |  |  |  |  |  |  | / | 
| 252 |  |  |  |  |  |  | ) { | 
| 253 | 1 |  |  | 1 |  | 6 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 219 |  | 
| 254 |  |  |  |  |  |  | *{$op} = sub { | 
| 255 | 38 |  |  | 38 |  | 37330 | require Class::Value::Exception::UnsupportedOperation; | 
| 256 | 38 |  |  |  |  | 241 | throw Class::Value::Exception::UnsupportedOperation( | 
| 257 |  |  |  |  |  |  | ref    => ref($_[0]), | 
| 258 |  |  |  |  |  |  | value  => $_[0], | 
| 259 |  |  |  |  |  |  | opname => $op, | 
| 260 |  |  |  |  |  |  | ); | 
| 261 |  |  |  |  |  |  | }; | 
| 262 |  |  |  |  |  |  | } | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | sub str_cmp { | 
| 265 | 9 |  | 50 | 9 | 1 | 35 | sprintf("%s", ($_[0] || '')) cmp sprintf("%s", ($_[1] || '')); | 
|  |  |  | 50 |  |  |  |  | 
| 266 |  |  |  |  |  |  | } | 
| 267 | 113 |  |  | 113 | 1 | 110346 | sub stringify { $_[0]->value } | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | sub comparable { | 
| 270 | 0 |  |  | 0 | 1 |  | my $self  = shift; | 
| 271 | 0 |  |  |  |  |  | my $value = $self->value; | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | # Convert the value into a string, because eq_or_diff seems to make a | 
| 274 |  |  |  |  |  |  | # difference between strings and numbers. | 
| 275 | 0 | 0 |  |  |  |  | defined $value ? "$value" : ''; | 
| 276 |  |  |  |  |  |  | } | 
| 277 |  |  |  |  |  |  | 1; | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | __END__ |