Bivio::UI::WidgetValueSource
# Copyright (c) 2001-2007 bivio Software, Inc. All rights reserved.
# $Id$
package Bivio::UI::WidgetValueSource;
use strict;
use base 'Bivio::UNIVERSAL';
use Bivio::IO::Alert;
use Bivio::IO::Trace;
# C<Bivio::UI::WidgetValueSource> defines
# L<get_widget_value|"get_widget_value"> interface, which is
# used by L<Bivio::UI:Widget|Bivio::UI:Widget> to get its dynamic
# values. Many classes implement this interface, but the main
# WidgetValueSources are L<Bivio::Agent::Request|Bivio::Agent::Request>,
# L<Bivio::Biz::Model|Bivio::Biz::Model>,
# and L<Bivio::UI::FacadeComponent|Bivio::UI::FacadeComponent>.
#
#
# A I<widget value> is a variable which a Widget uses as a control value or value
# to render. The widget value language is rich, but is unfortunately complex to
# describe. Read L<get_widget_value|"get_widget_value"> for a formal
# description. The following examples help to clarify how they are
# used in practice.
#
# ['uri']
#
# The I<uri> attribute from the WidgetValueSource is returned to the widget
# requesting it. In this case, the WidgetValueSource is probably a Request and
# the value is the uri on the request.
#
# ['auth_user', 'display_name']
#
# Again, a typical Request attribute is requested. The I<auth_user> is not
# a string, but an instance. We rarely want to display the instance, so
# we get its I<display_name> attribute here.
#
# [['->get_request'], 'auth_user', 'display_name']
#
# A nested widget value is used here to be sure we are retrieving the
# I<auth_user> attribute from the Request and not some other WidgetValueSource.
# WidgetValueSources implement L<get_request|"get_request"> in various
# ways. You should probably use this second form, because the simple
# case (previous example) may not be evaluated in the right context.
#
# ['RealmOwner.name']
#
# A L<ListModel|Bivio::Biz::ListModel>
# or L<FormModel|Bivio::Biz::FormModel> widget value usually
# looks something like this. We know this isn't a PropertyModel, because
# the name has a '.' in it. ListModels and FormModels reference fields
# in PropertyModels by prefixing field name with the PropertyModel name.
#
# During rendering, the L<Table|Bivio::UI::HTML::Widget::Table> widget changes
# its source to the ListModel being iterated. This allows you to define a widget
# value which returns a column's value, i.e. each time the widget value is
# evaluated, it returns the field value for each row of a ListModel.
#
# ['->format_uri', Bivio::Biz::QueryType->THIS_DETAIL,
# Bivio::Agent::TaskId->A_DETAIL_TASK]
#
# A drill down widget value is one which provides an href to
# a L<Link|Bivio::UI::HTML::Widget::Link> widget which is embedded
# in a L<Table|Bivio::UI::HTML::Widget::Table>. Here's we'd like
# to format a URI for the I<A_DETAIL_TASK>. The query string will
# contain a primary key which tells I<A_DETAIL_TASK> what value to
# operate on.
#
# [sub {
# my($source) = @_;
# return sqrt($source->get('SomeModel.some_value'));
# },
#
# You can insert arbitrary logic into widget values by passing
# code references (subroutines). Alternatively, you could specify
# the sub as a formatter, e.g.
#
# ['SomeModel.some_value', sub {sqrt(shift(@_))}]
#
# The difference between the two cases is subtle. In the first case,
# the first argument is a code_ref and it is executed directly.
# The current WidgetValueSource is passed along with any subsequent
# arguments (in this case there are none).
#
# In the second case, the sub is a formatter.
# I<SomeModel.some_value> is first interpreted by the current
# WidgetValueSource and attribute's value is I<formatted> using
# the code_ref. Here's another formatter example.
#
# ['RealmOwner.creation_date_time',
# 'Bivio::UI::HTML::Format::DateTime', 'DATE_TIME'],
#
# The attribute value I<RealmOwner.creation_date_time> is retrieved
# and formatted using the
# L<DateTime|Bivio::UI::HTML::Format::DateTime> formatter.
our($_TRACE);
sub get_request {
# Returns the current request. Should be implemented by
# subclasses. Defaults to
# L<Bivio::Agent::Request::get_current|Bivio::Agent::Request/"get_current">.
return Bivio::Agent::Request->get_current;
}
sub get_widget_value {
my($self) = shift;
# Returns a value to be used by a L<Bivio::UI::Widget|Bivio::UI::Widget>.
#
# I<Evaluation> is calling I<get_widget_value> on I<self> recursively.
# Any array_ref which is passed as a parameter is subject to evaluation.
#
# I<param1> is processed by this method to get a dynamic I<value> from
# I<self>. The way I<param1> is interpreted is value and type dependent
# as follows:
#
#
# Not operation (!)
#
# <param1> is the literal string "!" (exclamation point).
# The subsequent parameterrs are passed unevaluated to
# I<$self-E<gt>get_widget_value>. The result is logically
# negated (not) and a valid Type.Boolean (0 or 1) is returned, i.e.
#
# return $self->get_widget_value(evaluated-params) ? 1 : 0
#
# Named method invocation (-E<gt>method)
#
# I<param1> begins with C<-E<gt>>, the perl method call operator.
# C<$self-E<gt>$param1> will be
# called with the subsequent parameters after evaluation (see above), i.e.
#
# return $self->$param1(evaluated-params)
#
# Named value lookup (unsafe_get_widget_value_by_name)
#
# I<param1> is a string which is passed to
# L<unsafe_get_widget_value_by_name|"unsafe_get_widget_value_by_name">
# which is implemented by subclasses, i.e.
#
# $value = $self->unsafe_get_widget_value_by_name($param1)
#
# Subsequent interpretation of I<$value> is discussed below.
#
# Widget value evaluation (array_ref)
#
# I<param1> is an array_ref it is evaluated (see above), i.e.
#
# $value = $self->get_widget_value(@$param1)
#
# Subsequent interpretation of I<$value> is discussed below.
#
# Code invocation (code_ref)
#
# I<param1> is code reference (sub). I<param1> is passed
# $self followed by subsequent, evaluated parameters, i.e.
#
# return &$param1($self, evaluated-params, $self)
#
# Any other type/value
#
# Dies if I<param1> is another type, e.g. hash_ref or blessed reference.
#
#
# I<$value> and subsequent parameters go through a first phase of
# post processing as follows:
#
#
# No more params (only $param1)
#
# I<$value> is returned verbatim if I<param1> is the only argument.
#
# Blessed reference (get_widget_value)
#
# I<$value> is a blessed reference in which case it's get_widget_value
# will be called. Dies if I<$value> does not implement get_widget_value.
# I.e.
#
# return $value->get_widget_value(evaluated-parameters)
#
# array_ref
#
# I<$value> is an array_ref. The subsequent, evaluated parameter (param2)
# is used to index $value. The indexed value I<replaces> $value. Only
# one level of indexing is allowed and the indexed value must be defined.
# I.e.
#
# $value = $value->[evaluated-param2]
#
# hash_ref
#
# I<$value> is an hash_ref. The subsequent, evaluated parameter (param2)
# is used to index $value. The indexed value I<replaces> $value. Only
# one level of indexing is allowed and the indexed value must be defined.
# I.e.
#
# $value = $value->{evaluated-param2}
#
# scalar
#
# I<$value> is a scalar. Processing continues below.
#
# Any other type/value
#
# Dies if I<$value> is another type, e.g. code_ref.
#
#
# The second phase of post processing of I<$value> and subsequent parameters
# is defined below:
#
#
# No more params (only param1)
#
# If there are no more parameters, I<$value> is returned verbatim.
#
# Blessed reference formatter (ref)
#
# The next parameter is evaluated and must return a reference or
# class which implements I<get_widget_value>. I<$value> is passed
# to the formatter followed by the subsequent, evaluated parameters, i.e.
#
# return $param2->get_widget_value($value, evaluated-params)
#
# Class reference formatter (string)
#
# The same as blessed reference, but the class will be loaded
# by L<Bivio::IO::ClassLoader::map_require|Bivio::IO::ClassLoader/"map_require">
# first.
#
# Subroutine formatter (code_ref)
#
# I<$param2> is a code_ref. I<$value> is passed
# to the formatter followed by the subsequent, evaluated parameters, i.e.
#
# return $param2->get_widget_value($value, evaluated-params)
#
#
# See the L<EXAMPLES|"EXAMPLES"> section.
_trace('params=', \@_) if $_TRACE;
_die($self, 'too few arguments passed to ', $self) unless @_;
my($param1) = shift;
my($value, $exists);
unless (ref($param1)) {
# "Not" operation?
return $self->get_widget_value(@_) ? 0 : 1
if $param1 eq '!';
# If first arg begins with '->', then is a method to call.
# Evaluate the rest of the arguments in this context.
return $self->$param1(_eval_args($self, @_))
if $param1 =~ s/^\-\>//;
# Try to get by name after special names have been exhausted
($value, $exists) = $self->unsafe_get_widget_value_by_name($param1);
}
unless ($exists) {
if (_can_recurse($param1)) {
# Have to have params to call get_widget_value
return $param1->get_widget_value(_eval_args($self, @_)) if @_;
# Otherwise, couldn't find it.
_die($self, $param1, ': not found in WidgetValueSource ', $self);
}
if (ref($param1) eq 'ARRAY') {
$value = $self->get_widget_value(@$param1);
# We fall through
}
elsif (ref($param1) eq 'CODE') {
return &$param1($self, _eval_args($self, @_));
}
else {
_die($self, $param1, ": not found and can't get_widget_value");
}
}
# Anything more to evaluate?
return $value unless @_;
# Have value figure out what to do with it
unless (ref($value)) {
# fall through, not a reference. Next param is formatter (see below)
}
elsif ($value =~ /=/) {
# It's a blessed reference, must support get_widget_value
return $value->get_widget_value(_eval_args($self, @_));
}
else {
# value is not a blessed reference (array_ref, hash_ref, etc.)
my($param2) = shift;
_die($self, $param1, ': is a ref, but not passed second param')
unless defined($param2);
# Evaluate index if an array_ref
$param2 = $self->get_widget_value(@$param2)
if ref($param2) eq 'ARRAY';
if (ref($value) eq 'HASH') {
# key must exist
_die($self, $param1, '->{', $param2, '}: does not exist')
unless exists($value->{$param2});
$value = ($value->{$param2});
}
elsif (ref($value) eq 'ARRAY') {
#TODO: put this warning back, but fix societas notices first
# Bivio::IO::Alert->warn_deprecated(
# $value, ': argument will eventually be resolved fully',
# );
# index must exist (and be a number)
_die($self, $param1, '->[', $param2, ']: does not exist')
unless $param2 <= $#$value;
$value = $value->[$param2];
}
else {
_die($self, $param1, ': unsupported reference type: ',
ref($value));
}
}
# Anything more to evaluate?
return $value unless @_;
# Check for next param which must be able to get_widget_value or
# must be a widget value which returns something that can return
# a widget value.
my($param2) = shift(@_);
return $param2->($value, _eval_args($self, @_))
if ref($param2) eq 'CODE';
$param2 = $self->get_widget_value(@$param2)
if ref($param2) eq 'ARRAY';
unless (_can_recurse($param2)) {
my($tmp) = Bivio::IO::ClassLoader->map_require($param2);
_die($self, $tmp, ": can't get_widget_value (not a formatter)")
unless _can_recurse($tmp);
$param2 = $tmp;
}
return $param2->get_widget_value($value, _eval_args($self, @_))
}
sub unsafe_get_widget_value_by_name {
my($self, $name) = @_;
# Returns (I<value>, I<exists>) for the value from I<self> for I<name>.
# If doesn't exist, I<exists> will be false and I<value> should be C<undef>.
#
# Default implementation is deprecated form.
Bivio::IO::Alert->warn_deprecated('first argument must begin with ->');
return ($self->$name(), 1);
}
sub _can_recurse {
return UNIVERSAL::can(shift(@_), 'get_widget_value');
}
sub _die {
my($self, @msg) = @_;
# Terminates with nice message
my($sub) = (caller(1))[3];
$sub =~ s/.*://;
Bivio::IO::Alert->bootstrap_die($self, '->', $sub, ': ', @msg);
# DOES NOT RETURN
}
sub _eval_args {
my($self) = shift;
# Returns the arguments evaluated as widget values if they are
# array_ref.
return map {
ref($_) eq 'ARRAY' ? map({
Bivio::IO::Alert->warn_deprecated(
$_, ': argument will eventually be resolved fully',
) if ref($_) eq 'ARRAY';
$_;
} $self->get_widget_value(@$_)) : $_;
} @_;
}
1;