summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2009-05-28 19:55:56 -0500
committerJesse Luehrs <doy@tozt.net>2009-05-28 19:58:24 -0500
commitb6754f9c83d813eb0429fafd45bd5dd75f341f61 (patch)
treea763a97f213029247dfbb90fff9601b9ec4028c9
downloadmoosex-abc-b6754f9c83d813eb0429fafd45bd5dd75f341f61.tar.gz
moosex-abc-b6754f9c83d813eb0429fafd45bd5dd75f341f61.zip
initial commit
-rw-r--r--lib/MooseX/ABC.pm54
-rw-r--r--lib/MooseX/ABC/Trait/Class.pm31
2 files changed, 85 insertions, 0 deletions
diff --git a/lib/MooseX/ABC.pm b/lib/MooseX/ABC.pm
new file mode 100644
index 0000000..f0f1412
--- /dev/null
+++ b/lib/MooseX/ABC.pm
@@ -0,0 +1,54 @@
+package MooseX::ABC;
+use Moose ();
+use Moose::Exporter;
+use Moose::Util::MetaRole;
+
+sub requires {
+ my $caller = shift;
+ my $meta = Class::MOP::class_of($caller);
+ $meta->add_required_method($_) for @_;
+}
+
+Moose::Exporter->setup_import_methods(
+ with_caller => [qw(requires)],
+);
+
+sub init_meta {
+ shift;
+ my %options = @_;
+ Moose->init_meta(%options);
+ Moose::Util::MetaRole::apply_metaclass_roles(
+ for_class => $options{for_class},
+ metaclass_roles => ['MooseX::ABC::Trait::Class'],
+ );
+ return Class::MOP::class_of($options{for_class});
+}
+
+=for motivation
+
+22:41 <@rjbs> So, say I wanted to write 'shape'
+22:41 <@rjbs> as a class
+22:41 <@rjbs> but an abstract base class
+22:41 <@rjbs> I could say: requires 'area'
+22:41 <@rjbs> except I can't, because a class can't be abstract in that sense,
+ in Perl
+22:41 <@rjbs> er, in Moose
+22:42 <@rjbs> so to use a delegate is a compromise
+22:42 <@rjbs> It isn't what I want: an ABC
+22:42 <@rjbs> It isn't entirely what I don't wnat: a role
+22:42 <@doy> i wonder if something like a lazy role would work
+22:42 <@rjbs> lazy role?
+22:42 <@doy> instead of dying if all required methods aren't satisfied at
+ compile time, do it at instantiation time
+22:43 <@doy> applying a role and not satisfying all requires turns that class
+ into an abstract class
+22:43 <@rjbs> but now we're back to instantiation time!
+22:43 <@rjbs> I want compile time.
+22:44 <@rjbs> package Shape; use Moose::ABC; # now you get no new method, you
+ can't instantiate directly, and you have "requires" sugar
+22:44 <@rjbs> and if you "extends 'Shape'" without fulfilling rqeuires, you die
+22:44 <@doy> yeah, that would be nicer
+
+=cut
+
+1;
diff --git a/lib/MooseX/ABC/Trait/Class.pm b/lib/MooseX/ABC/Trait/Class.pm
new file mode 100644
index 0000000..c8b7341
--- /dev/null
+++ b/lib/MooseX/ABC/Trait/Class.pm
@@ -0,0 +1,31 @@
+package MooseX::ABC::Trait::Class;
+use Moose::Role;
+use MooseX::AttributeHelpers;
+
+has required_methods => (
+ metaclass => 'Collection::Array',
+ is => 'ro',
+ isa => 'ArrayRef[Str]',
+ default => sub { [] },
+ provides => {
+ push => 'add_required_method',
+ },
+);
+
+after _superclasses_updated => sub {
+ my $self = shift;
+ for my $superclass ($self->linearized_isa) {
+ my $super_meta = Class::MOP::class_of($superclass);
+ next unless $super_meta->does_role('MooseX::ABC::Trait::Class');
+ for my $method ($super_meta->required_methods) {
+ if (!$self->find_method_by_name($method)) {
+ my $classname = $self->name;
+ die "$superclass requires $classname to implement $method";
+ }
+ }
+ }
+};
+
+no Moose::Role;
+
+1;