aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Reaction/InterfaceModel/ObjectClass.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Reaction/InterfaceModel/ObjectClass.pm')
-rw-r--r--lib/Reaction/InterfaceModel/ObjectClass.pm148
1 files changed, 148 insertions, 0 deletions
diff --git a/lib/Reaction/InterfaceModel/ObjectClass.pm b/lib/Reaction/InterfaceModel/ObjectClass.pm
new file mode 100644
index 0000000..e6c413e
--- /dev/null
+++ b/lib/Reaction/InterfaceModel/ObjectClass.pm
@@ -0,0 +1,148 @@
+package Reaction::InterfaceModel::ObjectClass;
+
+use Reaction::ClassExporter;
+use Reaction::Class;
+use Class::MOP;
+
+#use Reaction::InterfaceModel::Object;
+use Moose::Util::TypeConstraints ();
+use Reaction::InterfaceModel::Object;
+
+class ObjectClass which {
+
+ overrides default_base => sub { ('Reaction::InterfaceModel::Object') };
+
+ overrides exports_for_package => sub {
+ my ($self, $package) = @_;
+ my %exports = $self->SUPER::exports_for_package($package);
+
+ $exports{domain_model} = sub {
+ my($dm_name, %opts)= @_;
+
+ my $isa = $opts{isa};
+ confess 'no isa declared!' unless defined $isa;
+
+ unless( ref $isa || Moose::Util::TypeConstraints::find_type_constraint($isa) ){
+ eval{ Class::MOP::load_class($isa) };
+ warn "'${isa}' is not a valid Moose type constraint. Moose will treat it as ".
+ "a class name and create an anonymous constraint for you. This class is ".
+ "not currently load it and ObjectClass failed to load it. ($@)"
+ if $@;
+ }
+
+ my $attrs = delete $opts{reflect};
+ my $meta = $package->meta;
+
+ #let opts override is and required as needed
+ my $dm_attr = $meta->add_domain_model($dm_name, is => 'ro', required => 1, %opts);
+
+ return unless ref $attrs && @$attrs;
+ my $dm_meta = eval{ $isa->meta };
+ confess "Reflection requires that the argument to isa ('${isa}') be a class ".
+ " supporting introspection e.g a Moose-based class." if $@;
+
+ foreach my $attr_name (@$attrs) {
+ my $from_attr = $dm_meta->find_attribute_by_name($attr_name);
+ my $reader = $from_attr->get_read_method;
+
+ my %attr_opts = ( is => 'ro',
+ lazy_build => 1,
+ isa => $from_attr->_isa_metadata,
+ clearer => "_clear_${attr_name}",
+ domain_model => $dm_name,
+ orig_attr_name => $attr_name,
+ );
+
+ $meta->add_attribute( $attr_name, %attr_opts);
+ $meta->add_method( "build_${attr_name}", sub{ shift->$dm_name->$reader });
+ }
+
+ my $clearer = sub{ $_[0]->$_ for map { "_clear_${_}" } @$attrs };
+
+ $package->can('_clear_reflected') ?
+ $meta->add_before_method_modifier('_clear_reflected', $clearer) :
+ $meta->add_method('_clear_reflected', $clearer);
+
+ #i dont like this, this needs reworking, maybe pass
+ # target_models => [$self->meta->domain_models?]
+ # or maybe this should be done by reflect_actions ?
+ # what about non-reflected actions then though?
+ # maybe a has_action => ('Action_Name' => ActionClass) keyword?
+ #it'd help in registering action_for ....
+ #UPDATE: this is going away very very soon
+ my $dm_reader = $dm_attr->get_read_method;
+ if($package->can('_default_action_args_for')){
+ my $act_args = sub {
+ my $super = shift;
+ my $self = shift;
+ return { %{ $super->($self, @_) }, target_model => $self->$dm_reader };
+ };
+ $meta->add_around_method_modifier('_default_action_args_for', $act_args);
+ } else {
+ $meta->add_method('_default_action_args_for', sub {
+ return {target_model => shift->$dm_reader};
+ }
+ );
+ }
+ };
+
+ return %exports;
+ };
+
+};
+
+1;
+
+__END__;
+
+=head1 NAME
+
+Reaction::Class::InterfaceModel::ObjectClass
+
+=head1 SYNOPSIS
+
+ package MyApp::AdminModel::Foo;
+ use Reaction::Class::InterfaceModel::ObjectClass;
+
+ #will default to be a Reaction::InterfaceModel::Object unless otherwise specified
+ class Foo, which{
+ #create an attribute _user_store with type constraint MyApp::Data::User
+ domain_model '_user_store' =>
+ (isa => 'MyApp::Data::User',
+ #mirror the following attributes from MyApp::Data::User
+ reflect => [qw/id username password created_d/],
+ ...
+ };
+
+=head1 DESCRIPTION
+
+Extends C<Reaction::Class> to provide new sugar for InterfaceModel Objects.
+
+=head1 Extended methods / new functionality
+
+=head2 exports_for_package
+
+Overridden to add exported methods C<proxies> and C<_clear_proxied>
+
+=head2 domain_model $name => ( isa => 'Classname' reflect => [qw/attr names/] )
+
+Will create a read-only required attribute $name of type C<isa> which will
+reflect the attributes named in C<reflect>, to the local class as
+read-only attributes that will build lazily.
+
+It will also override C<_default_action_args_for> to pass the domain model
+as C<target_model>
+
+=head2 _clear_reflected
+
+Will clear all reflected attributes.
+
+=head2 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut