our
$VERSION
=
"0.47"
;
UR::Object::Type->define(
class_name
=>
'UR::Singleton'
,
is
=> [
'UR::Object'
],
is_abstract
=> 1,
);
sub
id {
my
$self
=
shift
;
return
(
ref
$self
?
$self
->SUPER::id(
@_
) :
$self
);
}
sub
_init_subclass {
my
$class_name
=
shift
;
my
$class_meta_object
=
$class_name
->__meta__;
my
$src
;
if
(
$class_meta_object
->is_abstract) {
$src
=
qq|sub ${class_name}::_singleton_object { Carp::confess("${class_name} is an abstract singleton! Select a concrete sub-class.") }|
.
"\n"
.
qq|sub ${class_name}::_singleton_class_name { Carp::confess("${class_name} is an abstract singleton! Select a concrete sub-class.") }|
.
"\n"
.
qq|sub ${class_name}::_load { shift->_abstract_load(\@_) }|
}
else
{
$src
=
qq|sub ${class_name}::_singleton_object { \$${class_name}::singleton or shift->_concrete_load() }|
.
"\n"
.
qq|sub ${class_name}::_singleton_class_name { '${class_name}' }|
.
"\n"
.
qq|sub ${class_name}::_load { shift->_concrete_load(\@_) }|
.
"\n"
.
qq|sub ${class_name}::get { shift->_concrete_get(\@_) }|
.
"\n"
.
qq|sub ${class_name}::is_loaded { shift->_concrete_is_loaded(\@_) }|
;
}
eval
$src
;
Carp::confess($@)
if
$@;
return
1;
}
sub
_abstract_load {
my
$class
=
shift
;
my
$bx
=
$class
->define_boolexpr(
@_
);
my
$id
=
$bx
->value_for_id;
unless
(
defined
$id
) {
my
$params
= {
$bx
->params_list };
Carp::confess(
"Cannot load a singleton ($class) except by specific identity. "
. Dumper(
$params
));
}
my
$subclass_name
=
$class
->_resolve_subclass_name_for_id(
$id
);
eval
"use $subclass_name"
;
if
($@) {
undef
$@;
return
;
}
return
$subclass_name
->get();
}
sub
_concrete_get {
if
(
@_
== 1 or (
@_
== 2 and
$_
[0] eq
$_
[1])) {
my
$self
=
$_
[0]->_singleton_object;
return
$self
if
$self
;
}
return
shift
->_concrete_load(
@_
);
}
sub
_concrete_is_loaded {
if
(
@_
== 1 or (
@_
== 2 and
$_
[0] eq
$_
[1])) {
my
$self
=
$_
[0]->_singleton_object;
return
$self
if
$self
;
}
return
shift
->SUPER::is_loaded(
@_
);
}
sub
_concrete_load {
my
$class
=
shift
;
$class
=
ref
(
$class
) ||
$class
;
no
strict
'refs'
;
my
$varref
= \${
$class
.
"::singleton"
};
unless
(
$$varref
) {
my
$id
=
$class
->_resolve_id_for_subclass_name(
$class
);
my
$class_object
=
$class
->__meta__;
my
@prop_names
=
$class_object
->all_property_names;
my
%default_values
;
foreach
my
$prop_name
(
@prop_names
) {
my
$prop
=
$class_object
->property_meta_for_name(
$prop_name
);
next
unless
$prop
;
my
$val
=
$prop
->{
'default_value'
};
next
unless
defined
$val
;
$default_values
{
$prop_name
} =
$val
;
}
$$varref
=
$UR::Context::current
->_construct_object(
$class
,
%default_values
,
id
=>
$id
);
$$varref
->{db_committed} = { %
$$varref
};
$$varref
->__signal_change__(
"load"
);
Scalar::Util::weaken(
$$varref
);
}
my
$self
=
$class
->_concrete_is_loaded(
@_
);
return
unless
$self
;
unless
(
$self
->init) {
Carp::confess(
"Failed to initialize singleton $class!"
);
}
return
$self
;
}
sub
init {
return
1;
}
sub
delete
{
my
$self
=
shift
;
my
$class
=
$self
->class;
$self
->SUPER::
delete
();
no
strict
'refs'
;
${
$class
.
"::singleton"
} =
undef
if
${
$class
.
"::singleton"
} eq
$self
;
return
$self
;
}
sub
_resolve_subclass_name_for_id {
my
$class
=
shift
;
my
$id
=
shift
;
return
$id
;
}
sub
_resolve_id_for_subclass_name {
my
$class
=
shift
;
my
$subclass_name
=
shift
;
return
$subclass_name
;
}
sub
create {
my
$class
=
shift
;
my
$bx
=
$class
->define_boolexpr(
@_
);
my
$id
=
$bx
->value_for_id;
unless
(
defined
$id
) {
Carp::confess(
"No singleton ID class specified for constructor?"
);
}
my
$subclass
=
$class
->_resolve_subclass_name_for_id(
$id
);
eval
"use $subclass"
;
unless
(
$subclass
->isa(__PACKAGE__)) {
eval
'@'
.
$subclass
.
"::ISA = ('"
. __PACKAGE__ .
"')"
;
}
return
$subclass
->_concrete_get();
}
1;